Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/zwave-js/zwave-js-ui into…
Browse files Browse the repository at this point in the history
… zwave-js-13
  • Loading branch information
robertsLando committed Jul 18, 2024
2 parents 546149d + f919273 commit e7338b3
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 7 deletions.
16 changes: 16 additions & 0 deletions api/lib/ZwaveClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export const allowedApis = validateMethods([
'getNodeNeighbors',
'discoverNodeNeighbors',
'getAssociations',
'checkAssociation',
'addAssociations',
'removeAssociations',
'removeAllAssociations',
Expand Down Expand Up @@ -1759,6 +1760,21 @@ class ZwaveClient extends TypedEventEmitter<ZwaveClientEventCallbacks> {
return toReturn
}

/**
* Check if a given association is allowed
*/
checkAssociation(
source: AssociationAddress,
groupId: number,
association: AssociationAddress,
) {
return this.driver.controller.checkAssociation(
source,
groupId,
association,
)
}

/**
* Add a node to the array of specified [associations](https://zwave-js.github.io/node-zwave-js/#/api/controller?id=association-interface)
*/
Expand Down
94 changes: 93 additions & 1 deletion src/components/dialogs/DialogAssociation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@
:items="targetEndpoints"
></v-select>
</v-col>

<v-col v-if="associationError" cols="12">
<v-alert text dense type="error">
{{ associationError }}
</v-alert>
</v-col>
</v-col>
</v-row>
</v-form>
Expand All @@ -108,7 +114,7 @@
<v-btn
color="blue darken-1"
text
:disabled="nodesInGroup >= maxNodes"
:disabled="nodesInGroup >= maxNodes || associationError"
@click="$refs.form.validate() && $emit('add', group)"
>ADD</v-btn
>
Expand All @@ -121,8 +127,13 @@
import { Protocols } from '@zwave-js/core/safe'
import { mapState } from 'pinia'
import useBaseStore from '../../stores/base.js'
import { getAssociationAddress } from '../../lib/utils'
import { AssociationCheckResult } from '@zwave-js/cc/safe'
import { getEnumMemberName } from 'zwave-js/safe'
import InstancesMixin from '../../mixins/InstancesMixin.js'
export default {
mixins: [InstancesMixin],
props: {
value: Boolean,
associations: Array,
Expand All @@ -132,6 +143,15 @@ export default {
value() {
this.$refs.form && this.$refs.form.resetValidation()
this.resetGroup()
this.associationError = ''
},
group: {
deep: true,
handler() {
if (this.$refs.form?.validate()) {
this.allowedAssociation()
}
},
},
},
computed: {
Expand Down Expand Up @@ -185,11 +205,83 @@ export default {
return {
valid: true,
group: {},
associationError: '',
defaultGroup: { endpoint: null },
required: (v) => !!v || 'This field is required',
}
},
methods: {
getAssociationAddress,
async allowedAssociation() {
const association = this.group
const target = !isNaN(association.target)
? parseInt(association.target)
: association.target?.id
if (isNaN(target)) {
this.associationError = ''
return
}
const group = association.group
if (!group) {
this.associationError = ''
return
}
const toAdd = { nodeId: target }
if (group.multiChannel && association.targetEndpoint >= 0) {
toAdd.endpoint = association.targetEndpoint
}
const args = [
this.getAssociationAddress({
nodeId: this.node.id,
endpoint: association.endpoint,
}),
group.value,
toAdd,
]
const response = await this.app.apiRequest(
'checkAssociation',
args,
{
showInfo: false,
},
)
if (response.success) {
const checkResult = response.result
if (checkResult === AssociationCheckResult.OK) {
this.associationError = ''
} else if (
checkResult ===
AssociationCheckResult.Forbidden_SecurityClassMismatch
) {
this.associationError = `Association not allowed: Node ${this.node.id} does not have the same security class as Node ${target}!`
} else if (
checkResult ===
AssociationCheckResult.Forbidden_DestinationSecurityClassNotGranted
) {
this.associationError = `Association not allowed: Node ${this.node.id} was not granted the highest security class of Node ${target}!`
} else if (
checkResult ===
AssociationCheckResult.Forbidden_NoSupportedCCs
) {
this.associationError = `Association not allowed: Node ${this.node.id} sends no commands in this group that Node ${target} supports!`
} else {
// This should not happen, but just in case
this.associationError = `Association not allowed: ${getEnumMemberName(
AssociationCheckResult,
checkResult,
)}`
}
}
},
resetGroup() {
this.group = Object.assign({}, this.defaultGroup)
},
Expand Down
8 changes: 2 additions & 6 deletions src/components/nodes-table/AssociationGroups.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import useBaseStore from '../../stores/base.js'
import InstancesMixin from '../../mixins/InstancesMixin.js'
import { getEnumMemberName } from 'zwave-js/safe'
import { AssociationCheckResult } from '@zwave-js/cc/safe'
import { getAssociationAddress } from '../../lib/utils'
export default {
components: {
Expand Down Expand Up @@ -112,12 +113,7 @@ export default {
},
methods: {
...mapActions(useBaseStore, ['showSnackbar']),
getAssociationAddress(ass) {
return {
nodeId: ass.nodeId,
endpoint: ass.endpoint === null ? undefined : ass.endpoint,
}
},
getAssociationAddress,
getNodeName(nodeId) {
const node = this.nodes[this.nodesMap.get(nodeId)]
return node ? node._name : 'NodeID_' + nodeId
Expand Down
7 changes: 7 additions & 0 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,10 @@ export function getProtocolIcon(protocol) {
description: getProtocol({ protocol }),
}
}

export function getAssociationAddress(ass) {
return {
nodeId: ass.nodeId,
endpoint: ass.endpoint === null ? undefined : ass.endpoint,
}
}

0 comments on commit e7338b3

Please sign in to comment.