diff --git a/src-electron/db/query-command.js b/src-electron/db/query-command.js index f4cde9b2f5ebcca04b08789f208dd83a0d47c7e8..05741db5b0c6818b8831c503f868691bd7974cfd 100644 --- a/src-electron/db/query-command.js +++ b/src-electron/db/query-command.js @@ -194,10 +194,13 @@ ORDER BY } /** - * All Clusters with available incoming commands. + * All Clusters with available incoming or outgoing commands. * @param db * @param endpointTypes - * @returns All Clusters with side that have available incoming commands. + * @param uniqueClusterCodes + * @param isIncoming + * @returns All Clusters with side that have available incoming or outgoing + * commands. * uniqueClusterCodes can be used to get unique clusters based on a cluster code * and this can eliminate duplicate cluster code entries when manufacturing * specific clusters exist with the same cluster code. @@ -206,13 +209,17 @@ ORDER BY * type command is not precisely linked to the sides of the cluster as commands * do not belong to a side of a cluster like an attribute. */ -async function selectAllClustersWithIncomingCommands( +async function selectAllClustersWithIncomingOrOutgoingCommands( db, endpointTypes, - uniqueClusterCodes = false + uniqueClusterCodes, + isIncoming ) { let endpointTypeIds = endpointTypes.map((ep) => ep.endpointTypeId).toString() let sqlGroupBy = uniqueClusterCodes ? 'CLUSTER.CODE' : 'CLUSTER.NAME' + let isIncomingSql = isIncoming + ? `ENDPOINT_TYPE_COMMAND.INCOMING = 1 ` + : `ENDPOINT_TYPE_COMMAND.OUTGOING = 1 ` let mapFunction = (x) => { return { id: x.CLUSTER_ID, @@ -257,8 +264,7 @@ WHERE ENDPOINT_TYPE_COMMAND.ENDPOINT_TYPE_REF IN (${endpointTypeIds}) AND ENDPOINT_TYPE_COMMAND.ENDPOINT_TYPE_REF = ENDPOINT_TYPE_CLUSTER.ENDPOINT_TYPE_REF AND ENDPOINT_TYPE_CLUSTER.SIDE IN ("client", "server") - AND ENDPOINT_TYPE_CLUSTER.ENABLED = 1 - AND ENDPOINT_TYPE_COMMAND.INCOMING = 1 + AND ENDPOINT_TYPE_CLUSTER.ENABLED = 1 AND ${isIncomingSql} AND COMMAND.SOURCE != ENDPOINT_TYPE_CLUSTER.SIDE GROUP BY ${sqlGroupBy}, ENDPOINT_TYPE_CLUSTER.SIDE ORDER BY CLUSTER.NAME, ENDPOINT_TYPE_CLUSTER.SIDE` @@ -266,6 +272,60 @@ GROUP BY .then((rows) => rows.map(mapFunction)) } +/** + * All Clusters with available incoming commands. + * @param db + * @param endpointTypes + * @param uniqueClusterCodes + * @returns All Clusters with side that have available incoming commands. + * uniqueClusterCodes can be used to get unique clusters based on a cluster code + * and this can eliminate duplicate cluster code entries when manufacturing + * specific clusters exist with the same cluster code. + * Note: The relationship between the endpoint_type_cluster being enabled and a + * endpoint_type_command is indirect. The reason for this being the endpoint + * type command is not precisely linked to the sides of the cluster as commands + * do not belong to a side of a cluster like an attribute. + */ +async function selectAllClustersWithIncomingCommands( + db, + endpointTypes, + uniqueClusterCodes = false +) { + return selectAllClustersWithIncomingOrOutgoingCommands( + db, + endpointTypes, + uniqueClusterCodes, + true + ) +} + +/** + * All Clusters with available outgoing commands. + * @param db + * @param endpointTypes + * @param uniqueClusterCodes + * @returns All Clusters with side that have available outgoing commands. + * uniqueClusterCodes can be used to get unique clusters based on a cluster code + * and this can eliminate duplicate cluster code entries when manufacturing + * specific clusters exist with the same cluster code. + * Note: The relationship between the endpoint_type_cluster being enabled and a + * endpoint_type_command is indirect. The reason for this being the endpoint + * type command is not precisely linked to the sides of the cluster as commands + * do not belong to a side of a cluster like an attribute. + */ +async function selectAllClustersWithOutgoingCommands( + db, + endpointTypes, + uniqueClusterCodes = false +) { + return selectAllClustersWithIncomingOrOutgoingCommands( + db, + endpointTypes, + uniqueClusterCodes, + false + ) +} + /** * All Manufacturing Clusters with available incoming commands for a given * cluster code. @@ -441,21 +501,22 @@ async function selectAllIncomingCommandsForClusterCombined( } /** - * Returns all incoming per cluster commands. * - * @param {*} db - * @param {*} endpointTypes - * @param {*} clName - * @param {*} clSide - * @param {*} isMfgSpecific - * @returns promise of incoming per-cluster commands. + * @param db + * @param endpointTypes + * @param clName + * @param clSide + * @param isMfgSpecific + * @param isIncoming + * @returns Incoming or Outgoing commands for a given cluster */ -async function selectAllIncomingCommandsForCluster( +async function selectAllIncomingOrOutgoingCommandsForCluster( db, endpointTypes, clName, clSide, - isMfgSpecific + isMfgSpecific, + isIncoming ) { let endpointTypeIds = endpointTypes.map((ep) => ep.endpointTypeId).toString() let mfgSpecificString = @@ -464,6 +525,9 @@ async function selectAllIncomingCommandsForCluster( : isMfgSpecific ? ` AND COMMAND.MANUFACTURER_CODE IS NOT NULL ` : ` AND COMMAND.MANUFACTURER_CODE IS NULL ` + let isIncomingSql = isIncoming + ? `ENDPOINT_TYPE_COMMAND.INCOMING=1 ` + : `ENDPOINT_TYPE_COMMAND.OUTGOING=1 ` let mapFunction = (x) => { return { clusterId: x.CLUSTER_ID, @@ -490,50 +554,93 @@ async function selectAllIncomingCommandsForCluster( .dbAll( db, ` -SELECT - CLUSTER.CLUSTER_ID, - CLUSTER.NAME AS CLUSTER_NAME, - CLUSTER.CODE AS CLUSTER_CODE, - CLUSTER.DEFINE AS CLUSTER_DEFINE, - COMMAND.MANUFACTURER_CODE AS COMMAND_MANUFACTURER_CODE, - ENDPOINT_TYPE_CLUSTER.SIDE AS CLUSTER_SIDE, - ENDPOINT_TYPE_CLUSTER.ENABLED AS CLUSTER_ENABLED, - ENDPOINT_TYPE_CLUSTER.ENDPOINT_TYPE_CLUSTER_ID, - COUNT(*) OVER (PARTITION BY CLUSTER.NAME, COMMAND.NAME) AS NO_OF_CLUSTER_SIDES_ENABLED, - COMMAND.COMMAND_ID AS COMMAND_ID, - COMMAND.NAME AS COMMAND_NAME, - COMMAND.SOURCE AS COMMAND_SOURCE, - COMMAND.CODE AS COMMAND_CODE, - COMMAND.MUST_USE_TIMED_INVOKE AS MUST_USE_TIMED_INVOKE, - ENDPOINT_TYPE_COMMAND.INCOMING AS INCOMING, - ENDPOINT_TYPE_COMMAND.OUTGOING AS OUTGOING, - COUNT(COMMAND.MANUFACTURER_CODE) OVER () AS MANUFACTURING_SPECIFIC_COMMAND_COUNT -FROM - COMMAND -INNER JOIN - ENDPOINT_TYPE_COMMAND -ON - ENDPOINT_TYPE_COMMAND.COMMAND_REF = COMMAND.COMMAND_ID -INNER JOIN - CLUSTER -ON - CLUSTER.CLUSTER_ID = COMMAND.CLUSTER_REF -INNER JOIN - ENDPOINT_TYPE_CLUSTER -ON - ENDPOINT_TYPE_CLUSTER.CLUSTER_REF = CLUSTER.CLUSTER_ID -WHERE - ENDPOINT_TYPE_COMMAND.ENDPOINT_TYPE_REF IN (${endpointTypeIds}) + SELECT + CLUSTER.CLUSTER_ID, + CLUSTER.NAME AS CLUSTER_NAME, + CLUSTER.CODE AS CLUSTER_CODE, + CLUSTER.DEFINE AS CLUSTER_DEFINE, + COMMAND.MANUFACTURER_CODE AS COMMAND_MANUFACTURER_CODE, + ENDPOINT_TYPE_CLUSTER.SIDE AS CLUSTER_SIDE, + ENDPOINT_TYPE_CLUSTER.ENABLED AS CLUSTER_ENABLED, + ENDPOINT_TYPE_CLUSTER.ENDPOINT_TYPE_CLUSTER_ID, + COUNT(*) OVER (PARTITION BY CLUSTER.NAME, COMMAND.NAME) AS NO_OF_CLUSTER_SIDES_ENABLED, + COMMAND.COMMAND_ID AS COMMAND_ID, + COMMAND.NAME AS COMMAND_NAME, + COMMAND.SOURCE AS COMMAND_SOURCE, + COMMAND.CODE AS COMMAND_CODE, + COMMAND.MUST_USE_TIMED_INVOKE AS MUST_USE_TIMED_INVOKE, + ENDPOINT_TYPE_COMMAND.INCOMING AS INCOMING, + ENDPOINT_TYPE_COMMAND.OUTGOING AS OUTGOING, + COUNT(COMMAND.MANUFACTURER_CODE) OVER () AS MANUFACTURING_SPECIFIC_COMMAND_COUNT + FROM COMMAND + INNER JOIN ENDPOINT_TYPE_COMMAND + ON ENDPOINT_TYPE_COMMAND.COMMAND_REF = COMMAND.COMMAND_ID + INNER JOIN CLUSTER + ON CLUSTER.CLUSTER_ID = COMMAND.CLUSTER_REF + INNER JOIN ENDPOINT_TYPE_CLUSTER + ON ENDPOINT_TYPE_CLUSTER.CLUSTER_REF = CLUSTER.CLUSTER_ID + WHERE ENDPOINT_TYPE_COMMAND.ENDPOINT_TYPE_REF IN (${endpointTypeIds}) AND ENDPOINT_TYPE_COMMAND.ENDPOINT_TYPE_REF = ENDPOINT_TYPE_CLUSTER.ENDPOINT_TYPE_REF - AND ENDPOINT_TYPE_CLUSTER.SIDE IN ("client", "server") AND ENDPOINT_TYPE_CLUSTER.ENABLED=1 - AND ENDPOINT_TYPE_COMMAND.INCOMING=1 AND COMMAND.SOURCE!=ENDPOINT_TYPE_CLUSTER.SIDE + AND ENDPOINT_TYPE_CLUSTER.SIDE IN ("client", "server") AND ENDPOINT_TYPE_CLUSTER.ENABLED=1 AND ${isIncomingSql} + AND COMMAND.SOURCE!=ENDPOINT_TYPE_CLUSTER.SIDE AND CLUSTER.NAME = "${clName}" AND ENDPOINT_TYPE_CLUSTER.SIDE = "${clSide}" - ${mfgSpecificString} -GROUP BY COMMAND.NAME` + ${mfgSpecificString} GROUP BY COMMAND.NAME` ) .then((rows) => rows.map(mapFunction)) } +/** + * + * @param db + * @param endpointTypes + * @param clName + * @param clSide + * @param isMfgSpecific + * @returns Incoming Commands for a cluster + */ +async function selectAllIncomingCommandsForCluster( + db, + endpointTypes, + clName, + clSide, + isMfgSpecific +) { + return selectAllIncomingOrOutgoingCommandsForCluster( + db, + endpointTypes, + clName, + clSide, + isMfgSpecific, + true + ) +} + +/** + * + * @param db + * @param endpointTypes + * @param clName + * @param clSide + * @param isMfgSpecific + * @returns Outgoing Commands for a cluster + */ +async function selectAllOutgoingCommandsForCluster( + db, + endpointTypes, + clName, + clSide, + isMfgSpecific +) { + return selectAllIncomingOrOutgoingCommandsForCluster( + db, + endpointTypes, + clName, + clSide, + isMfgSpecific, + false + ) +} + /** * Returns all incoming commands. * @param {*} db @@ -1574,3 +1681,7 @@ exports.selectMfgClustersWithIncomingCommandsForClusterCode = selectMfgClustersWithIncomingCommandsForClusterCode exports.selectAllCommandsWithClusterInfo = selectAllCommandsWithClusterInfo exports.selectAllCommandsWithArguments = selectAllCommandsWithArguments +exports.selectAllClustersWithOutgoingCommands = + selectAllClustersWithOutgoingCommands +exports.selectAllOutgoingCommandsForCluster = + selectAllOutgoingCommandsForCluster diff --git a/src-electron/generator/helper-session.js b/src-electron/generator/helper-session.js index f55236923732fbe5bac2013b55b43b60cfe973ae..e1097641e84aae8fba2b0fcb305696f99578971a 100644 --- a/src-electron/generator/helper-session.js +++ b/src-electron/generator/helper-session.js @@ -781,6 +781,84 @@ async function all_user_cluster_generated_commands(options) { return templateUtil.collectBlocks(endpointCommands, options, this) } +/** + * Util function for all clusters with side that have available incoming or + * outgiong commands across all endpoints. + * @param options + * @param is_incoming boolean to check if commands are incoming or outgoing + * @returns All clusters with side that have available incoming or outgiong + * commands across all endpoints. + */ +async function all_user_clusters_with_incoming_or_outgoing_commands( + options, + currentContext, + isIncoming +) { + let endpointTypes = await queryEndpointType.selectUsedEndpointTypeIds( + currentContext.global.db, + currentContext.global.sessionId + ) + if (isIncoming) { + if ( + 'uniqueClusterCodes' in options.hash && + options.hash.uniqueClusterCodes == 'true' + ) { + let clustersWithIncomingCommands = + await queryCommand.selectAllClustersWithIncomingCommands( + currentContext.global.db, + endpointTypes, + true + ) + return templateUtil.collectBlocks( + clustersWithIncomingCommands, + options, + currentContext + ) + } else { + let clustersWithIncomingCommands = + await queryCommand.selectAllClustersWithIncomingCommands( + currentContext.global.db, + endpointTypes, + false + ) + return templateUtil.collectBlocks( + clustersWithIncomingCommands, + options, + currentContext + ) + } + } else { + if ( + 'uniqueClusterCodes' in options.hash && + options.hash.uniqueClusterCodes == 'true' + ) { + let clustersWithOutgoingCommands = + await queryCommand.selectAllClustersWithOutgoingCommands( + currentContext.global.db, + endpointTypes, + true + ) + return templateUtil.collectBlocks( + clustersWithOutgoingCommands, + options, + currentContext + ) + } else { + let clustersWithOutgoingCommands = + await queryCommand.selectAllClustersWithOutgoingCommands( + currentContext.global.db, + endpointTypes, + false + ) + return templateUtil.collectBlocks( + clustersWithOutgoingCommands, + options, + currentContext + ) + } + } +} + /** * All clusters with side that have available incoming commands * @param options @@ -788,25 +866,25 @@ async function all_user_cluster_generated_commands(options) { * all endpoints. */ function all_user_clusters_with_incoming_commands(options) { - return queryEndpointType - .selectUsedEndpointTypeIds(this.global.db, this.global.sessionId) - .then((endpointTypes) => - 'uniqueClusterCodes' in options.hash && - options.hash.uniqueClusterCodes == 'true' - ? queryCommand.selectAllClustersWithIncomingCommands( - this.global.db, - endpointTypes, - true - ) - : queryCommand.selectAllClustersWithIncomingCommands( - this.global.db, - endpointTypes, - false - ) - ) - .then((clustersWithIncomingCommands) => - templateUtil.collectBlocks(clustersWithIncomingCommands, options, this) - ) + return all_user_clusters_with_incoming_or_outgoing_commands( + options, + this, + true + ) +} + +/** + * All clusters with side that have available outgoing commands + * @param options + * @returns All clusters with side that have available outgoing commands across + * all endpoints. + */ +async function all_user_clusters_with_outgoing_commands(options) { + return all_user_clusters_with_incoming_or_outgoing_commands( + options, + this, + false + ) } /** @@ -913,15 +991,21 @@ async function all_user_incoming_commands_for_all_clusters(options) { } /** - * All commands that need to be parsed for a given cluster - * @param clusterName - * @param options - * @returns all commands that need to be parsed for a given cluster - */ -async function all_incoming_commands_for_cluster( + * A util function for all incoming or outgoing commands that need to be parsed + * for a given cluster + * @param clusterName + * @param clusterSide + * @param isIncoming + * @param options + * @returns All incoming or outgoing commands that need to be parsed for a given + cluster + */ +async function all_incoming_or_outgoing_commands_for_cluster( clusterName, clusterSide, - options + isIncoming, + options, + currentContext ) { let isMfgSpec = 'isMfgSpecific' in options.hash @@ -929,20 +1013,71 @@ async function all_incoming_commands_for_cluster( : undefined let endpointTypes = await queryEndpointType.selectUsedEndpointTypeIds( - this.global.db, - this.global.sessionId + currentContext.global.db, + currentContext.global.sessionId ) - let clustersWithIncomingCommands = - await queryCommand.selectAllIncomingCommandsForCluster( - this.global.db, - endpointTypes, - clusterName, - clusterSide, - isMfgSpec - ) + let clustersWithIncomingOrOutgoingCommands = isIncoming + ? await queryCommand.selectAllIncomingCommandsForCluster( + currentContext.global.db, + endpointTypes, + clusterName, + clusterSide, + isMfgSpec + ) + : await queryCommand.selectAllOutgoingCommandsForCluster( + currentContext.global.db, + endpointTypes, + clusterName, + clusterSide, + isMfgSpec + ) - return templateUtil.collectBlocks(clustersWithIncomingCommands, options, this) + return templateUtil.collectBlocks( + clustersWithIncomingOrOutgoingCommands, + options, + currentContext + ) +} + +/** + * All incoming commands that need to be parsed for a given cluster + * @param clusterName + * @param options + * @returns all incoming commands that need to be parsed for a given cluster + */ +async function all_incoming_commands_for_cluster( + clusterName, + clusterSide, + options +) { + return all_incoming_or_outgoing_commands_for_cluster( + clusterName, + clusterSide, + true, + options, + this + ) +} + +/** + * All outgoing commands that need to be parsed for a given cluster + * @param clusterName + * @param options + * @returns all outgoing commands that need to be parsed for a given cluster + */ +async function all_outgoing_commands_for_cluster( + clusterName, + clusterSide, + options +) { + return all_incoming_or_outgoing_commands_for_cluster( + clusterName, + clusterSide, + false, + options, + this + ) } /** @@ -1303,3 +1438,6 @@ exports.all_incoming_commands_for_cluster_combined = dep( exports.if_command_discovery_enabled = if_command_discovery_enabled exports.manufacturing_clusters_with_incoming_commands = manufacturing_clusters_with_incoming_commands +exports.all_user_clusters_with_outgoing_commands = + all_user_clusters_with_outgoing_commands +exports.all_outgoing_commands_for_cluster = all_outgoing_commands_for_cluster diff --git a/test/gen-template/zigbee/gen-templates.json b/test/gen-template/zigbee/gen-templates.json index 991788578a87d985620a651093e53a0e2f7a216c..8716e0c1397003d3726d51f21183d24bc48ff528 100644 --- a/test/gen-template/zigbee/gen-templates.json +++ b/test/gen-template/zigbee/gen-templates.json @@ -197,6 +197,11 @@ "name": "ZCL command APIs", "output": "zap-command.h" }, + { + "path": "zap-outgoing-command.zapt", + "name": "ZCL outgoing command", + "output": "zap-outgoing-command.out" + }, { "path": "zap-config.zapt", "name": "ZCL endpoint configuration", diff --git a/test/gen-template/zigbee/zap-outgoing-command.zapt b/test/gen-template/zigbee/zap-outgoing-command.zapt new file mode 100644 index 0000000000000000000000000000000000000000..7ac8a5471b5ea6b56d9d71d189864ac3b145ab31 --- /dev/null +++ b/test/gen-template/zigbee/zap-outgoing-command.zapt @@ -0,0 +1,6 @@ +{{#all_user_clusters_with_outgoing_commands}} +{{clusterName}} Cluster Ougtoing commands +{{#all_outgoing_commands_for_cluster clusterName clusterSide isMfgSpecific="false"}} +Outgoing Command: {{commandName}} +{{/all_outgoing_commands_for_cluster}} +{{/all_user_clusters_with_outgoing_commands}} diff --git a/test/gen-zigbee.test.js b/test/gen-zigbee.test.js index 829b533dadac64e92729c162c2ef67eb18dc25cb..f324205e5599873fa4dea18b8aae9c19c2f48e5d 100644 --- a/test/gen-zigbee.test.js +++ b/test/gen-zigbee.test.js @@ -608,6 +608,14 @@ test( expect(pv5).toContain('case ZCL_GP_PROXY_TABLE_REQUEST_COMMAND_ID:') expect(pv5).not.toContain('uint64_t gpdIeee;') expect(pv5).toContain('uint8_t * gpdIeee;') + + //********* Test the helpers related to outgoing commands****************** + // Test Cluster outgoing commands that should be generated + let zapOutgoingCommands = genResult.content['zap-outgoing-command.out'] + expect(zapOutgoingCommands).not.toBeNull() + expect(zapOutgoingCommands).toContain( + 'ZLL Commissioning Cluster Ougtoing commands\nOutgoing Command: DeviceInformationResponse\nOutgoing Command: EndpointInformation\nOutgoing Command: GetEndpointListResponse\nOutgoing Command: GetGroupIdentifiersResponse\nOutgoing Command: NetworkJoinEndDeviceResponse\nOutgoing Command: NetworkJoinRouterResponse\nOutgoing Command: NetworkStartResponse\nOutgoing Command: ScanResponse\nZLL Commissioning Cluster Ougtoing commands\nOutgoing Command: DeviceInformationRequest\nOutgoing Command: IdentifyRequest\nOutgoing Command: NetworkJoinEndDeviceRequest\nOutgoing Command: NetworkJoinRouterRequest\nOutgoing Command: NetworkStartRequest\nOutgoing Command: NetworkUpdateRequest\nOutgoing Command: ResetToFactoryNewRequest\nOutgoing Command: ScanRequest\n' + ) }, testUtil.timeout.long() ) diff --git a/test/test-util.js b/test/test-util.js index 7668e958e1f2f54de4ab874225d5f2abebef93df..ebff347d6e78a052a740d905d52339b92f1e4cfa 100644 --- a/test/test-util.js +++ b/test/test-util.js @@ -78,7 +78,7 @@ exports.timeout = { exports.testTemplate = { zigbee: './test/gen-template/zigbee/gen-templates.json', - zigbeeCount: 26, + zigbeeCount: 27, matter: './test/gen-template/matter/gen-test.json', matterCount: 3, dotdot: './test/gen-template/dotdot/dotdot-templates.json',