Skip to content

Commit

Permalink
Endpoint Composition - rootNode on Endpoint 0 (fullFamily) (#1423)
Browse files Browse the repository at this point in the history
* adding mandatory rootNode device type on endpoint 0
  • Loading branch information
paulr34 authored Sep 24, 2024
1 parent 2b71747 commit 83db0e6
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 61 deletions.
24 changes: 17 additions & 7 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4034,18 +4034,15 @@ If they do not match, it performs an insert with the composition's type.
<a name="module_DB API_ zcl loading queries..getEndpointCompositionIdByCode"></a>

### DB API: zcl loading queries~getEndpointCompositionIdByCode(db, deviceType) ⇒ <code>Promise.&lt;(number\|null)&gt;</code>
Retrieves the endpoint composition ID by device code.

This function executes a SQL query to fetch the endpoint composition ID
associated with a given device code. If the query fails, an error is logged.
Retrieves the endpoint composition ID for a given device type code.

**Kind**: inner method of [<code>DB API: zcl loading queries</code>](#module_DB API_ zcl loading queries)
**Returns**: <code>Promise.&lt;(number\|null)&gt;</code> - The endpoint composition ID or null if not found.
**Returns**: <code>Promise.&lt;(number\|null)&gt;</code> - - A promise that resolves to the endpoint composition ID or null if not found.

| Param | Type | Description |
| --- | --- | --- |
| db | <code>Object</code> | The database connection object. |
| deviceType | <code>Object</code> | The device type object containing the device code. |
| db | <code>\*</code> | The database connection object. |
| deviceType | <code>Object</code> | The device type object containing the code. |

<a name="module_DB API_ zcl loading queries..insertDeviceComposition"></a>

Expand Down Expand Up @@ -15065,6 +15062,7 @@ This module provides the REST API to the user specific data.
* [~httpPostEndpoint(db)](#module_REST API_ endpoint..httpPostEndpoint) ⇒
* [~httpPatchEndpoint(db)](#module_REST API_ endpoint..httpPatchEndpoint) ⇒
* [~httpPostEndpointType(db)](#module_REST API_ endpoint..httpPostEndpointType) ⇒
* [~httpGetInitialComposition(db)](#module_REST API_ endpoint..httpGetInitialComposition) ⇒ <code>function</code>
* [~httpPatchEndpointType(db)](#module_REST API_ endpoint..httpPatchEndpointType) ⇒

<a name="module_REST API_ endpoint..httpDeleteEndpoint"></a>
Expand Down Expand Up @@ -15127,6 +15125,18 @@ HTTP POST endpoint type
| --- | --- |
| db | <code>\*</code> |

<a name="module_REST API_ endpoint..httpGetInitialComposition"></a>

### REST API: endpoint~httpGetInitialComposition(db) ⇒ <code>function</code>
Handles the HTTP GET request to retrieve the root node.

**Kind**: inner method of [<code>REST API: endpoint</code>](#module_REST API_ endpoint)
**Returns**: <code>function</code> - - An async function that handles the HTTP request and response.

| Param | Type | Description |
| --- | --- | --- |
| db | <code>Object</code> | The database connection object. |

<a name="module_REST API_ endpoint..httpPatchEndpointType"></a>

### REST API: endpoint~httpPatchEndpointType(db) ⇒
Expand Down
10 changes: 10 additions & 0 deletions src-electron/db/db-mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,16 @@ exports.map = {
}
},

rootNode: (x) => {
if (x == null) return undefined
return {
deviceTypeRef: x.DEVICE_TYPE_REF,
code: x.CODE,
name: x.NAME,
type: x.TYPE
}
},

user: (x) => {
if (x == null) return undefined
return {
Expand Down
35 changes: 35 additions & 0 deletions src-electron/db/query-endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
const dbApi = require('./db-api.js')
const bin = require('../util/bin')
const dbMapping = require('./db-mapping.js')
const dbEnum = require('../../src-shared/db-enum.js')

/**
* Returns a promise resolving into all endpoints.
Expand Down Expand Up @@ -59,6 +60,39 @@ ORDER BY E1.ENDPOINT_IDENTIFIER
return rows.map(dbMapping.map.endpoint)
}

/**
* Retrieves the root node device type references from the ENDPOINT_COMPOSITION table
* for the given package IDs.
*
* @param {*} db - The database connection object.
* @param {Array<number>} packageIds - An array of package IDs to query.
* @returns {Promise<Array>} - A promise that resolves to an array of rows containing DEVICE_TYPE_REF.
*/
async function getRootNode(db, packageIds) {
const query = `
SELECT
EC.DEVICE_TYPE_REF,
EC.CODE,
EC.TYPE,
DT.NAME,
DT.PACKAGE_REF
FROM
ENDPOINT_COMPOSITION EC
JOIN
DEVICE_TYPE DT ON EC.DEVICE_TYPE_REF = DT.DEVICE_TYPE_ID
WHERE
EC.TYPE = ? AND
DT.PACKAGE_REF IN (${packageIds.map(() => '?').join(', ')})
`

let result = await dbApi.dbAll(db, query, [
dbEnum.composition.rootNode,
...packageIds
])

return result.map(dbMapping.map.rootNode)
}

/**
* Returns a promise resolving into all endpoints based on the template
* category(eg zigbee/matter).
Expand Down Expand Up @@ -593,3 +627,4 @@ exports.getParentEndpointRef = getParentEndpointRef
exports.getParentEndpointIdentifier = getParentEndpointIdentifier
exports.selectAllEndpointsBasedOnTemplateCategory =
selectAllEndpointsBasedOnTemplateCategory
exports.getRootNode = getRootNode
49 changes: 18 additions & 31 deletions src-electron/db/query-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1029,46 +1029,33 @@ async function insertAtomics(db, packageId, data) {
* @returns A promise resolved with the result of the database insert operation.
*/
async function insertEndpointComposition(db, composition, context) {
try {
if (parseInt(context.mandatoryDeviceTypes, 16) === composition.code) {
return await dbApi.dbInsert(
db,
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
[dbEnum.composition.mandatoryEndpoint, composition.code]
)
} else {
return await dbApi.dbInsert(
db,
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
[composition.compositionType, composition.code]
)
}
} catch (error) {
console.error('Error inserting endpoint composition:', error)
throw error // Re-throw the error after logging it
if (parseInt(context.mandatoryDeviceTypes, 16) === composition.code) {
return dbApi.dbInsert(
db,
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
[dbEnum.composition.rootNode, composition.code]
)
} else {
return dbApi.dbInsert(
db,
'INSERT INTO ENDPOINT_COMPOSITION (TYPE, CODE) VALUES (?, ?)',
[composition.compositionType, composition.code]
)
}
}

/**
* Retrieves the endpoint composition ID by device code.
* Retrieves the endpoint composition ID for a given device type code.
*
* This function executes a SQL query to fetch the endpoint composition ID
* associated with a given device code. If the query fails, an error is logged.
*
* @param {Object} db - The database connection object.
* @param {Object} deviceType - The device type object containing the device code.
* @returns {Promise<number|null>} The endpoint composition ID or null if not found.
* @param {*} db - The database connection object.
* @param {Object} deviceType - The device type object containing the code.
* @returns {Promise<number|null>} - A promise that resolves to the endpoint composition ID or null if not found.
*/
async function getEndpointCompositionIdByCode(db, deviceType) {
const query =
'SELECT ENDPOINT_COMPOSITION_ID FROM ENDPOINT_COMPOSITION WHERE CODE = ?'
try {
const result = await dbApi.dbGet(db, query, [deviceType.code])
return result ? result.ENDPOINT_COMPOSITION_ID : null
} catch (error) {
console.error('Error retrieving endpoint composition ID:', error)
return null
}
const result = await dbApi.dbGet(db, query, [deviceType.code])
return result ? result.ENDPOINT_COMPOSITION_ID : null
}

/**
Expand Down
27 changes: 26 additions & 1 deletion src-electron/rest/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const queryEndpointType = require('../db/query-endpoint-type.js')
const queryEndpoint = require('../db/query-endpoint.js')
const queryConfig = require('../db/query-config.js')
const querySession = require('../db/query-session.js')
const queryPackage = require('../db/query-package.js')
const validation = require('../validation/validation.js')
const restApi = require('../../src-shared/rest-api.js')
const notification = require('../db/query-session-notification.js')
Expand Down Expand Up @@ -201,6 +202,25 @@ function httpPostEndpointType(db) {
}
}

/**
* Handles the HTTP GET request to retrieve the root node.
*
* @param {Object} db - The database connection object.
* @returns {Function} - An async function that handles the HTTP request and response.
*/
function httpGetInitialComposition(db) {
return async (request, response) => {
let sessionId = request.zapSessionId
let packages = await queryPackage.getPackageSessionPackagePairBySessionId(
db,
sessionId
)
let packageIds = packages.map((item) => item.pkg.id)
let rootNode = await queryEndpoint.getRootNode(db, packageIds)
response.status(StatusCodes.OK).json(rootNode[0])
}
}

/**
* HTTP POST: endpoint type update
*
Expand Down Expand Up @@ -234,7 +254,12 @@ function httpPatchEndpointType(db) {
})
}
}

exports.get = [
{
uri: restApi.uri.loadComposition,
callback: httpGetInitialComposition
}
]
exports.post = [
{
uri: restApi.uri.endpoint,
Expand Down
9 changes: 8 additions & 1 deletion src-shared/db-enum.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ exports.packageType = {
jsonExtension: 'json-extension'
}

exports.rootNode = {
endpointId: 0,
getParentEndpointIdentifier: null,
deviceVersion: 1,
type: 'rootNode'
}

exports.packageOptionCategory = {
manufacturerCodes: 'manufacturerCodes',
typeMap: 'typeMap',
Expand Down Expand Up @@ -78,7 +85,7 @@ exports.storageOption = {
exports.composition = {
fullFamily: 'fullFamily',
tree: 'tree',
mandatoryEndpoint: 'mandatoryEndpoint'
rootNode: 'rootNode'
}

exports.zclType = {
Expand Down
3 changes: 2 additions & 1 deletion src-shared/rest-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ const uri = {
sessionCreate: '/zcl/sessionCreate',
reloadSession: '/zcl/reloadSession',
init: '/init',
forcedExternal: '/zcl/forcedExternal'
forcedExternal: '/zcl/forcedExternal',
loadComposition: '/zcl/loadComposition'
}

const uiMode = {
Expand Down
49 changes: 48 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { defineComponent } from 'vue'
import { QSpinnerGears } from 'quasar'
import ZclTour from './tutorials/ZclTour.vue'
import CommonMixin from './util/common-mixin'
import uiOptions from './util/ui-options'

const rendApi = require(`../src-shared/rend-api.js`)
const restApi = require(`../src-shared/rest-api.js`)
Expand Down Expand Up @@ -136,8 +137,18 @@ export default defineComponent({
components: {
ZclTour
},
mixins: [CommonMixin],
mixins: [CommonMixin, uiOptions],
computed: {
endpointType: {
get() {
return this.$store.state.zap.endpointView.endpointType
}
},
endpointDeviceTypeRef: {
get() {
return this.$store.state.zap.endpointTypeView.deviceTypeRef
}
},
showExceptionIcon() {
return this.$store.state.zap.showExceptionIcon
},
Expand Down Expand Up @@ -219,6 +230,39 @@ export default defineComponent({
this.$store.dispatch('zap/setDefaultUiMode', 'general')
this.$store.commit('zap/toggleShowExceptionIcon', false)
},
async loadInitialEndpoints() {
let endpoint = await this.$store.dispatch('zap/loadComposition')
if (endpoint) {
this.$store.dispatch('zap/updateSelectedEndpointType', {
endpointType: this.endpointType[endpoint.id],
deviceTypeRef:
this.endpointDeviceTypeRef[this.endpointType[endpoint.id]]
})
this.$store.dispatch('zap/updateClusters')
let info = await this.$store.dispatch(
'zap/endpointTypeClustersInfo',
this.endpointType[endpoint.id]
)
if (info.data) {
const clusterStates = info.data
const enabledClusterStates = clusterStates.filter((x) => x.enabled)
for (const states of enabledClusterStates) {
const { endpointTypeRef, clusterRef, side, enabled } = states

const arg = {
side: [side],
clusterId: clusterRef,
added: enabled
}

console.log(`Enabling UC component ${JSON.stringify(arg)}`)
this.updateSelectedComponentRequest(arg)
}
}
this.$store.dispatch('zap/updateSelectedEndpoint', endpoint.id)
this.$store.commit('zap/toggleEndpointModal', false)
}
},
getAppData() {
if (this.$serverGet != null) {
this.$serverGet(restApi.uri.uiOptions).then((res) => {
Expand Down Expand Up @@ -283,6 +327,9 @@ export default defineComponent({

// load initial UC component state
this.$store.dispatch(`zap/loadUcComponentState`)
if (query[`newConfig`]) {
this.loadInitialEndpoints()
}

// handles UC component state change events
this.$onWebSocket(
Expand Down
3 changes: 2 additions & 1 deletion src/components/ZclClusterManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,8 @@ export default {
this.$store.dispatch('zap/setFilterString', filterString)
},
updateDeviceTypeFeatures() {
let deviceTypeRefs = this.endpointDeviceTypeRef[this.selectedEndpointId]
let deviceTypeRefs =
this.endpointDeviceTypeRef[this.selectedEndpointTypeId]
this.$store.dispatch(
'zap/updateSelectedDeviceTypeFeatures',
deviceTypeRefs
Expand Down
10 changes: 4 additions & 6 deletions src/components/ZclCreateModifyEndpoint.vue
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,6 @@ export default {
return this.$store.state.zap.endpointTypeView.deviceVersion
}
},
endpointDeviceTypeRef: {
get() {
return this.$store.state.zap.endpointTypeView.deviceTypeRef
}
},
endpointDeviceId: {
get() {
return this.$store.state.zap.endpointTypeView.deviceIdentifier
Expand Down Expand Up @@ -693,7 +688,10 @@ export default {
}
})

this.$store.dispatch('zap/updateSelectedEndpoint', res.id)
this.$store.dispatch(
'zap/updateSelectedEndpoint',
res.endpointType
)
this.$store.commit('zap/toggleEndpointModal', false)
})
})
Expand Down
Loading

0 comments on commit 83db0e6

Please sign in to comment.