Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: send custom command api #360

Merged
merged 8 commits into from
Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 57 additions & 17 deletions docs/guide/mqtt.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MQTT

You have full access to all [zwavejs APIs](https://zwave-js.github.io/node-zwave-js/#/README) (and more) by simply using MQTT.
You have access to almost all [zwavejs APIs](https://zwave-js.github.io/node-zwave-js/#/README) (and more) via MQTT.

## Zwave Events

Expand Down Expand Up @@ -34,22 +34,6 @@ Where `args` is an array with the args used to call the api, the topic is:

The result will be published on the same topic without `/set`

Example: If I publish the previous json object to the topic

`zwave/_CLIENTS/ZWAVE_GATEWAY-office/api/getAssociations/set`

I will get this response (in the same topic without the suffix `/set`):

```json
{
"success": true,
"message": "Success zwave api call",
"result": [1]
}
```

`result` will contain the value returned from the API. In this example I will get an array with all node IDs that are associated to the group 1 (lifeline) of node 2.

### APIs

This are the available apis:
Expand Down Expand Up @@ -96,6 +80,62 @@ This are the available apis:
- `beginFirmwareUpdate(nodeId, fileName, data)`: Starts a firmware update of a node. The `fileName` is used to check the extension (used to detect the firmware file type) and data is a `Buffer`
- `abortFirmwareUpdate(nodeId)`: Aborts a firmware update
- `writeValue(valueId, value)`: Write a specific value to a [valueId](https://zwave-js.github.io/node-zwave-js/#/api/valueid?id=valueid)
- `sendCommand(valueId, command, args)`: Send a custom command

### Api call examples

#### Get Associations

Get all the associations of node `23` group `Lifeline` (groupId `1`)

Topic: `zwave/_CLIENTS/ZWAVE_GATEWAY-office/api/getAssociations/set`

Payload:

```js
{
"args": [
23, // nodeid
1 // lifeline group id
]
}

```

I will get this response (in the same topic without the suffix `/set`):

```js
{
"success": true,
"message": "Success zwave api call",
"result": [1] // the controller id
}
```

`result` will contain the value returned from the API. In this example I will get an array with all node IDs that are associated to the group 1 (lifeline) of node 23.

#### Send Command

Example calling [startLevelChange](https://github.com/zwave-js/node-zwave-js/blob/c695ee81cb2b1d3cf15e3db1cc14b1e41a911cc0/packages/zwave-js/src/lib/commandclass/MultilevelSwitchCC.ts) command:

Topic: `zwavejs/_CLIENTS/ZWAVE_GATEWAY-<yourName>/api/sendCommand/set`

Payload:

```js
{ "args": [
{
"nodeId": 23,
"commandClass": 38,
"commandClassName": "Multilevel Switch",
"endpoint": 0,
"property": "targetValue"
},
"startLevelChange",
[{}] // this are the args, in this case it could be omitted
]
}
```

## Set values

Expand Down
47 changes: 47 additions & 0 deletions lib/ZwaveClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const allowedApis = [
'refreshInfo',
'beginFirmwareUpdate',
'abortFirmwareUpdate',
'sendCommand',
'writeValue'
]

Expand Down Expand Up @@ -2278,6 +2279,52 @@ ZwaveClient.prototype.hardReset = async function () {

throw Error('Driver is closed')
}

ZwaveClient.prototype.sendCommand = async function (valueId, command, args) {
if (this.driver && !this.closed) {
if (typeof valueId.nodeId !== 'number') {
throw Error('nodeId must be a number')
}

const error = utils.isValueId(valueId)

if (typeof error === 'string') {
throw Error(error)
}

if (args !== undefined && !Array.isArray(args)) {
throw Error('if args is given, it must be an array')
}

const node = this.getNode(valueId.nodeId)
if (!node) {
throw Error(`Node ${valueId.nodeId} was not found!`)
}
const endpoint = node.getEndpoint(valueId.endpoint || 0)
if (!endpoint) {
throw Error(
`Endpoint ${valueId.endpoint} does not exist on Node ${valueId.nodeId}!`
)
}
const api = endpoint.commandClasses[valueId.commandClass]

if (!api || !api.isSupported()) {
throw Error(
`Node ${valueId.nodeId} (Endpoint ${valueId.endpoint}) does not support CC ${valueId.commandClass}`
)
} else if (!(command in api)) {
throw Error(
`The command ${command} does not exist for CC ${valueId.commandClass}`
)
}

const method = api[command].bind(api)
const result = args ? await method(...args) : await method()
return result
}

throw Error('Driver is closed')
}
/**
* Calls a specific `client` or `ZwaveClient` method based on `apiName`
* ZwaveClients methods used are the ones that overrides default Zwave methods
Expand Down
30 changes: 30 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,35 @@ function joinProps (...props) {
return ret
}

/**
* Checks if an object is a valueId, returns error otherwise
*
* @param {import('zwave-js').ValueID} v the object
* @returns {boolean|string} Returns true if it's a valid valueId, an error string otherwise
*/
function isValueId (v) {
if (typeof v.commandClass !== 'number' || v.commandClass < 0) {
return 'invalid `commandClass`'
}
if (v.endpoint !== undefined && v.endpoint < 0) {
return 'invalid `endpoint`'
}
if (
v.property === undefined ||
(typeof v.property !== 'string' && typeof v.property !== 'number')
) {
return 'invalid `property`'
}
if (
v.propertyKey !== undefined &&
typeof v.propertyKey !== 'string' &&
typeof v.propertyKey !== 'number'
) {
return 'invalid `propertyKey`'
}
return true
}

/**
* Converts a decimal to an hex number of 4 digits and `0x` as prefix
*
Expand Down Expand Up @@ -150,6 +179,7 @@ module.exports = {
getPath,
joinPath,
joinProps,
isValueId,
num2hex,
getVersion,
sanitizeTopic,
Expand Down
1 change: 1 addition & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ export interface ZwaveClient extends EventEmitter {
...args: any
): Promise<{ success: boolean; message: string; result: any; args: any[] }>
writeValue(valueId: Z2MValueId, value: number | string): Promise<void>
sendCommand(valueId: Z2MValueId, command: string, args: any[]): Promise<any>
}

export interface Z2MGateway {
Expand Down