diff --git a/docs/api.md b/docs/api.md index 8b08d79495..ce7e140ee0 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3642,19 +3642,229 @@ This module provides queries related to events. ## DB API: feature related queries This module provides queries for features. + +* [DB API: feature related queries](#module_DB API_ feature related queries) + * [~getFeaturesByDeviceTypeRefs(db, deviceTypeRefs, endpointTypeRef)](#module_DB API_ feature related queries..getFeaturesByDeviceTypeRefs) ⇒ + * [~evaluateConformanceExpression(expression, elementMap)](#module_DB API_ feature related queries..evaluateConformanceExpression) ⇒ + * [~evaluateBooleanExpression(expr)](#module_DB API_ feature related queries..evaluateConformanceExpression..evaluateBooleanExpression) + * [~evaluateWithParentheses(expr)](#module_DB API_ feature related queries..evaluateConformanceExpression..evaluateWithParentheses) + * [~checkMissingTerms(expression, elementMap)](#module_DB API_ feature related queries..checkMissingTerms) ⇒ + * [~filterElementsContainingDesc(elements)](#module_DB API_ feature related queries..filterElementsContainingDesc) ⇒ + * [~filterRelatedDescElements(elements, featureCode)](#module_DB API_ feature related queries..filterRelatedDescElements) ⇒ + * [~generateWarningMessage(featureData, endpointId, missingTerms, featureMap, descElements)](#module_DB API_ feature related queries..generateWarningMessage) ⇒ + * [~checkElementConformance(elements, featureMap, featureData, endpointId)](#module_DB API_ feature related queries..checkElementConformance) ⇒ + * [~filterElementsToUpdate(elements, elementMap, featureCode)](#module_DB API_ feature related queries..filterElementsToUpdate) ⇒ + * [~getOutdatedElementWarning(featureData, elements, elementMap)](#module_DB API_ feature related queries..getOutdatedElementWarning) ⇒ + * [~processElements(elementType)](#module_DB API_ feature related queries..getOutdatedElementWarning..processElements) + * [~filterRequiredElements(elements, elementMap, featureMap)](#module_DB API_ feature related queries..filterRequiredElements) ⇒ + * [~checkIfConformanceDataExist(db)](#module_DB API_ feature related queries..checkIfConformanceDataExist) ⇒ + -### DB API: feature related queries~getFeaturesByDeviceTypeRefs(db, deviceTypeRefs) ⇒ -Get all device type features associated with a list of device type refs +### DB API: feature related queries~getFeaturesByDeviceTypeRefs(db, deviceTypeRefs, endpointTypeRef) ⇒ +Get all device type features associated with a list of device type refs and an endpoint. +Join ENDPOINT_TYPE_ATTRIBUTE and ATTRIBUTE table to get featureMap attribute associated with the feature, +so the frontend could get and set featureMap bit easier. +Only return features with cluster on the side specified in the device type. **Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) **Returns**: All feature information and device type conformance -with associated device type and cluster details +with associated device type, cluster, and featureMap attribute details | Param | Type | | --- | --- | | db | \* | | deviceTypeRefs | \* | +| endpointTypeRef | \* | + + + +### DB API: feature related queries~evaluateConformanceExpression(expression, elementMap) ⇒ +Evaluate the value of a boolean conformance expression that includes terms and operators. +A term can be an attribute, command, event, feature, or conformance abbreviation. +Operators include AND (&), OR (|), and NOT (!). +The '[]' indicates optional conformance if the expression inside true. +Expression containing comma means otherwise conformance. See spec for details. +Examples of conformance expression: 'A & (!B | C)', 'A & B, [!C]' + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: 'mandatory', 'optional', 'provisional', or 'notSupported' + +| Param | Type | +| --- | --- | +| expression | \* | +| elementMap | \* | + + +* [~evaluateConformanceExpression(expression, elementMap)](#module_DB API_ feature related queries..evaluateConformanceExpression) ⇒ + * [~evaluateBooleanExpression(expr)](#module_DB API_ feature related queries..evaluateConformanceExpression..evaluateBooleanExpression) + * [~evaluateWithParentheses(expr)](#module_DB API_ feature related queries..evaluateConformanceExpression..evaluateWithParentheses) + + + +#### evaluateConformanceExpression~evaluateBooleanExpression(expr) +helper function to evaluate a single boolean expression + +**Kind**: inner method of [evaluateConformanceExpression](#module_DB API_ feature related queries..evaluateConformanceExpression) + +| Param | Type | +| --- | --- | +| expr | \* | + + + +#### evaluateConformanceExpression~evaluateWithParentheses(expr) +helper function to process parentheses and evaluate inner expressions first + +**Kind**: inner method of [evaluateConformanceExpression](#module_DB API_ feature related queries..evaluateConformanceExpression) + +| Param | Type | +| --- | --- | +| expr | \* | + + + +### DB API: feature related queries~checkMissingTerms(expression, elementMap) ⇒ +Check if any terms in the expression are neither a key in the elementMap nor an abbreviation. +If so, it means the conformance depends on terms with unknown values and changes are not allowed. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: all missing terms in an array + +| Param | Type | +| --- | --- | +| expression | \* | +| elementMap | \* | + + + +### DB API: feature related queries~filterElementsContainingDesc(elements) ⇒ +Filter an array of elements by if any element has conformance containing the term 'desc'. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: elements with conformance containing 'desc' + +| Param | Type | +| --- | --- | +| elements | \* | + + + +### DB API: feature related queries~filterRelatedDescElements(elements, featureCode) ⇒ +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: elements with conformance containing 'desc' and the feature code + +| Param | Type | +| --- | --- | +| elements | \* | +| featureCode | \* | + + + +### DB API: feature related queries~generateWarningMessage(featureData, endpointId, missingTerms, featureMap, descElements) ⇒ +Generate a warning message after processing conformance of the updated device type feature. +Set flags to decide whether to show a popup warning or disable changes in the frontend. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: warning message array, disableChange flag, and displayWarning flag + +| Param | Type | +| --- | --- | +| featureData | \* | +| endpointId | \* | +| missingTerms | \* | +| featureMap | \* | +| descElements | \* | + + + +### DB API: feature related queries~checkElementConformance(elements, featureMap, featureData, endpointId) ⇒ +Check if elements need to be updated for correct conformance if featureData provided. +Otherwise, check if elements are required or unsupported by their conformance. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: attributes, commands, and events to update, with warnings if featureData provided; +required and unsupported attributes, commands, and events, with warnings if not. + +| Param | Type | Default | +| --- | --- | --- | +| elements | \* | | +| featureMap | \* | | +| featureData | \* | | +| endpointId | \* | | + + + +### DB API: feature related queries~filterElementsToUpdate(elements, elementMap, featureCode) ⇒ +Return attributes, commands, or events to be updated satisfying: +(1) its conformance includes feature code of the updated feature +(2) it has mandatory conformance but it is not enabled, OR, + it is has notSupported conformance but it is enabled + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: elements that should be updated + +| Param | Type | +| --- | --- | +| elements | \* | +| elementMap | \* | +| featureCode | \* | + + + +### DB API: feature related queries~getOutdatedElementWarning(featureData, elements, elementMap) ⇒ +Get warnings for element requirements that are outdated after a feature update. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: array of outdated element warnings + +| Param | Type | +| --- | --- | +| featureData | \* | +| elements | \* | +| elementMap | \* | + + + +#### getOutdatedElementWarning~processElements(elementType) +Build substrings of outdated warnings and add to returned array if: +(1) the element conformance includes the feature code +(2) the element conformance has changed after the feature update + +**Kind**: inner method of [getOutdatedElementWarning](#module_DB API_ feature related queries..getOutdatedElementWarning) + +| Param | Type | +| --- | --- | +| elementType | \* | + + + +### DB API: feature related queries~filterRequiredElements(elements, elementMap, featureMap) ⇒ +Filter required and unsupported elements based on their conformance and generate warnings. +An element is required if it conforms to element(s) in elementMap and has 'mandatory' conform. +An element is unsupported if it conforms to element(s) in elementMap and has 'notSupported' conform. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: required and not supported elements with warnings + +| Param | Type | +| --- | --- | +| elements | \* | +| elementMap | \* | +| featureMap | \* | + + + +### DB API: feature related queries~checkIfConformanceDataExist(db) ⇒ +Check if any non-empty conformance data exist in ATTRIBUTE, COMMAND, +and DEVICE_TYPE_FEATURE table. + +**Kind**: inner method of [DB API: feature related queries](#module_DB API_ feature related queries) +**Returns**: boolean value indicating if conformance data exists + +| Param | Type | +| --- | --- | +| db | \* | @@ -14372,6 +14582,9 @@ This module provides the API to access zcl specific information. * [~httpGetSessionKeyValues(db)](#module_REST API_ user data..httpGetSessionKeyValues) ⇒ * [~httpGetEndpointIds(db)](#module_REST API_ user data..httpGetEndpointIds) ⇒ * [~httpGetDeviceTypeFeatures(db)](#module_REST API_ user data..httpGetDeviceTypeFeatures) ⇒ + * [~getEndpointTypeElements(db, endpointTypeClusterId, deviceTypeClusterId)](#module_REST API_ user data..getEndpointTypeElements) ⇒ + * [~httpPostCheckConformOnFeatureUpdate(db)](#module_REST API_ user data..httpPostCheckConformOnFeatureUpdate) ⇒ + * [~httpGetRequiredElements(db)](#module_REST API_ user data..httpGetRequiredElements) ⇒ * [~httpGetSessionNotifications(db)](#module_REST API_ user data..httpGetSessionNotifications) ⇒ * [~httpDeleteSessionNotification(db)](#module_REST API_ user data..httpDeleteSessionNotification) ⇒ * [~httpGetPackageNotifications(db)](#module_REST API_ user data..httpGetPackageNotifications) ⇒ @@ -14401,6 +14614,9 @@ This module provides the API to access zcl specific information. * [~httpDeleteSessionPackage(db)](#module_REST API_ user data..httpDeleteSessionPackage) ⇒ * [~httpPostDuplicateEndpoint(db)](#module_REST API_ user data..httpPostDuplicateEndpoint) ⇒ * [~httpPostDuplicateEndpointType(db)](#module_REST API_ user data..httpPostDuplicateEndpointType) ⇒ + * [~httpPatchUpdateBitOfFeatureMapAttribute(db)](#module_REST API_ user data..httpPatchUpdateBitOfFeatureMapAttribute) ⇒ + * [~httpGetConformDataExists(db)](#module_REST API_ user data..httpGetConformDataExists) ⇒ + * [~httpPostRequiredElementWarning(db)](#module_REST API_ user data..httpPostRequiredElementWarning) ⇒ * [~duplicateEndpointTypeClusters(db, oldEndpointTypeId, newEndpointTypeId)](#module_REST API_ user data..duplicateEndpointTypeClusters) @@ -14454,6 +14670,45 @@ HTTP GET: device type features | --- | --- | | db | \* | + + +### REST API: user data~getEndpointTypeElements(db, endpointTypeClusterId, deviceTypeClusterId) ⇒ +Get all attributes, commands and events in an endpoint type cluster. + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: elements object containing all attributes, commands and events +in an endpoint type cluster + +| Param | Type | +| --- | --- | +| db | \* | +| endpointTypeClusterId | \* | +| deviceTypeClusterId | \* | + + + +### REST API: user data~httpPostCheckConformOnFeatureUpdate(db) ⇒ +HTTP POST: elements to be updated after toggle a device type feature + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: callback for the express uri registration + +| Param | Type | +| --- | --- | +| db | \* | + + + +### REST API: user data~httpGetRequiredElements(db) ⇒ +HTTP GET: required and unsupported cluster elements based on conformance + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: callback for the express uri registration + +| Param | Type | +| --- | --- | +| db | \* | + ### REST API: user data~httpGetSessionNotifications(db) ⇒ @@ -14800,6 +15055,42 @@ Creating a duplicate for endpoint-type and endpoint-type-attributes | --- | --- | | db | \* | + + +### REST API: user data~httpPatchUpdateBitOfFeatureMapAttribute(db) ⇒ +Update feature map attribute with given new value + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: status of the update + +| Param | Type | +| --- | --- | +| db | \* | + + + +### REST API: user data~httpGetConformDataExists(db) ⇒ +Check if conformance data exists in the database + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: boolean value of data exist or not + +| Param | Type | +| --- | --- | +| db | \* | + + + +### REST API: user data~httpPostRequiredElementWarning(db) ⇒ +Set warning for the required element, and delete its existing warning if any. + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: response of setting the warning notification + +| Param | Type | +| --- | --- | +| db | \* | + ### REST API: user data~duplicateEndpointTypeClusters(db, oldEndpointTypeId, newEndpointTypeId) @@ -15630,6 +15921,9 @@ This module provides the REST API to the user specific data. * [~httpGetSessionKeyValues(db)](#module_REST API_ user data..httpGetSessionKeyValues) ⇒ * [~httpGetEndpointIds(db)](#module_REST API_ user data..httpGetEndpointIds) ⇒ * [~httpGetDeviceTypeFeatures(db)](#module_REST API_ user data..httpGetDeviceTypeFeatures) ⇒ + * [~getEndpointTypeElements(db, endpointTypeClusterId, deviceTypeClusterId)](#module_REST API_ user data..getEndpointTypeElements) ⇒ + * [~httpPostCheckConformOnFeatureUpdate(db)](#module_REST API_ user data..httpPostCheckConformOnFeatureUpdate) ⇒ + * [~httpGetRequiredElements(db)](#module_REST API_ user data..httpGetRequiredElements) ⇒ * [~httpGetSessionNotifications(db)](#module_REST API_ user data..httpGetSessionNotifications) ⇒ * [~httpDeleteSessionNotification(db)](#module_REST API_ user data..httpDeleteSessionNotification) ⇒ * [~httpGetPackageNotifications(db)](#module_REST API_ user data..httpGetPackageNotifications) ⇒ @@ -15659,6 +15953,9 @@ This module provides the REST API to the user specific data. * [~httpDeleteSessionPackage(db)](#module_REST API_ user data..httpDeleteSessionPackage) ⇒ * [~httpPostDuplicateEndpoint(db)](#module_REST API_ user data..httpPostDuplicateEndpoint) ⇒ * [~httpPostDuplicateEndpointType(db)](#module_REST API_ user data..httpPostDuplicateEndpointType) ⇒ + * [~httpPatchUpdateBitOfFeatureMapAttribute(db)](#module_REST API_ user data..httpPatchUpdateBitOfFeatureMapAttribute) ⇒ + * [~httpGetConformDataExists(db)](#module_REST API_ user data..httpGetConformDataExists) ⇒ + * [~httpPostRequiredElementWarning(db)](#module_REST API_ user data..httpPostRequiredElementWarning) ⇒ * [~duplicateEndpointTypeClusters(db, oldEndpointTypeId, newEndpointTypeId)](#module_REST API_ user data..duplicateEndpointTypeClusters) @@ -15712,6 +16009,45 @@ HTTP GET: device type features | --- | --- | | db | \* | + + +### REST API: user data~getEndpointTypeElements(db, endpointTypeClusterId, deviceTypeClusterId) ⇒ +Get all attributes, commands and events in an endpoint type cluster. + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: elements object containing all attributes, commands and events +in an endpoint type cluster + +| Param | Type | +| --- | --- | +| db | \* | +| endpointTypeClusterId | \* | +| deviceTypeClusterId | \* | + + + +### REST API: user data~httpPostCheckConformOnFeatureUpdate(db) ⇒ +HTTP POST: elements to be updated after toggle a device type feature + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: callback for the express uri registration + +| Param | Type | +| --- | --- | +| db | \* | + + + +### REST API: user data~httpGetRequiredElements(db) ⇒ +HTTP GET: required and unsupported cluster elements based on conformance + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: callback for the express uri registration + +| Param | Type | +| --- | --- | +| db | \* | + ### REST API: user data~httpGetSessionNotifications(db) ⇒ @@ -16058,6 +16394,42 @@ Creating a duplicate for endpoint-type and endpoint-type-attributes | --- | --- | | db | \* | + + +### REST API: user data~httpPatchUpdateBitOfFeatureMapAttribute(db) ⇒ +Update feature map attribute with given new value + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: status of the update + +| Param | Type | +| --- | --- | +| db | \* | + + + +### REST API: user data~httpGetConformDataExists(db) ⇒ +Check if conformance data exists in the database + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: boolean value of data exist or not + +| Param | Type | +| --- | --- | +| db | \* | + + + +### REST API: user data~httpPostRequiredElementWarning(db) ⇒ +Set warning for the required element, and delete its existing warning if any. + +**Kind**: inner method of [REST API: user data](#module_REST API_ user data) +**Returns**: response of setting the warning notification + +| Param | Type | +| --- | --- | +| db | \* | + ### REST API: user data~duplicateEndpointTypeClusters(db, oldEndpointTypeId, newEndpointTypeId) @@ -19763,8 +20135,8 @@ This module provides the APIs for dotdot Loading * [~parseManufacturerData(db, ctx)](#module_Loader API_ Loader APIs..parseManufacturerData) ⇒ * [~parseProfilesData(db, ctx)](#module_Loader API_ Loader APIs..parseProfilesData) ⇒ * [~parseFeatureFlags(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseFeatureFlags) ⇒ - * [~parseFeatureConformance(operand)](#module_Loader API_ Loader APIs..parseFeatureConformance) ⇒ - * [~parseAndOrConformanceTerms(operand, joinChar)](#module_Loader API_ Loader APIs..parseAndOrConformanceTerms) ⇒ + * [~parseConformanceFromXML(operand)](#module_Loader API_ Loader APIs..parseConformanceFromXML) ⇒ + * [~parseConformanceRecursively(operand, depth, parentJoinChar)](#module_Loader API_ Loader APIs..parseConformanceRecursively) ⇒ * [~parseUiOptions(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseUiOptions) ⇒ * [~parseOptions(db)](#module_Loader API_ Loader APIs..parseOptions) ⇒ * [~parseTextOptions(db, pkgRef, textOptions)](#module_Loader API_ Loader APIs..parseTextOptions) ⇒ @@ -21115,10 +21487,14 @@ Key/velues of the object itself, end up in CODE/LABEL combinations. | packageId | \* | | featureFlags | \* | - + + +### Loader API: Loader APIs~parseConformanceFromXML(operand) ⇒ +Parses conformance from XML data. +The conformance could come from features, attributes, commands, or events -### Loader API: Loader APIs~parseFeatureConformance(operand) ⇒ -Parses feature conformance or an operand in feature conformance recursively from xml data. +Call recursive helper function to parse conformance only if the conformance exists. +Otherwise, return empty string directly An example of parsing the conformance of 'User' device type feature: @@ -21133,7 +21509,7 @@ Input operand from xml data: { "feature": [ { "$": {"name": "PIN"}}, { "$": {"name": "RID"}}, - { "$": {"name": "FPG"}}, + { "$": {"name": "FGP"}}, { "$": {"name": "FACE"}} ] } @@ -21144,28 +21520,32 @@ Input operand from xml data: ] } -Output device type feature conformance string: - "Matter & (PIN | RID | FPG | FACE)" +Output conformance string: + "Matter & (PIN | RID | FGP | FACE)" **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: The feature conformance string. +**Returns**: The conformance string -| Param | Type | Description | -| --- | --- | --- | -| operand | \* | The operand to be parsed. | +| Param | Type | +| --- | --- | +| operand | \* | + + - +### Loader API: Loader APIs~parseConformanceRecursively(operand, depth, parentJoinChar) ⇒ +helper function to parse conformance or an operand in conformance recursively -### Loader API: Loader APIs~parseAndOrConformanceTerms(operand, joinChar) ⇒ -Helper function to parse andTerm or orTerm from xml data +The baseLevelTerms variable include terms that can not have nested terms. +When they appear, stop recursing and return the name inside directly **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: feature conformance string +**Returns**: The conformance string. -| Param | Type | -| --- | --- | -| operand | \* | -| joinChar | \* | +| Param | Type | Default | +| --- | --- | --- | +| operand | \* | | +| depth | \* | 0 | +| parentJoinChar | \* | | @@ -21589,8 +21969,8 @@ This module provides the APIs for new data model loading * [~parseManufacturerData(db, ctx)](#module_Loader API_ Loader APIs..parseManufacturerData) ⇒ * [~parseProfilesData(db, ctx)](#module_Loader API_ Loader APIs..parseProfilesData) ⇒ * [~parseFeatureFlags(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseFeatureFlags) ⇒ - * [~parseFeatureConformance(operand)](#module_Loader API_ Loader APIs..parseFeatureConformance) ⇒ - * [~parseAndOrConformanceTerms(operand, joinChar)](#module_Loader API_ Loader APIs..parseAndOrConformanceTerms) ⇒ + * [~parseConformanceFromXML(operand)](#module_Loader API_ Loader APIs..parseConformanceFromXML) ⇒ + * [~parseConformanceRecursively(operand, depth, parentJoinChar)](#module_Loader API_ Loader APIs..parseConformanceRecursively) ⇒ * [~parseUiOptions(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseUiOptions) ⇒ * [~parseOptions(db)](#module_Loader API_ Loader APIs..parseOptions) ⇒ * [~parseTextOptions(db, pkgRef, textOptions)](#module_Loader API_ Loader APIs..parseTextOptions) ⇒ @@ -22941,10 +23321,14 @@ Key/velues of the object itself, end up in CODE/LABEL combinations. | packageId | \* | | featureFlags | \* | - + + +### Loader API: Loader APIs~parseConformanceFromXML(operand) ⇒ +Parses conformance from XML data. +The conformance could come from features, attributes, commands, or events -### Loader API: Loader APIs~parseFeatureConformance(operand) ⇒ -Parses feature conformance or an operand in feature conformance recursively from xml data. +Call recursive helper function to parse conformance only if the conformance exists. +Otherwise, return empty string directly An example of parsing the conformance of 'User' device type feature: @@ -22959,7 +23343,7 @@ Input operand from xml data: { "feature": [ { "$": {"name": "PIN"}}, { "$": {"name": "RID"}}, - { "$": {"name": "FPG"}}, + { "$": {"name": "FGP"}}, { "$": {"name": "FACE"}} ] } @@ -22970,28 +23354,32 @@ Input operand from xml data: ] } -Output device type feature conformance string: - "Matter & (PIN | RID | FPG | FACE)" +Output conformance string: + "Matter & (PIN | RID | FGP | FACE)" **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: The feature conformance string. +**Returns**: The conformance string -| Param | Type | Description | -| --- | --- | --- | -| operand | \* | The operand to be parsed. | +| Param | Type | +| --- | --- | +| operand | \* | - + -### Loader API: Loader APIs~parseAndOrConformanceTerms(operand, joinChar) ⇒ -Helper function to parse andTerm or orTerm from xml data +### Loader API: Loader APIs~parseConformanceRecursively(operand, depth, parentJoinChar) ⇒ +helper function to parse conformance or an operand in conformance recursively + +The baseLevelTerms variable include terms that can not have nested terms. +When they appear, stop recursing and return the name inside directly **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: feature conformance string +**Returns**: The conformance string. -| Param | Type | -| --- | --- | -| operand | \* | -| joinChar | \* | +| Param | Type | Default | +| --- | --- | --- | +| operand | \* | | +| depth | \* | 0 | +| parentJoinChar | \* | | @@ -23415,8 +23803,8 @@ This module provides the APIs for ZCL/Data-Model loading. * [~parseManufacturerData(db, ctx)](#module_Loader API_ Loader APIs..parseManufacturerData) ⇒ * [~parseProfilesData(db, ctx)](#module_Loader API_ Loader APIs..parseProfilesData) ⇒ * [~parseFeatureFlags(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseFeatureFlags) ⇒ - * [~parseFeatureConformance(operand)](#module_Loader API_ Loader APIs..parseFeatureConformance) ⇒ - * [~parseAndOrConformanceTerms(operand, joinChar)](#module_Loader API_ Loader APIs..parseAndOrConformanceTerms) ⇒ + * [~parseConformanceFromXML(operand)](#module_Loader API_ Loader APIs..parseConformanceFromXML) ⇒ + * [~parseConformanceRecursively(operand, depth, parentJoinChar)](#module_Loader API_ Loader APIs..parseConformanceRecursively) ⇒ * [~parseUiOptions(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseUiOptions) ⇒ * [~parseOptions(db)](#module_Loader API_ Loader APIs..parseOptions) ⇒ * [~parseTextOptions(db, pkgRef, textOptions)](#module_Loader API_ Loader APIs..parseTextOptions) ⇒ @@ -24767,10 +25155,14 @@ Key/velues of the object itself, end up in CODE/LABEL combinations. | packageId | \* | | featureFlags | \* | - + -### Loader API: Loader APIs~parseFeatureConformance(operand) ⇒ -Parses feature conformance or an operand in feature conformance recursively from xml data. +### Loader API: Loader APIs~parseConformanceFromXML(operand) ⇒ +Parses conformance from XML data. +The conformance could come from features, attributes, commands, or events + +Call recursive helper function to parse conformance only if the conformance exists. +Otherwise, return empty string directly An example of parsing the conformance of 'User' device type feature: @@ -24785,7 +25177,7 @@ Input operand from xml data: { "feature": [ { "$": {"name": "PIN"}}, { "$": {"name": "RID"}}, - { "$": {"name": "FPG"}}, + { "$": {"name": "FGP"}}, { "$": {"name": "FACE"}} ] } @@ -24796,28 +25188,32 @@ Input operand from xml data: ] } -Output device type feature conformance string: - "Matter & (PIN | RID | FPG | FACE)" +Output conformance string: + "Matter & (PIN | RID | FGP | FACE)" **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: The feature conformance string. +**Returns**: The conformance string -| Param | Type | Description | -| --- | --- | --- | -| operand | \* | The operand to be parsed. | +| Param | Type | +| --- | --- | +| operand | \* | - + -### Loader API: Loader APIs~parseAndOrConformanceTerms(operand, joinChar) ⇒ -Helper function to parse andTerm or orTerm from xml data +### Loader API: Loader APIs~parseConformanceRecursively(operand, depth, parentJoinChar) ⇒ +helper function to parse conformance or an operand in conformance recursively + +The baseLevelTerms variable include terms that can not have nested terms. +When they appear, stop recursing and return the name inside directly **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: feature conformance string +**Returns**: The conformance string. -| Param | Type | -| --- | --- | -| operand | \* | -| joinChar | \* | +| Param | Type | Default | +| --- | --- | --- | +| operand | \* | | +| depth | \* | 0 | +| parentJoinChar | \* | | @@ -25241,8 +25637,8 @@ This module provides the APIs for for common functionality related to loading. * [~parseManufacturerData(db, ctx)](#module_Loader API_ Loader APIs..parseManufacturerData) ⇒ * [~parseProfilesData(db, ctx)](#module_Loader API_ Loader APIs..parseProfilesData) ⇒ * [~parseFeatureFlags(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseFeatureFlags) ⇒ - * [~parseFeatureConformance(operand)](#module_Loader API_ Loader APIs..parseFeatureConformance) ⇒ - * [~parseAndOrConformanceTerms(operand, joinChar)](#module_Loader API_ Loader APIs..parseAndOrConformanceTerms) ⇒ + * [~parseConformanceFromXML(operand)](#module_Loader API_ Loader APIs..parseConformanceFromXML) ⇒ + * [~parseConformanceRecursively(operand, depth, parentJoinChar)](#module_Loader API_ Loader APIs..parseConformanceRecursively) ⇒ * [~parseUiOptions(db, packageId, featureFlags)](#module_Loader API_ Loader APIs..parseUiOptions) ⇒ * [~parseOptions(db)](#module_Loader API_ Loader APIs..parseOptions) ⇒ * [~parseTextOptions(db, pkgRef, textOptions)](#module_Loader API_ Loader APIs..parseTextOptions) ⇒ @@ -26593,10 +26989,14 @@ Key/velues of the object itself, end up in CODE/LABEL combinations. | packageId | \* | | featureFlags | \* | - + + +### Loader API: Loader APIs~parseConformanceFromXML(operand) ⇒ +Parses conformance from XML data. +The conformance could come from features, attributes, commands, or events -### Loader API: Loader APIs~parseFeatureConformance(operand) ⇒ -Parses feature conformance or an operand in feature conformance recursively from xml data. +Call recursive helper function to parse conformance only if the conformance exists. +Otherwise, return empty string directly An example of parsing the conformance of 'User' device type feature: @@ -26611,7 +27011,7 @@ Input operand from xml data: { "feature": [ { "$": {"name": "PIN"}}, { "$": {"name": "RID"}}, - { "$": {"name": "FPG"}}, + { "$": {"name": "FGP"}}, { "$": {"name": "FACE"}} ] } @@ -26622,28 +27022,32 @@ Input operand from xml data: ] } -Output device type feature conformance string: - "Matter & (PIN | RID | FPG | FACE)" +Output conformance string: + "Matter & (PIN | RID | FGP | FACE)" **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: The feature conformance string. +**Returns**: The conformance string -| Param | Type | Description | -| --- | --- | --- | -| operand | \* | The operand to be parsed. | +| Param | Type | +| --- | --- | +| operand | \* | + + - +### Loader API: Loader APIs~parseConformanceRecursively(operand, depth, parentJoinChar) ⇒ +helper function to parse conformance or an operand in conformance recursively -### Loader API: Loader APIs~parseAndOrConformanceTerms(operand, joinChar) ⇒ -Helper function to parse andTerm or orTerm from xml data +The baseLevelTerms variable include terms that can not have nested terms. +When they appear, stop recursing and return the name inside directly **Kind**: inner method of [Loader API: Loader APIs](#module_Loader API_ Loader APIs) -**Returns**: feature conformance string +**Returns**: The conformance string. -| Param | Type | -| --- | --- | -| operand | \* | -| joinChar | \* | +| Param | Type | Default | +| --- | --- | --- | +| operand | \* | | +| depth | \* | 0 | +| parentJoinChar | \* | | diff --git a/docs/zap-schema.svg b/docs/zap-schema.svg index 390b1a43ad..6f796b61f6 100644 --- a/docs/zap-schema.svg +++ b/docs/zap-schema.svg @@ -1,3113 +1,3088 @@ - - - + + SchemaCrawler_Diagram - -generated by -SchemaCrawler 16.22.2 -generated on -2024-10-28 19:22:42 - + +generated by +SchemaCrawler 16.22.2 +ZAP schema, Copyright (c) 2020 Silicon Labs, released under Apache 2.0 license. + access_72bb1dc3 - -ACCESS - -[table] -ACCESS_ID - -INTEGER - -auto-incremented -OPERATION_REF - -INTEGER -ROLE_REF - -INTEGER -ACCESS_MODIFIER_REF - -INTEGER - + + +ACCESS + +[table] +ACCESS_ID + +INTEGER + +auto-incremented +OPERATION_REF + +INTEGER +ROLE_REF + +INTEGER +ACCESS_MODIFIER_REF + +INTEGER + access_modifier_f63f3fb1 - -ACCESS_MODIFIER - -[table] -ACCESS_MODIFIER_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT - + + +ACCESS_MODIFIER + +[table] +ACCESS_MODIFIER_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT + access_72bb1dc3:w->access_modifier_f63f3fb1:e - - - - - - - -SCHCRWLR_F63ECB52_72BAA964 + + + + + + + operation_93359a6 - -OPERATION - -[table] -OPERATION_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT - + + +OPERATION + +[table] +OPERATION_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT + access_72bb1dc3:w->operation_93359a6:e - - - - - - - -SCHCRWLR_0932E547_72BAA964 + + + + + + + role_26ecd5 - -ROLE - -[table] -ROLE_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -LEVEL - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT - + + +ROLE + +[table] +ROLE_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +LEVEL + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT + access_72bb1dc3:w->role_26ecd5:e - - - - - - - -SCHCRWLR_00267876_72BAA964 + + + + + + + attribute_access_b017dce6 - -ATTRIBUTE_ACCESS - -[table] -ATTRIBUTE_REF - -INTEGER -ACCESS_REF - -INTEGER - + + +ATTRIBUTE_ACCESS + +[table] +ATTRIBUTE_REF + +INTEGER +ACCESS_REF + +INTEGER + attribute_access_b017dce6:w->access_72bb1dc3:e - - - - - - - -SCHCRWLR_72BAA964_B0176887 + + + + + + + attribute_a6e02edb - -ATTRIBUTE - -[table] -ATTRIBUTE_ID - -INTEGER - -auto-incremented -CLUSTER_REF - -INTEGER -PACKAGE_REF - -INTEGER -CODE - -INTEGER -MANUFACTURER_CODE - -INTEGER -NAME - -TEXT -TYPE - -TEXT -SIDE - -TEXT -DEFINE - -TEXT -MIN - -TEXT -MAX - -TEXT -MIN_LENGTH - -INTEGER -MAX_LENGTH - -INTEGER -REPORT_MIN_INTERVAL - -INTEGER -REPORT_MAX_INTERVAL - -INTEGER -REPORTABLE_CHANGE - -TEXT -REPORTABLE_CHANGE_LENGTH - -INTEGER -IS_WRITABLE - -INTEGER -DEFAULT_VALUE - -TEXT -IS_SCENE_REQUIRED - -INTEGER -IS_OPTIONAL - -INTEGER -REPORTING_POLICY - -TEXT -STORAGE_POLICY - -TEXT -IS_NULLABLE - -INTEGER -ARRAY_TYPE - -TEXT -MUST_USE_TIMED_WRITE - -INTEGER -INTRODUCED_IN_REF - -INTEGER -REMOVED_IN_REF - -INTEGER -API_MATURITY - -TEXT -IS_CHANGE_OMITTED - -INTEGER -PERSISTENCE - -TEXT - + + +ATTRIBUTE + +[table] +ATTRIBUTE_ID + +INTEGER + +auto-incremented +CLUSTER_REF + +INTEGER +PACKAGE_REF + +INTEGER +CODE + +INTEGER +MANUFACTURER_CODE + +INTEGER +NAME + +TEXT +TYPE + +TEXT +SIDE + +TEXT +DEFINE + +TEXT +CONFORMANCE + +TEXT +MIN + +TEXT +MAX + +TEXT +MIN_LENGTH + +INTEGER +MAX_LENGTH + +INTEGER +REPORT_MIN_INTERVAL + +INTEGER +REPORT_MAX_INTERVAL + +INTEGER +REPORTABLE_CHANGE + +TEXT +REPORTABLE_CHANGE_LENGTH + +INTEGER +IS_WRITABLE + +INTEGER +DEFAULT_VALUE + +TEXT +IS_SCENE_REQUIRED + +INTEGER +IS_OPTIONAL + +INTEGER +REPORTING_POLICY + +TEXT +STORAGE_POLICY + +TEXT +IS_NULLABLE + +INTEGER +ARRAY_TYPE + +TEXT +MUST_USE_TIMED_WRITE + +INTEGER +INTRODUCED_IN_REF + +INTEGER +REMOVED_IN_REF + +INTEGER +API_MATURITY + +TEXT +IS_CHANGE_OMITTED + +INTEGER +PERSISTENCE + +TEXT +MANUFACTURER_CODE_DERIVED + +generated + attribute_access_b017dce6:w->attribute_a6e02edb:e - - - - - - - -SCHCRWLR_A6DFBA7C_B0176887 + + + + + + + cluster_access_38ea13c8 - -CLUSTER_ACCESS - -[table] -CLUSTER_REF - -INTEGER -ACCESS_REF - -INTEGER - + + +CLUSTER_ACCESS + +[table] +CLUSTER_REF + +INTEGER +ACCESS_REF + +INTEGER + cluster_access_38ea13c8:w->access_72bb1dc3:e - - - - - - - -SCHCRWLR_72BAA964_38E99F69 + + + + + + + cluster_5ec71239 - -CLUSTER - -[table] -CLUSTER_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -DOMAIN_NAME - -TEXT -CODE - -INTEGER -MANUFACTURER_CODE - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT -DEFINE - -TEXT -IS_SINGLETON - -INTEGER -REVISION - -INTEGER -INTRODUCED_IN_REF - -INTEGER -REMOVED_IN_REF - -INTEGER -API_MATURITY - -TEXT - + + +CLUSTER + +[table] +CLUSTER_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +DOMAIN_NAME + +TEXT +CODE + +INTEGER +MANUFACTURER_CODE + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT +DEFINE + +TEXT +IS_SINGLETON + +INTEGER +REVISION + +INTEGER +INTRODUCED_IN_REF + +INTEGER +REMOVED_IN_REF + +INTEGER +API_MATURITY + +TEXT +MANUFACTURER_CODE_DERIVED + +generated + cluster_access_38ea13c8:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_38E99F69 + + + + + + + command_access_b02dd957 - -COMMAND_ACCESS - -[table] -COMMAND_REF - -INTEGER -ACCESS_REF - -INTEGER - + + +COMMAND_ACCESS + +[table] +COMMAND_REF + +INTEGER +ACCESS_REF + +INTEGER + command_access_b02dd957:w->access_72bb1dc3:e - - - - - - - -SCHCRWLR_72BAA964_B02D64F8 + + + + + + + command_6371df8a - -COMMAND - -[table] -COMMAND_ID - -INTEGER - -auto-incremented -CLUSTER_REF - -INTEGER -PACKAGE_REF - -INTEGER -CODE - -INTEGER -MANUFACTURER_CODE - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT -SOURCE - -TEXT -IS_OPTIONAL - -INTEGER -MUST_USE_TIMED_INVOKE - -INTEGER -IS_FABRIC_SCOPED - -INTEGER -INTRODUCED_IN_REF - -INTEGER -REMOVED_IN_REF - -INTEGER -RESPONSE_NAME - -INTEGER -RESPONSE_REF - -INTEGER -IS_DEFAULT_RESPONSE_ENABLED - -INTEGER -IS_LARGE_MESSAGE - -INTEGER - + + +COMMAND + +[table] +COMMAND_ID + +INTEGER + +auto-incremented +CLUSTER_REF + +INTEGER +PACKAGE_REF + +INTEGER +CODE + +INTEGER +MANUFACTURER_CODE + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT +SOURCE + +TEXT +IS_OPTIONAL + +INTEGER +CONFORMANCE + +TEXT +MUST_USE_TIMED_INVOKE + +INTEGER +IS_FABRIC_SCOPED + +INTEGER +INTRODUCED_IN_REF + +INTEGER +REMOVED_IN_REF + +INTEGER +RESPONSE_NAME + +INTEGER +RESPONSE_REF + +INTEGER +IS_DEFAULT_RESPONSE_ENABLED + +INTEGER +IS_LARGE_MESSAGE + +INTEGER +MANUFACTURER_CODE_DERIVED + +generated + command_access_b02dd957:w->command_6371df8a:e - - - - - - - -SCHCRWLR_63716B2B_B02D64F8 + + + + + + + default_access_7ba041a1 - -DEFAULT_ACCESS - -[table] -PACKAGE_REF - -INTEGER -ENTITY_TYPE - -TEXT -ACCESS_REF - -INTEGER - + + +DEFAULT_ACCESS + +[table] +PACKAGE_REF + +INTEGER +ENTITY_TYPE + +TEXT +ACCESS_REF + +INTEGER + default_access_7ba041a1:w->access_72bb1dc3:e - - - - - - - -SCHCRWLR_72BAA964_7B9FCD42 + + + + + + + package_fab13485 - -PACKAGE - -[table] -PACKAGE_ID - -INTEGER - -auto-incremented -PARENT_PACKAGE_REF - -INTEGER -PATH - -TEXT NOT NULL -TYPE - -TEXT -CRC - -INTEGER -VERSION - -INTEGER -CATEGORY - -TEXT -DESCRIPTION - -TEXT -IS_IN_SYNC - -BOOLEAN - + + +PACKAGE + +[table] +PACKAGE_ID + +INTEGER + +auto-incremented +PARENT_PACKAGE_REF + +INTEGER +PATH + +TEXT NOT NULL +TYPE + +TEXT +CRC + +INTEGER +VERSION + +INTEGER +CATEGORY + +TEXT +DESCRIPTION + +TEXT +IS_IN_SYNC + +BOOLEAN + default_access_7ba041a1:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_7B9FCD42 + + + + + + + event_access_4668c328 - -EVENT_ACCESS - -[table] -EVENT_REF - -INTEGER -ACCESS_REF - -INTEGER - + + +EVENT_ACCESS + +[table] +EVENT_REF + +INTEGER +ACCESS_REF + +INTEGER + event_access_4668c328:w->access_72bb1dc3:e - - - - - - - -SCHCRWLR_72BAA964_46684EC9 + + + + + + + event_3f4eed9 - -EVENT - -[table] -EVENT_ID - -INTEGER - -auto-incremented -CLUSTER_REF - -INTEGER -PACKAGE_REF - -INTEGER -CODE - -INTEGER -MANUFACTURER_CODE - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT -SIDE - -TEXT -IS_OPTIONAL - -INTEGER -IS_FABRIC_SENSITIVE - -INTEGER -PRIORITY - -TEXT -INTRODUCED_IN_REF - -INTEGER -REMOVED_IN_REF - -INTEGER - + + +EVENT + +[table] +EVENT_ID + +INTEGER + +auto-incremented +CLUSTER_REF + +INTEGER +PACKAGE_REF + +INTEGER +CODE + +INTEGER +MANUFACTURER_CODE + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT +SIDE + +TEXT +CONFORMANCE + +TEXT +IS_OPTIONAL + +INTEGER +IS_FABRIC_SENSITIVE + +INTEGER +PRIORITY + +TEXT +INTRODUCED_IN_REF + +INTEGER +REMOVED_IN_REF + +INTEGER +MANUFACTURER_CODE_DERIVED + +generated + event_access_4668c328:w->event_3f4eed9:e - - - - - - - -SCHCRWLR_03F47A7A_46684EC9 + + + + + + + access_modifier_f63f3fb1:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_F63ECB52 + + + + + + + atomic_73b03e8a - -"ATOMIC" - -[table] -ATOMIC_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT -ATOMIC_IDENTIFIER - -INTEGER -ATOMIC_SIZE - -INTEGER -IS_DISCRETE - -INTEGER -IS_STRING - -INTEGER -IS_LONG - -INTEGER -IS_CHAR - -INTEGER -IS_SIGNED - -INTEGER -IS_COMPOSITE - -INTEGER -IS_FLOAT - -INTEGER -BASE_TYPE - -TEXT - + + +"ATOMIC" + +[table] +ATOMIC_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT +ATOMIC_IDENTIFIER + +INTEGER +ATOMIC_SIZE + +INTEGER +IS_DISCRETE + +INTEGER +IS_STRING + +INTEGER +IS_LONG + +INTEGER +IS_CHAR + +INTEGER +IS_SIGNED + +INTEGER +IS_COMPOSITE + +INTEGER +IS_FLOAT + +INTEGER +BASE_TYPE + +TEXT + atomic_73b03e8a:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_CE3170F5 + + + + + + + attribute_a6e02edb:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_A6DFBA7C + + + + + + + attribute_a6e02edb:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_A6DFBA7C + + + + + + + spec_27641a - -SPEC - -[table] -SPEC_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -CODE - -TEXT NOT NULL -DESCRIPTION - -TEXT -CERTIFIABLE - -INTEGER - + + +SPEC + +[table] +SPEC_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +CODE + +TEXT NOT NULL +DESCRIPTION + +TEXT +CERTIFIABLE + +INTEGER + attribute_a6e02edb:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_A6DFBA7C + + + + + + + attribute_a6e02edb:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_A6DFBA7C + + + + + + + device_type_attribute_ce5151f - -DEVICE_TYPE_ATTRIBUTE - -[table] -DEVICE_TYPE_CLUSTER_REF - -INTEGER -ATTRIBUTE_REF - -INTEGER -ATTRIBUTE_NAME - -TEXT - + + +DEVICE_TYPE_ATTRIBUTE + +[table] +DEVICE_TYPE_CLUSTER_REF + +INTEGER +ATTRIBUTE_REF + +INTEGER +ATTRIBUTE_NAME + +TEXT + device_type_attribute_ce5151f:w->attribute_a6e02edb:e - - - - - - - -SCHCRWLR_A6DFBA7C_0CE4A0C0 + + + + + + + device_type_cluster_7298b97d - -DEVICE_TYPE_CLUSTER - -[table] -DEVICE_TYPE_CLUSTER_ID - -INTEGER - -auto-incremented -DEVICE_TYPE_REF - -INTEGER -CLUSTER_REF - -INTEGER -CLUSTER_NAME - -TEXT -INCLUDE_CLIENT - -INTEGER -INCLUDE_SERVER - -INTEGER -LOCK_CLIENT - -INTEGER -LOCK_SERVER - -INTEGER - + + +DEVICE_TYPE_CLUSTER + +[table] +DEVICE_TYPE_CLUSTER_ID + +INTEGER + +auto-incremented +DEVICE_TYPE_REF + +INTEGER +CLUSTER_REF + +INTEGER +CLUSTER_NAME + +TEXT +INCLUDE_CLIENT + +INTEGER +INCLUDE_SERVER + +INTEGER +LOCK_CLIENT + +INTEGER +LOCK_SERVER + +INTEGER + device_type_attribute_ce5151f:w->device_type_cluster_7298b97d:e - - - - - - - -SCHCRWLR_7298451E_0CE4A0C0 + + + + + + + endpoint_type_attribute_c265400 - -ENDPOINT_TYPE_ATTRIBUTE - -[table] -ENDPOINT_TYPE_ATTRIBUTE_ID - -INTEGER - -auto-incremented -ENDPOINT_TYPE_CLUSTER_REF - -INTEGER -ATTRIBUTE_REF - -INTEGER -INCLUDED - -INTEGER -STORAGE_OPTION - -TEXT -SINGLETON - -INTEGER -BOUNDED - -INTEGER -DEFAULT_VALUE - -TEXT -INCLUDED_REPORTABLE - -INTEGER -MIN_INTERVAL - -INTEGER -MAX_INTERVAL - -INTEGER -REPORTABLE_CHANGE - -INTEGER - + + +ENDPOINT_TYPE_ATTRIBUTE + +[table] +ENDPOINT_TYPE_ATTRIBUTE_ID + +INTEGER + +auto-incremented +ENDPOINT_TYPE_CLUSTER_REF + +INTEGER +ATTRIBUTE_REF + +INTEGER +INCLUDED + +INTEGER +STORAGE_OPTION + +TEXT +SINGLETON + +INTEGER +BOUNDED + +INTEGER +DEFAULT_VALUE + +TEXT +INCLUDED_REPORTABLE + +INTEGER +MIN_INTERVAL + +INTEGER +MAX_INTERVAL + +INTEGER +REPORTABLE_CHANGE + +INTEGER + endpoint_type_attribute_c265400:w->attribute_a6e02edb:e - - - - - - - -SCHCRWLR_A6DFBA7C_0C25DFA1 + + + + + + + endpoint_type_cluster_c12e3c9e - -ENDPOINT_TYPE_CLUSTER - -[table] -ENDPOINT_TYPE_CLUSTER_ID - -INTEGER - -auto-incremented -ENDPOINT_TYPE_REF - -INTEGER -CLUSTER_REF - -INTEGER -SIDE - -TEXT -ENABLED - -INTEGER - + + +ENDPOINT_TYPE_CLUSTER + +[table] +ENDPOINT_TYPE_CLUSTER_ID + +INTEGER + +auto-incremented +ENDPOINT_TYPE_REF + +INTEGER +CLUSTER_REF + +INTEGER +SIDE + +TEXT +ENABLED + +INTEGER + endpoint_type_attribute_c265400:w->endpoint_type_cluster_c12e3c9e:e - - - - - - - -SCHCRWLR_C12DC83F_0C25DFA1 + + + + + + + global_attribute_default_73c65a21 - -GLOBAL_ATTRIBUTE_DEFAULT - -[table] -GLOBAL_ATTRIBUTE_DEFAULT_ID - -INTEGER - -auto-incremented -CLUSTER_REF - -INTEGER NOT NULL -ATTRIBUTE_REF - -INTEGER NOT NULL -DEFAULT_VALUE - -TEXT - + + +GLOBAL_ATTRIBUTE_DEFAULT + +[table] +GLOBAL_ATTRIBUTE_DEFAULT_ID + +INTEGER + +auto-incremented +CLUSTER_REF + +INTEGER NOT NULL +ATTRIBUTE_REF + +INTEGER NOT NULL +DEFAULT_VALUE + +TEXT + global_attribute_default_73c65a21:w->attribute_a6e02edb:e - - - - - - - -SCHCRWLR_A6DFBA7C_73C5E5C2 + + + + + + + global_attribute_default_73c65a21:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_73C5E5C2 + + + + + + + attribute_mapping_caf33e4a - -ATTRIBUTE_MAPPING - -[table] -ATTRIBUTE_MAPPING_ID - -INTEGER - -auto-incremented -ATTRIBUTE_LEFT_REF - -INTEGER -ATTRIBUTE_RIGHT_REF - -INTEGER - + + +ATTRIBUTE_MAPPING + +[table] +ATTRIBUTE_MAPPING_ID + +INTEGER + +auto-incremented +ATTRIBUTE_LEFT_REF + +INTEGER +ATTRIBUTE_RIGHT_REF + +INTEGER + attribute_mapping_caf33e4a:w->attribute_a6e02edb:e - - - - - - - - -SCHCRWLR_A6DFBA7C_CAF2C9EB + + + + + + + + attribute_mapping_caf33e4a:w->attribute_a6e02edb:e - - - - - - - - -SCHCRWLR_A6DFBA7C_CAF2C9EB + + + + + + + + bitmap_74cc598e - -BITMAP - -[table] -BITMAP_ID - -INTEGER NOT NULL -SIZE - -INTEGER - + + +BITMAP + +[table] +BITMAP_ID + +INTEGER NOT NULL +SIZE + +INTEGER + data_type_9233070e - -DATA_TYPE - -[table] -DATA_TYPE_ID - -INTEGER NOT NULL - -auto-incremented -NAME - -TEXT -DESCRIPTION - -TEXT -DISCRIMINATOR_REF - -INTEGER -PACKAGE_REF - -INTEGER - + + +DATA_TYPE + +[table] +DATA_TYPE_ID + +INTEGER NOT NULL + +auto-incremented +NAME + +TEXT +DESCRIPTION + +TEXT +DISCRIMINATOR_REF + +INTEGER +PACKAGE_REF + +INTEGER + bitmap_74cc598e:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_74CBE52F + + + + + + + bitmap_field_bfea8629 - -BITMAP_FIELD - -[table] -BITMAP_FIELD_ID - -INTEGER NOT NULL - -auto-incremented -BITMAP_REF - -INTEGER -FIELD_IDENTIFIER - -INTEGER -NAME - -TEXT(100) -MASK - -INTEGER -TYPE - -TEXT(100) - + + +BITMAP_FIELD + +[table] +BITMAP_FIELD_ID + +INTEGER NOT NULL + +auto-incremented +BITMAP_REF + +INTEGER +FIELD_IDENTIFIER + +INTEGER +NAME + +TEXT(100) +MASK + +INTEGER +TYPE + +TEXT(100) + bitmap_field_bfea8629:w->bitmap_74cc598e:e - - - - - - - -SCHCRWLR_74CBE52F_BFEA11CA + + + + + + + cluster_5ec71239:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_5EC69DDA + + + + + + + cluster_5ec71239:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_5EC69DDA + + + + + + + cluster_5ec71239:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_5EC69DDA + + + + + + + command_6371df8a:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_63716B2B + + + + + + + command_6371df8a:w->command_6371df8a:e - - - - - - - -SCHCRWLR_63716B2B_63716B2B + + + + + + + command_6371df8a:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_63716B2B + + + + + + + command_6371df8a:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_63716B2B + + + + + + + command_6371df8a:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_63716B2B + + + + + + + data_type_cluster_8d9f2ca9 - -DATA_TYPE_CLUSTER - -[table] -DATA_TYPE_CLUSTER_ID - -INTEGER NOT NULL - -auto-incremented -CLUSTER_REF - -INTEGER -CLUSTER_CODE - -INTEGER -DATA_TYPE_REF - -INTEGER - + + +DATA_TYPE_CLUSTER + +[table] +DATA_TYPE_CLUSTER_ID + +INTEGER NOT NULL + +auto-incremented +CLUSTER_REF + +INTEGER +CLUSTER_CODE + +INTEGER +DATA_TYPE_REF + +INTEGER + data_type_cluster_8d9f2ca9:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_8D9EB84A + + + + + + + data_type_cluster_8d9f2ca9:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_8D9EB84A + + + + + + + device_type_cluster_7298b97d:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_7298451E + + + + + + + device_type_2620a7e2 - -DEVICE_TYPE - -[table] -DEVICE_TYPE_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -DOMAIN - -TEXT -CODE - -INTEGER -PROFILE_ID - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT -CLASS - -TEXT -"SCOPE" - -TEXT -SUPERSET - -TEXT - + + +DEVICE_TYPE + +[table] +DEVICE_TYPE_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +DOMAIN + +TEXT +CODE + +INTEGER +PROFILE_ID + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT +CLASS + +TEXT +"SCOPE" + +TEXT +SUPERSET + +TEXT + device_type_cluster_7298b97d:w->device_type_2620a7e2:e - - - - - - - -SCHCRWLR_26203383_7298451E + + + + + + + endpoint_type_cluster_c12e3c9e:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_C12DC83F + + + + + + + endpoint_type_9857dc03 - -ENDPOINT_TYPE - -[table] -ENDPOINT_TYPE_ID - -INTEGER - -auto-incremented -SESSION_PARTITION_REF - -INTEGER -NAME - -TEXT - + + +ENDPOINT_TYPE + +[table] +ENDPOINT_TYPE_ID + +INTEGER + +auto-incremented +SESSION_PARTITION_REF + +INTEGER +NAME + +TEXT + endpoint_type_cluster_c12e3c9e:w->endpoint_type_9857dc03:e - - - - - - - -SCHCRWLR_985767A4_C12DC83F + + + + + + + event_3f4eed9:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_03F47A7A + + + + + + + event_3f4eed9:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_03F47A7A + + + + + + + event_3f4eed9:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_03F47A7A + + + + + + + event_3f4eed9:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_03F47A7A + + + + + + + feature_f06e7b35 - -FEATURE - -[table] -FEATURE_ID - -INTEGER - -auto-incremented -NAME - -TEXT -CODE - -TEXT -BIT - -INTEGER -DEFAULT_VALUE - -INTEGER -DESCRIPTION - -TEXT -CONFORMANCE - -TEXT -PACKAGE_REF - -INTEGER -CLUSTER_REF - -INTEGER - + + +FEATURE + +[table] +FEATURE_ID + +INTEGER + +auto-incremented +NAME + +TEXT +CODE + +TEXT +BIT + +INTEGER +DEFAULT_VALUE + +INTEGER +DESCRIPTION + +TEXT +CONFORMANCE + +TEXT +PACKAGE_REF + +INTEGER +CLUSTER_REF + +INTEGER + feature_f06e7b35:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_F06E06D6 + + + + + + + feature_f06e7b35:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_F06E06D6 + + + + + + + tag_1b7d9 - -TAG - -[table] -TAG_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -CLUSTER_REF - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT - + + +TAG + +[table] +TAG_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +CLUSTER_REF + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT + tag_1b7d9:w->cluster_5ec71239:e - - - - - - - -SCHCRWLR_5EC69DDA_0001437A + + + + + + + tag_1b7d9:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_0001437A + + + + + + + command_arg_294e7f81 - -COMMAND_ARG - -[table] -COMMAND_REF - -INTEGER -FIELD_IDENTIFIER - -INTEGER -NAME - -TEXT -TYPE - -TEXT -MIN - -TEXT -MAX - -TEXT -MIN_LENGTH - -INTEGER -MAX_LENGTH - -INTEGER -IS_ARRAY - -INTEGER -PRESENT_IF - -TEXT -IS_NULLABLE - -INTEGER -IS_OPTIONAL - -INTEGER -COUNT_ARG - -TEXT -INTRODUCED_IN_REF - -INTEGER -REMOVED_IN_REF - -INTEGER - + + +COMMAND_ARG + +[table] +COMMAND_REF + +INTEGER +FIELD_IDENTIFIER + +INTEGER +NAME + +TEXT +TYPE + +TEXT +MIN + +TEXT +MAX + +TEXT +MIN_LENGTH + +INTEGER +MAX_LENGTH + +INTEGER +IS_ARRAY + +INTEGER +PRESENT_IF + +TEXT +IS_NULLABLE + +INTEGER +IS_OPTIONAL + +INTEGER +COUNT_ARG + +TEXT +INTRODUCED_IN_REF + +INTEGER +REMOVED_IN_REF + +INTEGER + command_arg_294e7f81:w->command_6371df8a:e - - - - - - - -SCHCRWLR_63716B2B_294E0B22 + + + + + + + command_arg_294e7f81:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_294E0B22 + + + + + + + command_arg_294e7f81:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_294E0B22 + + + + + + + device_type_command_774386ce - -DEVICE_TYPE_COMMAND - -[table] -DEVICE_TYPE_CLUSTER_REF - -INTEGER -COMMAND_REF - -INTEGER -COMMAND_NAME - -TEXT - + + +DEVICE_TYPE_COMMAND + +[table] +DEVICE_TYPE_CLUSTER_REF + +INTEGER +COMMAND_REF + +INTEGER +COMMAND_NAME + +TEXT + device_type_command_774386ce:w->command_6371df8a:e - - - - - - - -SCHCRWLR_63716B2B_7743126F + + + + + + + device_type_command_774386ce:w->device_type_cluster_7298b97d:e - - - - - - - -SCHCRWLR_7298451E_7743126F + + + + + + + endpoint_type_command_c5d909ef - -ENDPOINT_TYPE_COMMAND - -[table] -ENDPOINT_TYPE_COMMAND_ID - -INTEGER - -auto-incremented -ENDPOINT_TYPE_CLUSTER_REF - -INTEGER -COMMAND_REF - -INTEGER -IS_INCOMING - -INTEGER -IS_ENABLED - -INTEGER - + + +ENDPOINT_TYPE_COMMAND + +[table] +ENDPOINT_TYPE_COMMAND_ID + +INTEGER + +auto-incremented +ENDPOINT_TYPE_CLUSTER_REF + +INTEGER +COMMAND_REF + +INTEGER +IS_INCOMING + +INTEGER +IS_ENABLED + +INTEGER + endpoint_type_command_c5d909ef:w->command_6371df8a:e - - - - - - - -SCHCRWLR_63716B2B_C5D89590 + + + + + + + endpoint_type_command_c5d909ef:w->endpoint_type_cluster_c12e3c9e:e - - - - - - - -SCHCRWLR_C12DC83F_C5D89590 + + + + + + + discriminator_4931d2db - -DISCRIMINATOR - -[table] -DISCRIMINATOR_ID - -INTEGER NOT NULL - -auto-incremented -NAME - -TEXT -PACKAGE_REF - -INTEGER - + + +DISCRIMINATOR + +[table] +DISCRIMINATOR_ID + +INTEGER NOT NULL + +auto-incremented +NAME + +TEXT +PACKAGE_REF + +INTEGER + data_type_9233070e:w->discriminator_4931d2db:e - - - - - - - -SCHCRWLR_49315E7C_923292AF + + + + + + + data_type_9233070e:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_923292AF + + + + + + + enum_210160 - -ENUM - -[table] -ENUM_ID - -INTEGER NOT NULL -SIZE - -INTEGER - + + +ENUM + +[table] +ENUM_ID + +INTEGER NOT NULL +SIZE + +INTEGER + enum_210160:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_00208D01 + + + + + + + number_89ec43a8 - -NUMBER - -[table] -NUMBER_ID - -INTEGER NOT NULL -SIZE - -INTEGER -IS_SIGNED - -INTEGER - + + +NUMBER + +[table] +NUMBER_ID + +INTEGER NOT NULL +SIZE + +INTEGER +IS_SIGNED + +INTEGER + number_89ec43a8:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_89EBCF49 + + + + + + + string_9268c870 - -STRING - -[table] -STRING_ID - -INTEGER NOT NULL -IS_LONG - -INTEGER -SIZE - -INTEGER -IS_CHAR - -INTEGER - + + +STRING + +[table] +STRING_ID + +INTEGER NOT NULL +IS_LONG + +INTEGER +SIZE + +INTEGER +IS_CHAR + +INTEGER + string_9268c870:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_92685411 + + + + + + + struct_9268f434 - -STRUCT - -[table] -STRUCT_ID - -INTEGER NOT NULL -IS_FABRIC_SCOPED - -INTEGER -SIZE - -INTEGER -API_MATURITY - -TEXT - + + +STRUCT + +[table] +STRUCT_ID + +INTEGER NOT NULL +IS_FABRIC_SCOPED + +INTEGER +SIZE + +INTEGER +API_MATURITY + +TEXT + struct_9268f434:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_92687FD5 + + + + + + + struct_item_d6e4bd9c - -STRUCT_ITEM - -[table] -STRUCT_ITEM_ID - -INTEGER NOT NULL - -auto-incremented -STRUCT_REF - -INTEGER -FIELD_IDENTIFIER - -INTEGER -NAME - -TEXT(100) -IS_ARRAY - -INTEGER -IS_ENUM - -INTEGER -MIN_LENGTH - -INTEGER -MAX_LENGTH - -INTEGER -IS_WRITABLE - -INTEGER -IS_NULLABLE - -INTEGER -IS_OPTIONAL - -INTEGER -IS_FABRIC_SENSITIVE - -INTEGER -SIZE - -INTEGER -DATA_TYPE_REF - -INTEGER NOT NULL - + + +STRUCT_ITEM + +[table] +STRUCT_ITEM_ID + +INTEGER NOT NULL + +auto-incremented +STRUCT_REF + +INTEGER +FIELD_IDENTIFIER + +INTEGER +NAME + +TEXT(100) +IS_ARRAY + +INTEGER +IS_ENUM + +INTEGER +MIN_LENGTH + +INTEGER +MAX_LENGTH + +INTEGER +IS_WRITABLE + +INTEGER +IS_NULLABLE + +INTEGER +IS_OPTIONAL + +INTEGER +IS_FABRIC_SENSITIVE + +INTEGER +SIZE + +INTEGER +DATA_TYPE_REF + +INTEGER NOT NULL + struct_item_d6e4bd9c:w->data_type_9233070e:e - - - - - - - -SCHCRWLR_923292AF_D6E4493D + + + + + + + struct_item_d6e4bd9c:w->struct_9268f434:e - - - - - - - -SCHCRWLR_92687FD5_D6E4493D + + + + + + + device_composition_eab6b180 - -DEVICE_COMPOSITION - -[table] -DEVICE_COMPOSITION_ID - -INTEGER - -auto-incremented -CODE - -INTEGER -DEVICE_TYPE_REF - -INTEGER -ENDPOINT_COMPOSITION_REF - -INTEGER -CONFORMANCE - -TEXT -DEVICE_CONSTRAINT - -INTEGER - + + +DEVICE_COMPOSITION + +[table] +DEVICE_COMPOSITION_ID + +INTEGER + +auto-incremented +CODE + +INTEGER +DEVICE_TYPE_REF + +INTEGER +ENDPOINT_COMPOSITION_REF + +INTEGER +CONFORMANCE + +TEXT +DEVICE_CONSTRAINT + +INTEGER + device_composition_eab6b180:w->device_type_2620a7e2:e - - - - - - - -SCHCRWLR_26203383_EAB63D21 + + + + + + + endpoint_composition_fdc3c63f - -ENDPOINT_COMPOSITION - -[table] -ENDPOINT_COMPOSITION_ID - -INTEGER - -auto-incremented -DEVICE_TYPE_REF - -INTEGER -TYPE - -TEXT -CODE - -INTEGER - + + +ENDPOINT_COMPOSITION + +[table] +ENDPOINT_COMPOSITION_ID + +INTEGER + +auto-incremented +DEVICE_TYPE_REF + +INTEGER +TYPE + +TEXT +CODE + +INTEGER + device_composition_eab6b180:w->endpoint_composition_fdc3c63f:e - - - - - - - -SCHCRWLR_FDC351E0_EAB63D21 + + + + + + + device_type_2620a7e2:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_26203383 + + + + + + + endpoint_composition_fdc3c63f:w->device_type_2620a7e2:e - - - - - - - -SCHCRWLR_26203383_FDC351E0 + + + + + + + endpoint_type_device_e685fbb0 - -ENDPOINT_TYPE_DEVICE - -[table] -ENDPOINT_TYPE_DEVICE_ID - -INTEGER - -auto-incremented -DEVICE_TYPE_REF - -INTEGER -ENDPOINT_TYPE_REF - -INTEGER -DEVICE_TYPE_ORDER - -INTEGER -DEVICE_IDENTIFIER - -INTEGER -DEVICE_VERSION - -INTEGER - + + +ENDPOINT_TYPE_DEVICE + +[table] +ENDPOINT_TYPE_DEVICE_ID + +INTEGER + +auto-incremented +DEVICE_TYPE_REF + +INTEGER +ENDPOINT_TYPE_REF + +INTEGER +DEVICE_TYPE_ORDER + +INTEGER +DEVICE_IDENTIFIER + +INTEGER +DEVICE_VERSION + +INTEGER + endpoint_type_device_e685fbb0:w->device_type_2620a7e2:e - - - - - - - -SCHCRWLR_26203383_E6858751 + + + + + + + endpoint_type_device_e685fbb0:w->endpoint_type_9857dc03:e - - - - - - - -SCHCRWLR_985767A4_E6858751 + + + + + + + device_type_feature_4402279 - -DEVICE_TYPE_FEATURE - -[table] -DEVICE_TYPE_CLUSTER_REF - -INTEGER -FEATURE_REF - -INTEGER -FEATURE_CODE - -TEXT -DEVICE_TYPE_CLUSTER_CONFORMANCE - -TEXT - + + +DEVICE_TYPE_FEATURE + +[table] +DEVICE_TYPE_CLUSTER_REF + +INTEGER +FEATURE_REF + +INTEGER +FEATURE_CODE + +TEXT +DEVICE_TYPE_CLUSTER_CONFORMANCE + +TEXT + device_type_feature_4402279:w->device_type_cluster_7298b97d:e - - - - - - - -SCHCRWLR_7298451E_043FAE1A + + + + + + + device_type_feature_4402279:w->feature_f06e7b35:e - - - - - - - -SCHCRWLR_F06E06D6_043FAE1A + + + + + + + discriminator_4931d2db:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_49315E7C + + + + + + + domain_78873d23 - -DOMAIN - -[table] -DOMAIN_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -NAME - -TEXT -LATEST_SPEC_REF - -INTEGER - + + +DOMAIN + +[table] +DOMAIN_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +NAME + +TEXT +LATEST_SPEC_REF + +INTEGER + domain_78873d23:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_7886C8C4 + + + + + + + domain_78873d23:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_7886C8C4 + + + + + + + endpoint_966d81f4 - -ENDPOINT - -[table] -ENDPOINT_ID - -INTEGER - -auto-incremented -SESSION_REF - -INTEGER -ENDPOINT_TYPE_REF - -INTEGER -PROFILE - -INTEGER -ENDPOINT_IDENTIFIER - -INTEGER -NETWORK_IDENTIFIER - -INTEGER -PARENT_ENDPOINT_REF - -INTEGER - + + +ENDPOINT + +[table] +ENDPOINT_ID + +INTEGER + +auto-incremented +SESSION_REF + +INTEGER +ENDPOINT_TYPE_REF + +INTEGER +PROFILE + +INTEGER +ENDPOINT_IDENTIFIER + +INTEGER +NETWORK_IDENTIFIER + +INTEGER +PARENT_ENDPOINT_REF + +INTEGER + endpoint_966d81f4:w->endpoint_966d81f4:e - - - - - - - -SCHCRWLR_966D0D95_966D0D95 + + + + + + + endpoint_966d81f4:w->endpoint_type_9857dc03:e - - - - - - - -SCHCRWLR_985767A4_966D0D95 + + + + + + + session_a11c82d5 - -SESSION - -[table] -SESSION_ID - -INTEGER - -auto-incremented -USER_REF - -INTEGER -SESSION_KEY - -TEXT -CREATION_TIME - -INTEGER -DIRTY - -INTEGER -NEW_NOTIFICATION - -INTEGER - + + +SESSION + +[table] +SESSION_ID + +INTEGER + +auto-incremented +USER_REF + +INTEGER +SESSION_KEY + +TEXT +CREATION_TIME + +INTEGER +DIRTY + +INTEGER +NEW_NOTIFICATION + +INTEGER + endpoint_966d81f4:w->session_a11c82d5:e - - - - - - - -SCHCRWLR_A11C0E76_966D0D95 + + + + + + + session_partition_f35f84a0 - -SESSION_PARTITION - -[table] -SESSION_PARTITION_ID - -INTEGER - -auto-incremented -SESSION_PARTITION_NUMBER - -INTEGER -SESSION_REF - -INTEGER - + + +SESSION_PARTITION + +[table] +SESSION_PARTITION_ID + +INTEGER + +auto-incremented +SESSION_PARTITION_NUMBER + +INTEGER +SESSION_REF + +INTEGER + endpoint_type_9857dc03:w->session_partition_f35f84a0:e - - - - - - - -SCHCRWLR_F35F1041_985767A4 + + + + + + + endpoint_type_event_e67d6e7e - -ENDPOINT_TYPE_EVENT - -[table] -ENDPOINT_TYPE_EVENT_ID - -INTEGER - -auto-incremented -ENDPOINT_TYPE_CLUSTER_REF - -INTEGER -EVENT_REF - -INTEGER -INCLUDED - -INTEGER - + + +ENDPOINT_TYPE_EVENT + +[table] +ENDPOINT_TYPE_EVENT_ID + +INTEGER + +auto-incremented +ENDPOINT_TYPE_CLUSTER_REF + +INTEGER +EVENT_REF + +INTEGER +INCLUDED + +INTEGER + endpoint_type_event_e67d6e7e:w->endpoint_type_cluster_c12e3c9e:e - - - - - - - -SCHCRWLR_C12DC83F_E67CFA1F + + + + + + + endpoint_type_event_e67d6e7e:w->event_3f4eed9:e - - - - - - - -SCHCRWLR_03F47A7A_E67CFA1F + + + + + + + enum_item_b6420bf0 - -ENUM_ITEM - -[table] -ENUM_ITEM_ID - -INTEGER NOT NULL - -auto-incremented -ENUM_REF - -INTEGER -NAME - -TEXT -DESCRIPTION - -TEXT -FIELD_IDENTIFIER - -INTEGER -"VALUE" - -INTEGER - + + +ENUM_ITEM + +[table] +ENUM_ITEM_ID + +INTEGER NOT NULL + +auto-incremented +ENUM_REF + +INTEGER +NAME + +TEXT +DESCRIPTION + +TEXT +FIELD_IDENTIFIER + +INTEGER +"VALUE" + +INTEGER + enum_item_b6420bf0:w->enum_210160:e - - - - - - - -SCHCRWLR_00208D01_B6419791 + + + + + + + event_field_d102b734 - -EVENT_FIELD - -[table] -EVENT_REF - -INTEGER -FIELD_IDENTIFIER - -INTEGER -NAME - -TEXT -TYPE - -TEXT -IS_ARRAY - -INTEGER -IS_NULLABLE - -INTEGER -IS_OPTIONAL - -INTEGER -INTRODUCED_IN_REF - -INTEGER -REMOVED_IN_REF - -INTEGER - + + +EVENT_FIELD + +[table] +EVENT_REF + +INTEGER +FIELD_IDENTIFIER + +INTEGER +NAME + +TEXT +TYPE + +TEXT +IS_ARRAY + +INTEGER +IS_NULLABLE + +INTEGER +IS_OPTIONAL + +INTEGER +INTRODUCED_IN_REF + +INTEGER +REMOVED_IN_REF + +INTEGER + event_field_d102b734:w->event_3f4eed9:e - - - - - - - -SCHCRWLR_03F47A7A_D10242D5 + + + + + + + event_field_d102b734:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_D10242D5 + + + + + + + event_field_d102b734:w->spec_27641a:e - - - - - - - -SCHCRWLR_0026EFBB_D10242D5 + + + + + + + global_attribute_bit_e934f16d - -GLOBAL_ATTRIBUTE_BIT - -[table] -GLOBAL_ATTRIBUTE_DEFAULT_REF - -INTEGER NOT NULL -BIT - -INTEGER NOT NULL -"VALUE" - -INTEGER -TAG_REF - -INTEGER NOT NULL - + + +GLOBAL_ATTRIBUTE_BIT + +[table] +GLOBAL_ATTRIBUTE_DEFAULT_REF + +INTEGER NOT NULL +BIT + +INTEGER NOT NULL +"VALUE" + +INTEGER +TAG_REF + +INTEGER NOT NULL + global_attribute_bit_e934f16d:w->global_attribute_default_73c65a21:e - - - - - - - -SCHCRWLR_73C5E5C2_E9347D0E + + + + + + + global_attribute_bit_e934f16d:w->tag_1b7d9:e - - - - - - - -SCHCRWLR_0001437A_E9347D0E + + + + + + + operation_93359a6:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_0932E547 + + + + + + + package_fab13485:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_FAB0C026 + + + + + + + package_extension_2789e3a5 - -PACKAGE_EXTENSION - -[table] -PACKAGE_EXTENSION_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -ENTITY - -TEXT -PROPERTY - -TEXT -TYPE - -TEXT -CONFIGURABILITY - -TEXT -LABEL - -TEXT -GLOBAL_DEFAULT - -TEXT - + + +PACKAGE_EXTENSION + +[table] +PACKAGE_EXTENSION_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +ENTITY + +TEXT +PROPERTY + +TEXT +TYPE + +TEXT +CONFIGURABILITY + +TEXT +LABEL + +TEXT +GLOBAL_DEFAULT + +TEXT + package_extension_2789e3a5:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_27896F46 + + + + + + + package_notice_176ee570 - -PACKAGE_NOTICE - -[table] -PACKAGE_REF - -INTEGER -NOTICE_TYPE - -TEXT -NOTICE_MESSAGE - -TEXT -NOTICE_SEVERITY - -INTEGER -NOTICE_ID - -INTEGER - -auto-incremented - + + +PACKAGE_NOTICE + +[table] +PACKAGE_REF + +INTEGER +NOTICE_TYPE + +TEXT +NOTICE_MESSAGE + +TEXT +NOTICE_SEVERITY + +INTEGER +NOTICE_ID + +INTEGER + +auto-incremented + package_notice_176ee570:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_176E7111 + + + + + + + package_option_1931d70d - -PACKAGE_OPTION - -[table] -OPTION_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -OPTION_CATEGORY - -TEXT -OPTION_CODE - -TEXT -OPTION_LABEL - -TEXT - + + +PACKAGE_OPTION + +[table] +OPTION_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +OPTION_CATEGORY + +TEXT +OPTION_CODE + +TEXT +OPTION_LABEL + +TEXT + package_option_1931d70d:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_193162AE + + + + + + + package_option_default_64a251ef - -PACKAGE_OPTION_DEFAULT - -[table] -OPTION_DEFAULT_ID - -INTEGER - -auto-incremented -PACKAGE_REF - -INTEGER -OPTION_CATEGORY - -TEXT -OPTION_REF - -INTEGER - + + +PACKAGE_OPTION_DEFAULT + +[table] +OPTION_DEFAULT_ID + +INTEGER + +auto-incremented +PACKAGE_REF + +INTEGER +OPTION_CATEGORY + +TEXT +OPTION_REF + +INTEGER + package_option_default_64a251ef:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_64A1DD90 + + + + + + + package_option_default_64a251ef:w->package_option_1931d70d:e - - - - - - - -SCHCRWLR_193162AE_64A1DD90 + + + + + + + role_26ecd5:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_00267876 + + + + + + + session_package_61fa13bc - -SESSION_PACKAGE - -[table] -SESSION_PARTITION_REF - -INTEGER -PACKAGE_REF - -INTEGER -REQUIRED - -INTEGER -ENABLED - -INTEGER - + + +SESSION_PACKAGE + +[table] +SESSION_PARTITION_REF + +INTEGER +PACKAGE_REF + +INTEGER +REQUIRED + +INTEGER +ENABLED + +INTEGER + session_package_61fa13bc:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_61F99F5D + + + + + + + session_package_61fa13bc:w->session_partition_f35f84a0:e - - - - - - - -SCHCRWLR_F35F1041_61F99F5D + + + + + + + spec_27641a:w->package_fab13485:e - - - - - - - -SCHCRWLR_FAB0C026_0026EFBB + + + + + + + package_extension_default_d8d04687 - -PACKAGE_EXTENSION_DEFAULT - -[table] -PACKAGE_EXTENSION_REF - -INTEGER -ENTITY_CODE - -INTEGER -ENTITY_QUALIFIER - -TEXT -PARENT_CODE - -INTEGER -MANUFACTURER_CODE - -INTEGER -"VALUE" - -TEXT - + + +PACKAGE_EXTENSION_DEFAULT + +[table] +PACKAGE_EXTENSION_REF + +INTEGER +ENTITY_CODE + +INTEGER +ENTITY_QUALIFIER + +TEXT +PARENT_CODE + +INTEGER +MANUFACTURER_CODE + +INTEGER +"VALUE" + +TEXT + package_extension_default_d8d04687:w->package_extension_2789e3a5:e - - - - - - - -SCHCRWLR_27896F46_D8CFD228 + + + + + + + package_extension_value_8e65d377 - -PACKAGE_EXTENSION_VALUE - -[table] -PACKAGE_EXTENSION_VALUE_ID - -INTEGER - -auto-incremented -PACKAGE_EXTENSION_REF - -INTEGER -SESSION_REF - -INTEGER -ENTITY_CODE - -INTEGER -PARENT_CODE - -INTEGER -"VALUE" - -TEXT - + + +PACKAGE_EXTENSION_VALUE + +[table] +PACKAGE_EXTENSION_VALUE_ID + +INTEGER + +auto-incremented +PACKAGE_EXTENSION_REF + +INTEGER +SESSION_REF + +INTEGER +ENTITY_CODE + +INTEGER +PARENT_CODE + +INTEGER +"VALUE" + +TEXT + package_extension_value_8e65d377:w->package_extension_2789e3a5:e - - - - - - - -SCHCRWLR_27896F46_8E655F18 + + + + + + + package_extension_value_8e65d377:w->session_a11c82d5:e - - - - - - - -SCHCRWLR_A11C0E76_8E655F18 + + + + + + + user_28582a - -"USER" - -[table] -USER_ID - -INTEGER - -auto-incremented -USER_KEY - -TEXT -CREATION_TIME - -INTEGER - + + +"USER" + +[table] +USER_ID + +INTEGER + +auto-incremented +USER_KEY + +TEXT +CREATION_TIME + +INTEGER + session_a11c82d5:w->user_28582a:e - - - - - - - -SCHCRWLR_3ED95AD5_A11C0E76 + + + + + + + session_key_value_334d9527 - -SESSION_KEY_VALUE - -[table] -SESSION_REF - -INTEGER -KEY - -TEXT -"VALUE" - -TEXT - + + +SESSION_KEY_VALUE + +[table] +SESSION_REF + +INTEGER +KEY + +TEXT +"VALUE" + +TEXT + session_key_value_334d9527:w->session_a11c82d5:e - - - - - - - -SCHCRWLR_A11C0E76_334D20C8 + + + + + + + session_log_7f10ae3a - -SESSION_LOG - -[table] -SESSION_REF - -INTEGER -"TIMESTAMP" - -TEXT -LOG - -TEXT - + + +SESSION_LOG + +[table] +SESSION_REF + +INTEGER +"TIMESTAMP" + +TEXT +LOG + +TEXT + session_log_7f10ae3a:w->session_a11c82d5:e - - - - - - - -SCHCRWLR_A11C0E76_7F1039DB + + + + + + + session_notice_84addd20 - -SESSION_NOTICE - -[table] -SESSION_REF - -INTEGER -NOTICE_TYPE - -TEXT -NOTICE_MESSAGE - -TEXT -NOTICE_SEVERITY - -INTEGER -NOTICE_ID - -INTEGER - -auto-incremented -DISPLAY - -INTEGER -SEEN - -INTEGER - + + +SESSION_NOTICE + +[table] +SESSION_REF + +INTEGER +NOTICE_TYPE + +TEXT +NOTICE_MESSAGE + +TEXT +NOTICE_SEVERITY + +INTEGER +NOTICE_ID + +INTEGER + +auto-incremented +DISPLAY + +INTEGER +SEEN + +INTEGER + session_notice_84addd20:w->session_a11c82d5:e - - - - - - - -SCHCRWLR_A11C0E76_84AD68C1 + + + + + + + session_partition_f35f84a0:w->session_a11c82d5:e - - - - - - - -SCHCRWLR_A11C0E76_F35F1041 + + + + + + + setting_a12b0e8f - -SETTING - -[table] -CATEGORY - -TEXT -KEY - -TEXT -"VALUE" - -TEXT - + + +SETTING + +[table] +CATEGORY + +TEXT +KEY + +TEXT +"VALUE" + +TEXT + diff --git a/src-electron/db/db-mapping.js b/src-electron/db/db-mapping.js index 8c8b4258e0..e10a8756dc 100644 --- a/src-electron/db/db-mapping.js +++ b/src-electron/db/db-mapping.js @@ -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, @@ -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 @@ -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, @@ -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 } }, @@ -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) => { @@ -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 } }, @@ -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 { @@ -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 { diff --git a/src-electron/db/query-attribute.js b/src-electron/db/query-attribute.js index a3e10718ba..a817e13af6 100644 --- a/src-electron/db/query-attribute.js +++ b/src-electron/db/query-attribute.js @@ -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 ) @@ -1274,3 +1311,5 @@ exports.selectTokenAttributesForEndpoint = selectTokenAttributesForEndpoint exports.selectAllUserTokenAttributes = selectAllUserTokenAttributes exports.selectAttributeMappingsByPackageIds = selectAttributeMappingsByPackageIds +exports.selectAttributesByEndpointTypeClusterId = + selectAttributesByEndpointTypeClusterId diff --git a/src-electron/db/query-command.js b/src-electron/db/query-command.js index 97483cd7fb..bc708d9917 100644 --- a/src-electron/db/query-command.js +++ b/src-electron/db/query-command.js @@ -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, @@ -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, @@ -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 @@ -2098,3 +2144,5 @@ exports.selectAllOutgoingCommandsForCluster = exports.selectEndpointTypeCommandsByEndpointTypeRefAndClusterRef = selectEndpointTypeCommandsByEndpointTypeRefAndClusterRef exports.duplicateEndpointTypeCommand = duplicateEndpointTypeCommand +exports.selectCommandsByEndpointTypeClusterIdAndDeviceTypeClusterId = + selectCommandsByEndpointTypeClusterIdAndDeviceTypeClusterId diff --git a/src-electron/db/query-config.js b/src-electron/db/query-config.js index 2437b57c34..35a757b5be 100644 --- a/src-electron/db/query-config.js +++ b/src-electron/db/query-config.js @@ -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)) diff --git a/src-electron/db/query-device-type.js b/src-electron/db/query-device-type.js index 688848badd..3ba55e365f 100644 --- a/src-electron/db/query-device-type.js +++ b/src-electron/db/query-device-type.js @@ -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 diff --git a/src-electron/db/query-event.js b/src-electron/db/query-event.js index addafd8acf..6e85e11c88 100644 --- a/src-electron/db/query-event.js +++ b/src-electron/db/query-event.js @@ -113,6 +113,7 @@ SELECT NAME, DESCRIPTION, SIDE, + CONFORMANCE, IS_OPTIONAL, IS_FABRIC_SENSITIVE, PRIORITY @@ -148,6 +149,7 @@ SELECT E.NAME, E.DESCRIPTION, E.SIDE, + E.CONFORMANCE, E.IS_OPTIONAL, E.IS_FABRIC_SENSITIVE, E.PRIORITY @@ -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 diff --git a/src-electron/db/query-feature.js b/src-electron/db/query-feature.js index 98f9e64110..60a3045267 100644 --- a/src-electron/db/query-feature.js +++ b/src-electron/db/query-feature.js @@ -20,57 +20,616 @@ * * @module DB API: feature related queries */ -const dbApi = require('./db-api.js') -const dbMapping = require('./db-mapping.js') +const dbApi = require('./db-api') +const dbMapping = require('./db-mapping') /** - * Get all device type features associated with a list of device type refs + * Get all device type features associated with a list of device type refs and an endpoint. + * Join ENDPOINT_TYPE_ATTRIBUTE and ATTRIBUTE table to get featureMap attribute associated with the feature, + * so the frontend could get and set featureMap bit easier. + * Only return features with cluster on the side specified in the device type. + * + * @export * @param {*} db * @param {*} deviceTypeRefs + * @param {*} endpointTypeRef * @returns All feature information and device type conformance - * with associated device type and cluster details + * with associated device type, cluster, and featureMap attribute details */ -async function getFeaturesByDeviceTypeRefs(db, deviceTypeRefs) { +async function getFeaturesByDeviceTypeRefs( + db, + deviceTypeRefs, + endpointTypeRef +) { + let arg = [] let deviceTypeRefsSql = deviceTypeRefs.map(() => '?').join(', ') + arg.push(...deviceTypeRefs) + arg.push(endpointTypeRef) let features = await dbApi.dbAll( db, ` SELECT - d.DESCRIPTION AS DEVICE_TYPE_NAME, - dc.CLUSTER_NAME, - dc.INCLUDE_SERVER, - dc.INCLUDE_CLIENT, - df.DEVICE_TYPE_CLUSTER_CONFORMANCE, - f.FEATURE_ID, - f.NAME AS FEATURE_NAME, - f.CODE, - f.BIT, - f.DEFAULT_VALUE, - f.DESCRIPTION + D.DESCRIPTION AS DEVICE_TYPE_NAME, + DC.DEVICE_TYPE_CLUSTER_ID, + DC.CLUSTER_REF, + DC.CLUSTER_NAME, + DC.INCLUDE_SERVER, + DC.INCLUDE_CLIENT, + DF.DEVICE_TYPE_CLUSTER_CONFORMANCE, + F.FEATURE_ID, + F.NAME AS FEATURE_NAME, + F.CODE, + F.BIT, + F.DESCRIPTION, + ETC.ENDPOINT_TYPE_CLUSTER_ID, + ETA.ENDPOINT_TYPE_ATTRIBUTE_ID AS FEATURE_MAP_ATTRIBUTE_ID, + ETA.DEFAULT_VALUE AS FEATURE_MAP_VALUE FROM - DEVICE_TYPE d + DEVICE_TYPE D JOIN - DEVICE_TYPE_CLUSTER dc + DEVICE_TYPE_CLUSTER DC ON - d.DEVICE_TYPE_ID = dc.DEVICE_TYPE_REF + D.DEVICE_TYPE_ID = DC.DEVICE_TYPE_REF JOIN - DEVICE_TYPE_FEATURE df + DEVICE_TYPE_FEATURE DF ON - dc.DEVICE_TYPE_CLUSTER_ID = df.DEVICE_TYPE_CLUSTER_REF + DC.DEVICE_TYPE_CLUSTER_ID = DF.DEVICE_TYPE_CLUSTER_REF + JOIN + FEATURE F + ON + DF.FEATURE_REF = F.FEATURE_ID + JOIN + ENDPOINT_TYPE_CLUSTER ETC + ON + DC.CLUSTER_REF = ETC.CLUSTER_REF JOIN - FEATURE f + ENDPOINT_TYPE_ATTRIBUTE ETA ON - df.FEATURE_REF = f.FEATURE_ID + ETC.ENDPOINT_TYPE_CLUSTER_ID = ETA.ENDPOINT_TYPE_CLUSTER_REF + JOIN + ATTRIBUTE A + ON + ETA.ATTRIBUTE_REF = A.ATTRIBUTE_ID WHERE - d.DEVICE_TYPE_ID IN (${deviceTypeRefsSql}) + D.DEVICE_TYPE_ID IN (${deviceTypeRefsSql}) + AND + ETC.ENDPOINT_TYPE_REF = ? + AND + A.NAME = 'FeatureMap' + AND + A.CODE = 65532 + AND + ( + (DC.INCLUDE_SERVER = 1 AND ETC.SIDE = 'server') + OR + (DC.INCLUDE_CLIENT = 1 AND ETC.SIDE = 'client') + ) ORDER BY - d.DEVICE_TYPE_ID, - dc.CLUSTER_REF, - f.FEATURE_ID + D.DEVICE_TYPE_ID, + DC.CLUSTER_REF, + F.FEATURE_ID `, - deviceTypeRefs + arg ) return features.map(dbMapping.map.deviceTypeFeature) } +/** + * Evaluate the value of a boolean conformance expression that includes terms and operators. + * A term can be an attribute, command, event, feature, or conformance abbreviation. + * Operators include AND (&), OR (|), and NOT (!). + * The '[]' indicates optional conformance if the expression inside true. + * Expression containing comma means otherwise conformance. See spec for details. + * Examples of conformance expression: 'A & (!B | C)', 'A & B, [!C]' + * + * @export + * @param {*} expression + * @param {*} elementMap + * @returns 'mandatory', 'optional', 'provisional', or 'notSupported' + */ +function evaluateConformanceExpression(expression, elementMap) { + /** + * helper function to evaluate a single boolean expression + * @param {*} expr + */ + function evaluateBooleanExpression(expr) { + // Replace terms with their actual values from elementMap + expr = expr.replace(/[A-Za-z][A-Za-z0-9]*/g, (term) => { + if (elementMap[term]) { + return 'true' + } else { + return 'false' + } + }) + + // Evaluate NOT (!) operators + expr = expr.replace(/!true/g, 'false').replace(/!false/g, 'true') + + // Evaluate AND (&) and OR (|) operators by eval() function + return eval(expr) + } + + /** + * helper function to process parentheses and evaluate inner expressions first + * @param {*} expr + */ + function evaluateWithParentheses(expr) { + while (expr.includes('(')) { + expr = expr.replace(/\([^()]+\)/g, (terms) => + evaluateBooleanExpression(terms.slice(1, -1)) + ) + } + return evaluateBooleanExpression(expr) + } + + // Check ',' for otherwise conformance first. + // Split the expression by ',' and evaluate each part in sequence + let parts = expression.split(',') + // if any term is desc, the conformance is too complex to parse + for (let part of parts) { + let terms = part.match(/[A-Za-z][A-Za-z0-9]*/g) + if (terms && terms.includes('desc')) { + return 'desc' + } + } + for (let part of parts) { + if (part.includes('[') && part.includes(']')) { + // Extract and evaluate the content inside '[]' + let optionalExpr = part.match(/\[(.*?)\]/)[1] + let optionalResult = evaluateWithParentheses(optionalExpr) + if (optionalResult) { + return 'optional' + } else { + return 'notSupported' + } + } else { + part = part.trim() + if (part == 'M') { + return 'mandatory' + } else if (part == 'O') { + return 'optional' + } else if (part == 'D' || part == 'X') { + return 'notSupported' + } else if (part == 'P') { + return 'provisional' + } else { + // Evaluate the part with parentheses if needed + let result = evaluateWithParentheses(part) + if (result) return 'mandatory' + // if the mandatory part is false, go to the next part + } + } + } + + // If none of the parts are true and no optional part was valid, return 'notSupported' + return 'notSupported' +} + +/** + * Check if any terms in the expression are neither a key in the elementMap nor an abbreviation. + * If so, it means the conformance depends on terms with unknown values and changes are not allowed. + * + * @param {*} expression + * @param {*} elementMap + * @returns all missing terms in an array + */ +function checkMissingTerms(expression, elementMap) { + let terms = expression.match(/[A-Za-z][A-Za-z0-9]*/g) + let missingTerms = [] + let abbreviations = ['M', 'O', 'P', 'D', 'X'] + for (let term of terms) { + if (!(term in elementMap) && !abbreviations.includes(term)) { + missingTerms.push(term) + } + } + return missingTerms +} + +/** + * Filter an array of elements by if any element has conformance containing the term 'desc'. + * + * @export + * @param {*} elements + * @returns elements with conformance containing 'desc' + */ +function filterElementsContainingDesc(elements) { + return elements.filter((element) => { + let terms = element.conformance.match(/[A-Za-z][A-Za-z0-9]*/g) + return terms && terms.includes('desc') + }) +} + +/** + * + * @export + * @param {*} elements + * @param {*} featureCode + * @returns elements with conformance containing 'desc' and the feature code + */ +function filterRelatedDescElements(elements, featureCode) { + return elements.filter((element) => { + let terms = element.conformance.match(/[A-Za-z][A-Za-z0-9]*/g) + return terms && terms.includes('desc') && terms.includes(featureCode) + }) +} + +/** + * Generate a warning message after processing conformance of the updated device type feature. + * Set flags to decide whether to show a popup warning or disable changes in the frontend. + * + * @param {*} featureData + * @param {*} endpointId + * @param {*} missingTerms + * @param {*} featureMap + * @param {*} descElements + * @returns warning message array, disableChange flag, and displayWarning flag + */ +function generateWarningMessage( + featureData, + endpointId, + missingTerms, + featureMap, + descElements +) { + let featureName = featureData.name + let added = featureMap[featureData.code] ? true : false + let deviceTypeNames = featureData.deviceTypes.join(', ') + let result = { + warningMessage: '', + disableChange: true, + displayWarning: true + } + result.warningMessage = [] + + if (missingTerms.length > 0) { + let missingTermsString = missingTerms.join(', ') + result.warningMessage.push( + 'On Endpoint ' + + endpointId + + ', feature ' + + featureName + + ' cannot be enabled as its conformance depends on non device type features ' + + missingTermsString + + ' with unknown values' + ) + } + + if ( + (descElements.attributes && descElements.attributes.length > 0) || + (descElements.commands && descElements.commands.length > 0) || + (descElements.events && descElements.events.length > 0) + ) { + let attributeNames = descElements.attributes + .map((attr) => attr.name) + .join(', ') + let commandNames = descElements.commands + .map((command) => command.name) + .join(', ') + let eventNames = descElements.events.map((event) => event.name).join(', ') + result.warningMessage.push( + 'On endpoint ' + + endpointId + + ', feature ' + + featureName + + ' cannot be enabled as ' + + (attributeNames ? 'attribute ' + attributeNames : '') + + (attributeNames && commandNames ? ', ' : '') + + (commandNames ? 'command ' + commandNames : '') + + ((attributeNames || commandNames) && eventNames ? ', ' : '') + + (eventNames ? 'event ' + eventNames : '') + + ' depend on the feature and their conformance are too complex to parse.' + ) + } + + if ( + missingTerms.length == 0 && + descElements.attributes.length == 0 && + descElements.commands.length == 0 && + descElements.events.length == 0 + ) { + let conformance = evaluateConformanceExpression( + featureData.conformance, + featureMap + ) + // change is not disabled, by default does not display warning + result.disableChange = false + result.displayWarning = false + // in this case only 1 warning message is needed + result.warningMessage = '' + if (conformance == 'notSupported') { + result.warningMessage = + 'On endpoint ' + + endpointId + + ', feature ' + + featureName + + ' is enabled, but it is not supported for device type ' + + deviceTypeNames + result.displayWarning = added + } + if (conformance == 'provisional') { + result.warningMessage = + 'On endpoint ' + + endpointId + + ', feature ' + + featureName + + ' is enabled, but it is still provisional for device type ' + + deviceTypeNames + result.displayWarning = added + } + if (conformance == 'mandatory') { + result.warningMessage = + 'On endpoint ' + + endpointId + + ', feature ' + + featureName + + ' is disabled, but it is mandatory for device type ' + + deviceTypeNames + result.displayWarning = !added + } + } + + return result +} + +/** + * Check if elements need to be updated for correct conformance if featureData provided. + * Otherwise, check if elements are required or unsupported by their conformance. + * + * @export + * @param {*} elements + * @param {*} featureMap + * @param {*} featureData + * @param {*} endpointId + * @returns attributes, commands, and events to update, with warnings if featureData provided; + * required and unsupported attributes, commands, and events, with warnings if not. + */ +function checkElementConformance( + elements, + featureMap, + featureData = null, + endpointId = null +) { + let { attributes, commands, events } = elements + let featureCode = featureData ? featureData.code : '' + + // create a map of element names/codes to their enabled status + let elementMap = featureMap + attributes.forEach((attribute) => { + elementMap[attribute.name] = attribute.included + }) + commands.forEach((command) => { + elementMap[command.name] = command.isEnabled + }) + events.forEach((event) => { + elementMap[event.name] = event.included + }) + elementMap['Matter'] = 1 + elementMap['Zigbee'] = 0 + + let warningInfo = {} + if (featureData != null) { + let descElements = {} + descElements.attributes = filterRelatedDescElements(attributes, featureCode) + descElements.commands = filterRelatedDescElements(commands, featureCode) + descElements.events = filterRelatedDescElements(events, featureCode) + + let missingTerms = checkMissingTerms(featureData.conformance, elementMap) + warningInfo = generateWarningMessage( + featureData, + endpointId, + missingTerms, + featureMap, + descElements + ) + + if (warningInfo.disableChange) { + return { + ...warningInfo, + attributesToUpdate: [], + commandsToUpdate: [], + eventsToUpdate: [] + } + } + } + + // check element conformance for if they need update or are required + let attributesToUpdate = featureData + ? filterElementsToUpdate(attributes, elementMap, featureCode) + : filterRequiredElements(attributes, elementMap, featureMap) + let commandsToUpdate = featureData + ? filterElementsToUpdate(commands, elementMap, featureCode) + : filterRequiredElements(commands, elementMap, featureMap) + let eventsToUpdate = featureData + ? filterElementsToUpdate(events, elementMap, featureCode) + : filterRequiredElements(events, elementMap, featureMap) + + let result = { + attributesToUpdate: attributesToUpdate, + commandsToUpdate: commandsToUpdate, + eventsToUpdate: eventsToUpdate, + elementMap: elementMap + } + return featureData ? { ...warningInfo, ...result } : result +} + +/** + * Return attributes, commands, or events to be updated satisfying: + * (1) its conformance includes feature code of the updated feature + * (2) it has mandatory conformance but it is not enabled, OR, + * it is has notSupported conformance but it is enabled + * + * @param {*} elements + * @param {*} elementMap + * @param {*} featureCode + * @returns elements that should be updated + */ +function filterElementsToUpdate(elements, elementMap, featureCode) { + let elementsToUpdate = [] + elements + .filter((element) => element.conformance.includes(featureCode)) + .forEach((element) => { + let conformance = evaluateConformanceExpression( + element.conformance, + elementMap + ) + if ( + conformance == 'mandatory' && + (!elementMap[element.name] || elementMap[element.name] == 0) + ) { + element.value = true + elementsToUpdate.push(element) + } + if (conformance == 'notSupported' && elementMap[element.name]) { + element.value = false + elementsToUpdate.push(element) + } + }) + return elementsToUpdate +} + +/** + * Get warnings for element requirements that are outdated after a feature update. + * + * @param {*} featureData + * @param {*} elements + * @param {*} elementMap + * @returns array of outdated element warnings + */ +function getOutdatedElementWarning(featureData, elements, elementMap) { + let outdatedWarnings = [] + + /** + * Build substrings of outdated warnings and add to returned array if: + * (1) the element conformance includes the feature code + * (2) the element conformance has changed after the feature update + * + * @param {*} elementType + */ + function processElements(elementType) { + elements[elementType].forEach((element) => { + if (element.conformance.includes(featureData.code)) { + let newConform = evaluateConformanceExpression( + element.conformance, + elementMap + ) + let oldMap = { ...elementMap } + oldMap[featureData.code] = !oldMap[featureData.code] + let oldConform = evaluateConformanceExpression( + element.conformance, + oldMap + ) + if (newConform != oldConform) { + let pattern = `${element.name} conforms to ${element.conformance} and is` + outdatedWarnings.push(pattern) + } + } + }) + } + + processElements('attributes') + processElements('commands') + processElements('events') + + return outdatedWarnings +} + +/** + * Filter required and unsupported elements based on their conformance and generate warnings. + * An element is required if it conforms to element(s) in elementMap and has 'mandatory' conform. + * An element is unsupported if it conforms to element(s) in elementMap and has 'notSupported' conform. + * + * @param {*} elements + * @param {*} elementMap + * @param {*} featureMap + * @returns required and not supported elements with warnings + */ +function filterRequiredElements(elements, elementMap, featureMap) { + let requiredElements = { + required: {}, + notSupported: {} + } + elements.forEach((element) => { + let conformance = evaluateConformanceExpression( + element.conformance, + elementMap + ) + let expression = element.conformance + let terms = expression ? expression.match(/[A-Za-z][A-Za-z0-9]*/g) : [] + let featureTerms = terms.filter((term) => term in featureMap).join(', ') + let elementTerms = terms.filter((term) => !(term in featureMap)).join(', ') + let conformToElement = terms.some((term) => + Object.keys(elementMap).includes(term) + ) + + if (conformToElement) { + let conformState = '' + if (conformance == 'mandatory') { + conformState = 'mandatory' + } + if (conformance == 'notSupported') { + conformState = 'not supported' + } + + // generate warning message for required and unsupported elements + element.warningMessage = + `${element.name} conforms to ${element.conformance} and is ` + + `${conformState}` + + (featureTerms ? ` based on state of feature: ${featureTerms}` : '') + + (featureTerms && elementTerms ? ', ' : '') + + (elementTerms ? `element: ${elementTerms}` : '') + + '.' + if (conformance == 'mandatory') { + requiredElements.required[element.id] = element.warningMessage + } + if (conformance == 'notSupported') { + requiredElements.notSupported[element.id] = element.warningMessage + } + } + }) + return requiredElements +} + +/** + * Check if any non-empty conformance data exist in ATTRIBUTE, COMMAND, + * and DEVICE_TYPE_FEATURE table. + * + * @export + * @param {*} db + * @returns boolean value indicating if conformance data exists + */ +async function checkIfConformanceDataExist(db) { + try { + let deviceTypeFeatureRows = await dbApi.dbAll( + db, + 'SELECT DEVICE_TYPE_CLUSTER_CONFORMANCE FROM DEVICE_TYPE_FEATURE' + ) + let hasFeatureConformanceData = deviceTypeFeatureRows.some((row) => { + return ( + row.DEVICE_TYPE_CLUSTER_CONFORMANCE && + row.DEVICE_TYPE_CLUSTER_CONFORMANCE.trim() != '' + ) + }) + let attributeRows = await dbApi.dbAll( + db, + 'SELECT CONFORMANCE FROM ATTRIBUTE' + ) + let commandRows = await dbApi.dbAll(db, 'SELECT CONFORMANCE FROM COMMAND') + let hasConformanceData = (rows) => + rows.some((row) => row.CONFORMANCE && row.CONFORMANCE.trim() != '') + return ( + hasConformanceData(attributeRows) && + hasConformanceData(commandRows) && + hasFeatureConformanceData + ) + } catch (err) { + return false + } +} + exports.getFeaturesByDeviceTypeRefs = getFeaturesByDeviceTypeRefs +exports.checkElementConformance = checkElementConformance +exports.evaluateConformanceExpression = evaluateConformanceExpression +exports.filterElementsContainingDesc = filterElementsContainingDesc +exports.filterRelatedDescElements = filterRelatedDescElements +exports.checkIfConformanceDataExist = checkIfConformanceDataExist +exports.getOutdatedElementWarning = getOutdatedElementWarning diff --git a/src-electron/db/query-loader.js b/src-electron/db/query-loader.js index 95f1bbe127..47e05882f9 100644 --- a/src-electron/db/query-loader.js +++ b/src-electron/db/query-loader.js @@ -59,13 +59,14 @@ INSERT INTO EVENT ( NAME, DESCRIPTION, SIDE, + CONFORMANCE, IS_OPTIONAL, IS_FABRIC_SENSITIVE, PRIORITY, INTRODUCED_IN_REF, REMOVED_IN_REF ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT SPEC_ID FROM SPEC WHERE CODE = ? AND PACKAGE_REF = ?), (SELECT SPEC_ID FROM SPEC WHERE CODE = ? AND PACKAGE_REF = ?) ) @@ -96,6 +97,7 @@ INSERT INTO COMMAND ( DESCRIPTION, SOURCE, IS_OPTIONAL, + CONFORMANCE, MUST_USE_TIMED_INVOKE, IS_FABRIC_SCOPED, RESPONSE_NAME, @@ -105,7 +107,7 @@ INSERT INTO COMMAND ( IS_DEFAULT_RESPONSE_ENABLED, IS_LARGE_MESSAGE ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT SPEC_ID FROM SPEC WHERE CODE = ? AND PACKAGE_REF = ?), (SELECT SPEC_ID FROM SPEC WHERE CODE = ? AND PACKAGE_REF = ?), ?, ? @@ -146,6 +148,7 @@ INSERT INTO ATTRIBUTE ( TYPE, SIDE, DEFINE, + CONFORMANCE, MIN, MAX, MIN_LENGTH, @@ -170,7 +173,7 @@ INSERT INTO ATTRIBUTE ( IS_CHANGE_OMITTED, PERSISTENCE ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT SPEC_ID FROM SPEC WHERE CODE = ? AND PACKAGE_REF = ?), (SELECT SPEC_ID FROM SPEC WHERE CODE = ? AND PACKAGE_REF = ?), ?, @@ -222,6 +225,7 @@ function attributeMap(clusterId, packageId, attributes) { attribute.type, attribute.side, attribute.define, + attribute.conformance, attribute.min, attribute.max, attribute.minLength, @@ -267,6 +271,7 @@ function eventMap(clusterId, packageId, events) { event.name, event.description, event.side, + event.conformance, dbApi.toDbBool(event.isOptional), dbApi.toDbBool(event.isFabricSensitive), event.priority, @@ -294,6 +299,7 @@ function commandMap(clusterId, packageId, commands) { command.description, command.source, dbApi.toDbBool(command.isOptional), + command.conformance, dbApi.toDbBool(command.mustUseTimedInvoke), dbApi.toDbBool(command.isFabricScoped), command.responseName, diff --git a/src-electron/db/query-session-notification.js b/src-electron/db/query-session-notification.js index 1b0bec4d86..159d9f96a2 100644 --- a/src-electron/db/query-session-notification.js +++ b/src-electron/db/query-session-notification.js @@ -83,13 +83,11 @@ async function setNotification( } /** - * Deletes a notification from the SESSION_NOTICE table + * Deletes a notification from the SESSION_NOTICE table by NOTICE_ID * * @export * @param {*} db - * @param {*} sessionId - * @param {*} type - * @param {*} message + * @param {*} id */ async function deleteNotification(db, id) { return dbApi.dbUpdate( @@ -133,11 +131,11 @@ async function getUnseenNotificationCount(db, sessionId) { } /** - * update SEEN column to 1 for all notifications from the SESSION_NOTICE table with NOTIC_ORDER inside given input array unseenIds + * update SEEN column to 1 for all notifications from the SESSION_NOTICE table with NOTICE_ID inside given input array unseenIds * * @export * @param {*} db - * @param {*} sessionId + * @param {*} unseenIds */ async function markNotificationsAsSeen(db, unseenIds) { if (unseenIds && unseenIds.length > 0) { @@ -150,10 +148,158 @@ async function markNotificationsAsSeen(db, unseenIds) { } } +/** + * search for notifications with given message and delete them if found + * + * @export + * @param {*} db + * @param {*} sessionId + * @param {*} message + * @returns db delete promise if notification(s) were found and deleted, false otherwise + */ +async function searchNotificationByMessageAndDelete(db, sessionId, message) { + let rows = await dbApi.dbAll( + db, + 'SELECT NOTICE_ID FROM SESSION_NOTICE WHERE SESSION_REF = ? AND NOTICE_MESSAGE = ?', + [sessionId, message] + ) + if (rows && rows.length > 0) { + let ids = rows.map((row) => row.NOTICE_ID) + let deleteResponses = [] + for (let id of ids) { + let response = await deleteNotification(db, id) + deleteResponses.push(response) + } + return deleteResponses + } + return false +} + +/** + * check if notification with given message exists and if not, create a new one with type WARNING + * + * @export + * @param {*} db + * @param {*} sessionId + * @param {*} message + * @returns setNotification response if message does not exist and notification was set, false otherwise + */ +async function setWarningIfMessageNotExists(db, sessionId, message) { + let rows = await dbApi.dbAll( + db, + 'SELECT NOTICE_ID FROM SESSION_NOTICE WHERE SESSION_REF = ? AND NOTICE_MESSAGE = ?', + [sessionId, message] + ) + if (rows && rows.length == 0) { + return setNotification(db, 'WARNING', message, sessionId, 2, 0) + } + return false +} + +/** + * Set or delete warning notification after updating a device type feature. + * + * @export + * @param {*} db + * @param {*} sessionId + * @param {*} result + */ +async function setNotificationOnFeatureChange(db, sessionId, result) { + let { warningMessage, disableChange, displayWarning } = result + if (disableChange) { + for (let message of warningMessage) { + await setWarningIfMessageNotExists(db, sessionId, message) + } + return + } + if (displayWarning) { + await setNotification(db, 'WARNING', warningMessage, sessionId, 2, 0) + } else { + await searchNotificationByMessageAndDelete(db, sessionId, warningMessage) + } +} + +/** + * Set warning for the required element. Delete previous warning for the element if exists. + * + * @export + * @param {*} db + * @param {*} data + * @param {*} sessionId + * @returns response of setting warning notification + */ +async function setRequiredElementWarning(db, data, sessionId) { + let { element, contextMessage, requiredText, notSupportedText, added } = data + + // delete previous warning before setting new one + let patterns = [`${element.name} conforms to ${element.conformance} and is`] + await deleteNotificationWithPatterns(db, sessionId, patterns) + + let addResp = false + if (requiredText && !added) { + addResp = await setWarningIfMessageNotExists( + db, + sessionId, + contextMessage + requiredText + ) + } + if (notSupportedText && added) { + addResp = await setWarningIfMessageNotExists( + db, + sessionId, + contextMessage + notSupportedText + ) + } + return addResp +} + +/** + * Delete notifications with message containing substrings specified in the patterns array. + * + * @param {*} db + * @param {*} sessionId + * @param {*} patterns + * @returns response of deleting notifications + */ +async function deleteNotificationWithPatterns(db, sessionId, patterns) { + if (!Array.isArray(patterns) || patterns.length == 0) return false + + let placeholders = patterns.map(() => '?').join(' OR NOTICE_MESSAGE LIKE ') + let query = ` + SELECT + NOTICE_ID + FROM + SESSION_NOTICE + WHERE + SESSION_REF = ? + AND ( + NOTICE_MESSAGE LIKE ${placeholders} + ) + ` + let params = [sessionId, ...patterns.map((pattern) => `%${pattern}%`)] + + let rows = await dbApi.dbAll(db, query, params) + if (rows && rows.length > 0) { + let ids = rows.map((row) => row.NOTICE_ID) + let deleteResponses = [] + for (let id of ids) { + let response = await deleteNotification(db, id) + deleteResponses.push(response) + } + return deleteResponses + } + return false +} + // exports exports.setNotification = setNotification exports.deleteNotification = deleteNotification exports.getNotification = getNotification exports.getUnseenNotificationCount = getUnseenNotificationCount exports.markNotificationsAsSeen = markNotificationsAsSeen -//# sourceMappingURL=query-session-notification.js.map +exports.searchNotificationByMessageAndDelete = + searchNotificationByMessageAndDelete +exports.setWarningIfMessageNotExists = setWarningIfMessageNotExists +exports.setNotificationOnFeatureChange = setNotificationOnFeatureChange +exports.setRequiredElementWarning = setRequiredElementWarning +exports.deleteNotificationWithPatterns = deleteNotificationWithPatterns diff --git a/src-electron/db/query-zcl.js b/src-electron/db/query-zcl.js index ce134d51d9..567e371e46 100644 --- a/src-electron/db/query-zcl.js +++ b/src-electron/db/query-zcl.js @@ -694,6 +694,7 @@ SELECT TYPE, SIDE, DEFINE, + CONFORMANCE, MIN, MAX, MIN_LENGTH, @@ -988,6 +989,7 @@ SELECT A.TYPE, A.SIDE, A.DEFINE, + A.CONFORMANCE, A.MIN, A.MAX, A.MIN_LENGTH, diff --git a/src-electron/db/zap-schema.sql b/src-electron/db/zap-schema.sql index a87f3c888f..870783920a 100644 --- a/src-electron/db/zap-schema.sql +++ b/src-electron/db/zap-schema.sql @@ -176,6 +176,7 @@ CREATE TABLE IF NOT EXISTS "COMMAND" ( "DESCRIPTION" text, "SOURCE" text, "IS_OPTIONAL" integer, + "CONFORMANCE" text, "MUST_USE_TIMED_INVOKE" integer, "IS_FABRIC_SCOPED" integer, "INTRODUCED_IN_REF" integer, @@ -230,6 +231,7 @@ CREATE TABLE IF NOT EXISTS "EVENT" ( "NAME" text, "DESCRIPTION" text, "SIDE" text, + "CONFORMANCE" text, "IS_OPTIONAL" integer, "IS_FABRIC_SENSITIVE" integer, "PRIORITY" text, @@ -275,6 +277,7 @@ CREATE TABLE IF NOT EXISTS "ATTRIBUTE" ( "TYPE" text, "SIDE" text, "DEFINE" text, + "CONFORMANCE" text, "MIN" text, "MAX" text, "MIN_LENGTH" integer, diff --git a/src-electron/rest/user-data.js b/src-electron/rest/user-data.js index cc154529f7..6fa1174ed3 100644 --- a/src-electron/rest/user-data.js +++ b/src-electron/rest/user-data.js @@ -84,12 +84,123 @@ function httpGetEndpointIds(db) { */ function httpGetDeviceTypeFeatures(db) { return async (request, response) => { - let deviceTypeRefs = request.query.deviceTypeRefs - let deviceTypeFeatures = await queryFeature.getFeaturesByDeviceTypeRefs( + let { deviceTypeRefs, endpointTypeRef } = request.query + if (Array.isArray(deviceTypeRefs) && deviceTypeRefs.length > 0) { + let deviceTypeFeatures = await queryFeature.getFeaturesByDeviceTypeRefs( + db, + deviceTypeRefs, + endpointTypeRef + ) + response.status(StatusCodes.OK).json(deviceTypeFeatures) + } else { + response.status(StatusCodes.OK).json([]) + } + } +} + +/** + * Get all attributes, commands and events in an endpoint type cluster. + * @param {*} db + * @param {*} endpointTypeClusterId + * @param {*} deviceTypeClusterId + * @returns elements object containing all attributes, commands and events + * in an endpoint type cluster + */ +async function getEndpointTypeElements( + db, + endpointTypeClusterId, + deviceTypeClusterId +) { + let [attributes, commands, events] = await Promise.all([ + queryAttribute.selectAttributesByEndpointTypeClusterId( + db, + endpointTypeClusterId + ), + queryCommand.selectCommandsByEndpointTypeClusterIdAndDeviceTypeClusterId( db, - deviceTypeRefs + endpointTypeClusterId, + deviceTypeClusterId + ), + queryEvent.selectEventsByEndpointTypeClusterIdAndDeviceTypeClusterId( + db, + endpointTypeClusterId, + deviceTypeClusterId ) - response.status(StatusCodes.OK).json(deviceTypeFeatures) + ]) + return { attributes, commands, events } +} + +/** + * HTTP POST: elements to be updated after toggle a device type feature + * + * @param {*} db + * @returns callback for the express uri registration + */ +function httpPostCheckConformOnFeatureUpdate(db) { + return async (request, response) => { + let sessionId = request.zapSessionId + let { featureData, featureMap, endpointId } = request.body + let { endpointTypeClusterId, deviceTypeClusterId } = featureData + + let elements = await getEndpointTypeElements( + db, + endpointTypeClusterId, + deviceTypeClusterId + ) + // check element conform and return elements that need to be updated + let result = queryFeature.checkElementConformance( + elements, + featureMap, + featureData, + endpointId + ) + + // set device type feature warning + await querySessionNotification.setNotificationOnFeatureChange( + db, + sessionId, + result + ) + // do not set element warning if feature change disabled + if (!result.disableChange) { + let outdatedWarnings = queryFeature.getOutdatedElementWarning( + featureData, + elements, + result.elementMap + ) + await querySessionNotification.deleteNotificationWithPatterns( + db, + sessionId, + outdatedWarnings + ) + } + + response.status(StatusCodes.OK).json(result) + } +} + +/** + * HTTP GET: required and unsupported cluster elements based on conformance + * @param {*} db + * @returns callback for the express uri registration + */ +function httpGetRequiredElements(db) { + return async (request, response) => { + let { featureMap, deviceTypeClusterId, endpointTypeClusterId } = JSON.parse( + request.query.data + ) + featureMap = JSON.parse(featureMap) + let endpointTypeElements = await getEndpointTypeElements( + db, + endpointTypeClusterId, + deviceTypeClusterId + ) + let result = queryFeature.checkElementConformance( + endpointTypeElements, + featureMap + ) + + response.status(StatusCodes.OK).json(result) } } @@ -1008,6 +1119,58 @@ function httpPostDuplicateEndpointType(db) { } } +/** + * Update feature map attribute with given new value + * + * @param {*} db + * @returns status of the update + */ +function httpPatchUpdateBitOfFeatureMapAttribute(db) { + return async (request, response) => { + let { featureMapAttributeId, newValue } = request.body + let updated = await queryConfig.updateEndpointTypeAttribute( + db, + featureMapAttributeId, + [['defaultValue', newValue]] + ) + response.status(StatusCodes.OK).json({ + successful: updated > 0 + }) + } +} + +/** + * Check if conformance data exists in the database + * + * @param {*} db + * @returns boolean value of data exist or not + */ +function httpGetConformDataExists(db) { + return async (request, response) => { + let conformDataExists = await queryFeature.checkIfConformanceDataExist(db) + response.status(StatusCodes.OK).json(conformDataExists) + } +} + +/** + * Set warning for the required element, and delete its existing warning if any. + * + * @param {*} db + * @returns response of setting the warning notification + */ +function httpPostRequiredElementWarning(db) { + return async (request, response) => { + let { element, contextMessage, requiredText, notSupportedText, added } = + request.body + let resp = await querySessionNotification.setRequiredElementWarning( + db, + { element, contextMessage, requiredText, notSupportedText, added }, + request.zapSessionId + ) + response.status(StatusCodes.OK).json(resp) + } +} + /** * duplicate all clusters and attributes of an old endpoint type, using oldEndpointType id and newly created endpointType id * @@ -1115,6 +1278,14 @@ exports.post = [ { uri: restApi.uri.duplicateEndpointType, callback: httpPostDuplicateEndpointType + }, + { + uri: restApi.uri.checkConformOnFeatureUpdate, + callback: httpPostCheckConformOnFeatureUpdate + }, + { + uri: restApi.uri.requiredElementWarning, + callback: httpPostRequiredElementWarning } ] @@ -1170,6 +1341,14 @@ exports.get = [ { uri: restApi.uri.getAllPackages, callback: httpGetAllPackages + }, + { + uri: restApi.uri.conformDataExists, + callback: httpGetConformDataExists + }, + { + uri: restApi.uri.requiredElements, + callback: httpGetRequiredElements } ] @@ -1187,3 +1366,10 @@ exports.delete = [ callback: httpDeletePackageNotification } ] + +exports.patch = [ + { + uri: restApi.uri.updateBitOfFeatureMapAttribute, + callback: httpPatchUpdateBitOfFeatureMapAttribute + } +] diff --git a/src-electron/zcl/zcl-loader-silabs.js b/src-electron/zcl/zcl-loader-silabs.js index 2fe6022982..518ab91c68 100644 --- a/src-electron/zcl/zcl-loader-silabs.js +++ b/src-electron/zcl/zcl-loader-silabs.js @@ -492,6 +492,7 @@ function prepareCluster(cluster, context, isExtension = false) { description: command.description ? command.description[0].trim() : '', source: command.$.source, isOptional: command.$.optional == 'true' ? true : false, + conformance: parseConformanceFromXML(command), mustUseTimedInvoke: command.$.mustUseTimedInvoke == 'true', introducedIn: command.$.introducedIn, removedIn: command.$.removedIn, @@ -547,6 +548,7 @@ function prepareCluster(cluster, context, isExtension = false) { manufacturerCode: event.$.manufacturerCode, name: event.$.name, side: event.$.side, + conformance: parseConformanceFromXML(event), priority: event.$.priority, description: event.description ? event.description[0].trim() : '', isOptional: event.$.optional == 'true', @@ -647,6 +649,7 @@ function prepareCluster(cluster, context, isExtension = false) { : attribute.$.type, side: attribute.$.side, define: attribute.$.define, + conformance: parseConformanceFromXML(attribute), min: attribute.$.min, max: attribute.$.max, minLength: 0, @@ -734,7 +737,7 @@ function prepareCluster(cluster, context, isExtension = false) { bit: feature.$.bit, defaultValue: feature.$.default, description: feature.$.summary, - conformance: parseFeatureConformance(feature) + conformance: parseConformanceFromXML(feature) } ret.features.push(f) @@ -1686,10 +1689,9 @@ function prepareDeviceType(deviceType) { } if ('features' in include) { include.features[0].feature.forEach((f) => { - let conformance = parseFeatureConformance(f) features.push({ code: f.$.code, - conformance: conformance + conformance: parseConformanceFromXML(f) }) }) } @@ -2252,7 +2254,11 @@ async function parseFeatureFlags(db, packageId, featureFlags) { } /** - * Parses feature conformance or an operand in feature conformance recursively from xml data. + * Parses conformance from XML data. + * The conformance could come from features, attributes, commands, or events + * + * Call recursive helper function to parse conformance only if the conformance exists. + * Otherwise, return empty string directly * * An example of parsing the conformance of 'User' device type feature: * @@ -2267,7 +2273,7 @@ async function parseFeatureFlags(db, packageId, featureFlags) { * { "feature": [ * { "$": {"name": "PIN"}}, * { "$": {"name": "RID"}}, - * { "$": {"name": "FPG"}}, + * { "$": {"name": "FGP"}}, * { "$": {"name": "FACE"}} * ] * } @@ -2278,18 +2284,41 @@ async function parseFeatureFlags(db, packageId, featureFlags) { * ] * } * - * Output device type feature conformance string: - * "Matter & (PIN | RID | FPG | FACE)" + * Output conformance string: + * "Matter & (PIN | RID | FGP | FACE)" * - * @param {*} operand - The operand to be parsed. - * @returns The feature conformance string. + * @param {*} operand + * @returns The conformance string */ -function parseFeatureConformance(operand) { +function parseConformanceFromXML(operand) { + let hasConformance = Object.keys(operand).some((key) => + key.includes('Conform') + ) + return hasConformance ? parseConformanceRecursively(operand) : '' +} + +/** + * helper function to parse conformance or an operand in conformance recursively + * + * The baseLevelTerms variable include terms that can not have nested terms. + * When they appear, stop recursing and return the name inside directly + * + * @param {*} operand + * @param {*} depth + * @param {*} parentJoinChar + * @returns The conformance string. + */ +function parseConformanceRecursively(operand, depth = 0, parentJoinChar = '') { + if (depth > 200) { + throw new Error(`Maximum recursion depth exceeded + when parsing conformance: ${JSON.stringify(operand)}`) + } + const baseLevelTerms = ['feature', 'condition', 'attribute', 'command'] if (operand.mandatoryConform) { let insideTerm = operand.mandatoryConform[0] // Recurse further if insideTerm is not empty if (insideTerm && Object.keys(insideTerm).toString() != '$') { - return parseFeatureConformance(operand.mandatoryConform[0]) + return parseConformanceRecursively(operand.mandatoryConform[0], depth + 1) } else { return 'M' } @@ -2298,65 +2327,64 @@ function parseFeatureConformance(operand) { // check '$' key is not the only key in the object to handle special cases // e.g. '' if (insideTerm && Object.keys(insideTerm).toString() != '$') { - return `[${parseFeatureConformance(operand.optionalConform[0])}]` + return `[${parseConformanceRecursively(operand.optionalConform[0], depth + 1)}]` } else { return 'O' } + } else if (operand.otherwiseConform) { + return Object.entries(operand.otherwiseConform[0]) + .map(([key, value]) => + parseConformanceRecursively({ [key]: value }, depth + 1) + ) + .join(', ') + } else if (operand.notTerm) { + // need to surround terms inside a notTerm with '()' if it contains multiple terms + // e.g. !(A | B) or !(A & B) + // able to process multiple parallel notTerms, e.g. !A & !B + return operand.notTerm + .map((term) => { + let nt = parseConformanceRecursively(term, depth + 1) + return nt.includes('&') || nt.includes('|') ? `!(${nt})` : `!${nt}` + }) + .join(` ${parentJoinChar} `) + } else if (operand.andTerm || operand.orTerm) { + // process andTerm and orTerm in the same logic + // when joining multiple orTerms inside andTerms, we need to + // surround them with '()', vice versa for andTerms inside orTerms + // e.g. A & (B | C) or A | (B & C) + let joinChar = operand.andTerm ? '&' : '|' + let termKey = operand.andTerm ? 'andTerm' : 'orTerm' + let oppositeChar = joinChar == '&' ? '|' : '&' + return Object.entries(operand[termKey][0]) + .map(([key, value]) => { + if (baseLevelTerms.includes(key)) { + return value.map((operand) => operand.$.name).join(` ${joinChar} `) + } else { + let terms = parseConformanceRecursively( + { [key]: value }, + depth + 1, + joinChar + ) + return terms.includes(oppositeChar) ? `(${terms})` : terms + } + }) + .join(` ${joinChar} `) } else if (operand.provisionalConform) { return 'P' } else if (operand.disallowConform) { return 'X' } else if (operand.deprecateConform) { return 'D' - } else if (operand.feature) { - return operand.feature[0].$.name - } else if (operand.condition) { - return operand.condition[0].$.name - } else if (operand.otherwiseConform) { - return Object.entries(operand.otherwiseConform[0]) - .map(([key, value]) => parseFeatureConformance({ [key]: value })) - .join(', ') - } else if (operand.notTerm) { - let notTerms = parseFeatureConformance(operand.notTerm[0]) - // need to surround notTerms with '()' if it contains multiple terms - // e.g. !(A | B) or !(A & B) - return notTerms.includes('&') || notTerms.includes('|') - ? `!(${notTerms})` - : `!${notTerms}` - } else if (operand.andTerm) { - return parseAndOrConformanceTerms(operand.andTerm, '&') - } else if (operand.orTerm) { - return parseAndOrConformanceTerms(operand.orTerm, '|') } else { - return '' - } -} - -/** - * Helper function to parse andTerm or orTerm from xml data - * @param {*} operand - * @param {*} joinChar - * @returns feature conformance string - */ -function parseAndOrConformanceTerms(operand, joinChar) { - // when joining multiple orTerms inside andTerms, we need to - // surround them with '()', vice versa for andTerms inside orTerms - // e.g. A & (B | C) or A | (B & C) - let oppositeChar = joinChar === '&' ? '|' : '&' - let oppositeTerm = joinChar === '&' ? 'orTerm' : 'andTerm' - - return Object.entries(operand[0]) - .map(([key, value]) => { - if (key == 'feature' || key == 'condition') { - return value.map((operand) => operand.$.name).join(` ${joinChar} `) - } else if (key == oppositeTerm) { - let terms = parseFeatureConformance({ [key]: value }) - return terms.includes(oppositeChar) ? `(${terms})` : terms - } else { - return '' + // reach base level terms, return the name directly + for (const term of baseLevelTerms) { + if (operand[term]) { + return operand[term][0].$.name } - }) - .join(` ${joinChar} `) + } + // reaching here means the term is too complex to parse + return 'desc' + } } /** diff --git a/src-shared/db-enum.js b/src-shared/db-enum.js index 4e9a9ccc53..eb70e43f79 100644 --- a/src-shared/db-enum.js +++ b/src-shared/db-enum.js @@ -220,6 +220,7 @@ exports.packageMatch = { exports.deviceTypeFeature = { name: { + enabled: 'enabled', deviceType: 'deviceType', cluster: 'cluster', clusterSide: 'clusterSide', @@ -230,6 +231,7 @@ exports.deviceTypeFeature = { description: 'description' }, label: { + enabled: 'Enabled', deviceType: 'Device Type', cluster: 'Cluster', clusterSide: 'Cluster Side', diff --git a/src-shared/rest-api.js b/src-shared/rest-api.js index ed76449be4..73308107d0 100644 --- a/src-shared/rest-api.js +++ b/src-shared/rest-api.js @@ -36,11 +36,16 @@ const uri = { packageNotification: '/packageNotification', packageNotificationById: '/packageNotificationById/:packageId', updateNotificationToSeen: '/updateNotificationToSeen', + updateBitOfFeatureMapAttribute: '/updateBitOfFeatureMapAttribute', + conformDataExists: '/conformDataExists', + requiredElementWarning: '/requiredElementWarning', deleteSessionNotification: '/deleteSessionNotification', deletePackageNotification: 'deletePackageNotification', duplicateEndpoint: '/duplicateEndpoint', duplicateEndpointType: '/duplicateEndpointType', dirtyFlag: '/dirtyFlag', + checkConformOnFeatureUpdate: '/checkConformOnFeatureUpdate', + requiredElements: '/requiredElements', option: '/option', uiOptions: '/uiOptions', commandUpdate: '/command/update', diff --git a/src/components/ZclAttributeManager.vue b/src/components/ZclAttributeManager.vue index d1eb518389..94d324a8aa 100644 --- a/src/components/ZclAttributeManager.vue +++ b/src/components/ZclAttributeManager.vue @@ -44,19 +44,23 @@ limitations under the License. style="width: 30px; max-width: 30px" > - This attribute is mandatory for the cluster and device type - configuration you have enabled +
+ {{ warning }} +
@@ -294,15 +298,52 @@ export default { setToNull(row, selectedClusterId) { this.handleLocalChange(null, 'defaultValue', row, selectedClusterId) }, - isAttributeRequired(attribute) { - return this.requiredAttributes.includes(attribute.id) + isAttributeRequired(attributeId) { + return this.requiredAttributes.includes(attributeId) }, - displayAttrWarning(row) { + isRequiredAttributeDisabled(attributeId) { return ( - this.isAttributeRequired(row) && - !this.selection.includes( - this.hashAttributeIdClusterId(row.id, this.selectedCluster.id) - ) + this.isAttributeRequired(attributeId) && + !this.isAttributeSelected(attributeId) + ) + }, + /* Display warnings if attributes required by device type is disabled, + or if attribute state does not match mandatory or notSupported conformance. + Two types of warnings can be displayed at the same time. */ + displayAttrWarning(attributeId) { + return ( + (this.enableFeature && + ((this.attributesRequiredByConform[attributeId] && + !this.isAttributeSelected(attributeId)) || + (this.attributesNotSupportedByConform[attributeId] && + this.isAttributeSelected(attributeId)))) || + this.isRequiredAttributeDisabled(attributeId) + ) + }, + getAttrWarning(attributeId) { + let warnings = [] + if ( + this.attributesRequiredByConform[attributeId] && + !this.isAttributeSelected(attributeId) && + this.enableFeature + ) { + warnings.push(this.attributesRequiredByConform[attributeId]) + } + if ( + this.attributesNotSupportedByConform[attributeId] && + this.isAttributeSelected(attributeId) && + this.enableFeature + ) { + warnings.push(this.attributesNotSupportedByConform[attributeId]) + } + if (this.isRequiredAttributeDisabled(attributeId)) { + warnings.push(this.defaultWarning) + } + return warnings + }, + isAttributeSelected(attributeId) { + return this.selection.includes( + this.hashAttributeIdClusterId(attributeId, this.selectedCluster.id) ) }, customAttributeSort(rows, sortBy, descending) { @@ -415,10 +456,13 @@ export default { sortBy: 'clientServer' }, columns: [], - forcedExternal: [] + forcedExternal: [], + defaultWarning: `This attribute is mandatory for the + cluster and device type configuration you have enabled` } }, mounted() { + this.setRequiredConformElement() this.columns = [ { name: 'status', diff --git a/src/components/ZclClusterManager.vue b/src/components/ZclClusterManager.vue index 2179e3bf09..7ea46d3c23 100644 --- a/src/components/ZclClusterManager.vue +++ b/src/components/ZclClusterManager.vue @@ -46,7 +46,7 @@ limitations under the License. rounded label="Device Type Features" color="secondary" - @click="updateDeviceTypeFeatures" + @click="setDeviceTypeFeatures" to="/feature" /> @@ -151,6 +151,7 @@ export default { this.scrollToElementById(this.lastSelectedDomain) } this.changeDomainFilter(this.filter) + this.$store.dispatch('zap/updateConformDataExists') }, watch: { enabledClusters() { @@ -350,13 +351,12 @@ export default { changeFilterString(filterString) { this.$store.dispatch('zap/setFilterString', filterString) }, - updateDeviceTypeFeatures() { - let deviceTypeRefs = - this.endpointDeviceTypeRef[this.selectedEndpointTypeId] - this.$store.dispatch( - 'zap/updateSelectedDeviceTypeFeatures', - deviceTypeRefs - ) + setDeviceTypeFeatures() { + let deviceTypeRefs = this.endpointDeviceTypeRef[this.selectedEndpointId] + this.$store.dispatch('zap/setDeviceTypeFeatures', { + deviceTypeRefs: deviceTypeRefs, + endpointTypeRef: this.selectedEndpointId + }) } }, components: { diff --git a/src/components/ZclCommandManager.vue b/src/components/ZclCommandManager.vue index ed996aca81..e1d783ff44 100644 --- a/src/components/ZclCommandManager.vue +++ b/src/components/ZclCommandManager.vue @@ -48,7 +48,12 @@ limitations under the License. self="bottom middle" :offset="[10, 10]" > - {{ validationErrorMessage }} +
+ {{ warning }} +
@@ -135,10 +140,12 @@ limitations under the License.