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: multicast/broadcast apis #25

Merged
merged 32 commits into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8dd1d36
feat: multicast api
robertsLando Nov 24, 2020
fa2679f
docs: fix lint
robertsLando Nov 24, 2020
0b63206
Merge branch 'master' into feat#multicast
robertsLando Nov 24, 2020
af42de6
fix: await writes to zwave broadcast and multicast requests
robertsLando Nov 25, 2020
b9fbecc
fix: lint issues
robertsLando Nov 25, 2020
badad4e
fix: use parsed payload in write value
robertsLando Nov 25, 2020
9570bec
Merge branch 'master' into feat#multicast
robertsLando Nov 25, 2020
9ce6fff
docs: json payload
robertsLando Nov 25, 2020
7f20390
Merge branch 'master' into feat#multicast
robertsLando Dec 7, 2020
1648adf
Merge branch 'master' into feat#multicast
robertsLando Dec 10, 2020
fbeccbd
feat: use native multicast and broadcast from zwave-js
robertsLando Dec 11, 2020
64dbcd5
docs: multicast and broadcast
robertsLando Dec 11, 2020
0d51cdd
Merge branch 'master' into feat#multicast
robertsLando Dec 14, 2020
6e1e51c
Merge branch 'master' into feat#multicast
robertsLando Jan 23, 2021
854b4fa
fix: replace debug with logger
robertsLando Jan 23, 2021
42cccfb
fix: replace debug with logger
robertsLando Jan 23, 2021
ddd63c6
fix: replace debug with logger
robertsLando Jan 23, 2021
0d1cb48
fix: replace debug with logger
robertsLando Jan 23, 2021
0e880b8
fix: replace debug with logger
robertsLando Jan 23, 2021
cafd301
fix: replace debug with logger
robertsLando Jan 23, 2021
4b63eed
fix: replace debug with logger
robertsLando Jan 23, 2021
c686a0f
fix: types
robertsLando Jan 23, 2021
22aed8c
fix: types
robertsLando Jan 23, 2021
f611bcc
fix: lint issues
robertsLando Jan 23, 2021
b1987a5
Merge branch 'master' into feat#multicast
robertsLando Mar 9, 2021
de8847b
fix: lint issues and missing fallbacks
robertsLando Mar 10, 2021
103575d
fix: broadcast/multicast docs
robertsLando Mar 10, 2021
4100f38
fix: lint issues
robertsLando Mar 10, 2021
c35ecb4
fix: docs nits
robertsLando Mar 10, 2021
36e4eb3
Merge branch 'master' into feat#multicast
robertsLando Mar 10, 2021
566dbef
Merge branch 'master' into feat#multicast
robertsLando Mar 15, 2021
19a8339
docs: fix docs/guide/mqtt.md
robertsLando Mar 15, 2021
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
49 changes: 37 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,26 @@ Fully configurable Zwave to MQTT **Gateway** and **Control Panel**.

- [Zwave To MQTT](#zwave-to-mqtt)
- [📖 Table of contents](#-table-of-contents)
- [:electric_plug: Installation](#-installation)
- [DOCKER :tada: way](#docker--way)
- [:electric_plug: Installation](#electric_plug-installation)
- [DOCKER :tada: way](#docker-tada-way)
- [Kubernetes way](#kubernetes-way)
- [NodeJS or PKG version](#nodejs-or-pkg-version)
- [Reverse Proxy Setup](#reverse-proxy-setup)
- [:nerd_face: Development](#-development)
- [:nerd_face: Development](#nerd_face-development)
- [Developing against a different backend](#developing-against-a-different-backend)
- [:wrench: Usage](#-usage)
- [:wrench: Usage](#wrench-usage)
- [Zwave](#zwave)
- [MQTT](#mqtt)
- [Gateway](#gateway)
- [Special topics](#special-topics)
- [Gateway values table](#gateway-values-table)
- [:file_folder: Nodes Management](#-nodes-management)
- [:file_folder: Nodes Management](#file_folder-nodes-management)
- [Add a node](#add-a-node)
- [Remove a node](#remove-a-node)
- [Replace failed node (NOT IMPLEMENTED YET)](#replace-failed-node-not-implemented-yet)
- [Remove a failed node](#remove-a-failed-node)
- [:star: Features](#-features)
- [:robot: Home Assistant integration (BETA)](#-home-assistant-integration-beta)
- [:star: Features](#star-features)
- [:robot: Home Assistant integration (BETA)](#robot-home-assistant-integration-beta)
- [Components management](#components-management)
- [Rediscover Node](#rediscover-node)
- [Edit existing component](#edit-existing-component)
Expand All @@ -64,13 +64,14 @@ Fully configurable Zwave to MQTT **Gateway** and **Control Panel**.
- [Thermostats](#thermostats)
- [Fans](#fans)
- [Thermostats with Fans](#thermostats-with-fans)
- [:gift: MQTT APIs](#-mqtt-apis)
- [:gift: MQTT APIs](#gift-mqtt-apis)
- [Zwave Events](#zwave-events)
- [Zwave APIs](#zwave-apis)
- [Custom APIs](#custom-apis)
- [Set values](#set-values)
- [Multicast](#multicast)
- [Broadcast](#broadcast)
- [:camera: Screenshots](#-screenshots)
- [:camera: Screenshots](#camera-screenshots)
- [Settings](#settings)
- [Control Panel](#control-panel)
- [Groups associations](#groups-associations)
Expand All @@ -79,9 +80,9 @@ Fully configurable Zwave to MQTT **Gateway** and **Control Panel**.
- [Debug](#debug)
- [Health check endpoints](#health-check-endpoints)
- [Environment variables](#environment-variables)
- [:question: FAQ](#-faq)
- [:pray: Thanks](#-thanks)
- [:pencil: TODOs](#-todos)
- [:question: FAQ](#question-faq)
- [:pray: Thanks](#pray-thanks)
- [:pencil: TODOs](#pencil-todos)
- [:bowtie: Author](#bowtie-author)

## :electric_plug: Installation
Expand Down Expand Up @@ -728,6 +729,30 @@ I will set the Heating setpoint of the node with id `4` located in the `office`

`zwave/office/nodeID_4/thermostat_setpoint/heating`

### Multicast

You can send Multicast requests to _all values with a specific suffix_ of a _group_ of nodes in the network.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really clear on this, maybe provide a use case from the outset such as adjusting all your thermostats/lights?

be clearer that this is just a shorthand, it's not an actual multicast, behind the scenes zj2m is doing the heavy lifting and iterating

Copy link
Member

@AlCalzone AlCalzone Nov 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zwave-js does support actual multicast (if you use the same command and target the same endpoint of all nodes).

But it doesn't go through the commandClasses API that is being used here. Might be a good idea for a new feature , so you can grab a group of nodes and send a single multicast command to all of them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've raised zwave-js/node-zwave-js#1150 to track that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, what you can do already is this:

const cmd = new BinarySwitchCCSet(driver, {
	nodeId: [2,3,4,5],
	targetValue: true,
});
await driver.sendCommand(cmd);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do agree with that i really would like to send a group of nodes the command at the same time using a real multicast if that is possible.

Why, because i found it nice that all nodes respond at the same time, now it takes upto 5 seconds to respond.
Which is also generating errors like #26

My feeling about this is that since all these nodes are generating data (e.g. power reports, closing / dimming duration reports) that it become quite busy on the network, therefore i would like to send it using a real multicast to the nodes so that they all do receive the command before it is getting busy on the network.

If i see it now it looks like this is the same as what i can do within openhab

Shutters_Goup_All.members.forEach[ shutter | shutter.sendCommand(100) ]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should wait for: zwave-js/node-zwave-js#1150 Before going on with this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, what you can do already is this:

@AlCalzone How could I get the correct command class constructor based on the command class id?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Multicast API is accessible from:

`<mqtt_prefix>/_CLIENTS/ZWAVE_GATEWAY-<mqtt_name>/multicast/<value_topic_suffix>/set`

- `value_topic_suffix`: the suffix of the topic of the value I want to control using multicast.

It works like the set value API without the node name and location properties.
If the API is correctly called the same payload of the request will be published
to the topic without `/set` suffix.

Example of multicast command (gateway configured as `named topics`):

`zwave/_CLIENTS/ZWAVE_GATEWAY-test/multicast/thermostat_setpoint/heating/set`

Payload: `{ "nodes": [5, 7, 9], "value": 25.5}`

Nodes **5, 7, 9** with command class `thermostat_setpoint` and value `heating` will be set to `25.5` and I will get the same value on the topic:

`zwave/_CLIENTS/ZWAVE_GATEWAY-test/multicast/thermostat_setpoint/heating`

### Broadcast

You can send broadcast values to _all values with a specific suffix_ in the network.
Expand Down
43 changes: 38 additions & 5 deletions lib/Gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ function init (config, zwave, mqtt) {
if (mqtt) {
mqtt.on('writeRequest', onWriteRequest.bind(this))
mqtt.on('broadcastRequest', onBroadRequest.bind(this))
mqtt.on('multicastRequest', onMulticastRequest.bind(this))
mqtt.on('apiCall', onApiRequest.bind(this))
mqtt.on('hassStatus', onHassStatus.bind(this))
mqtt.on('brokerStatus', onBrokerStatus.bind(this))
Expand Down Expand Up @@ -289,7 +290,7 @@ function onValueChanged (valueId, node, changed) {
data = { time: Date.now(), value: tmpVal }
}

if (!valueId.read_only && !this.topicValues[topic]) {
if (valueId.writeable && !this.topicValues[topic]) {
const levels = topic.split('/').length

if (this.topicLevels.indexOf(levels) < 0) {
Expand Down Expand Up @@ -423,7 +424,7 @@ async function onApiRequest (topic, apiName, payload) {
}
}

function onBroadRequest (parts, payload) {
async function onBroadRequest (parts, payload) {
const topic = parts.join('/')
const values = Object.keys(this.topicValues).filter(t => t.endsWith(topic))

Expand All @@ -435,17 +436,49 @@ function onBroadRequest (parts, payload) {
this.topicValues[values[0]].conf
)
for (let i = 0; i < values.length; i++) {
this.zwave.writeValue(this.topicValues[values[i]], payload)
await this.zwave.writeValue(this.topicValues[values[i]], payload)
}
}
}

function onWriteRequest (parts, payload) {
async function onWriteRequest (parts, payload) {
const valueId = this.topicValues[parts.join('/')]

if (valueId) {
payload = this.parsePayload(payload, valueId, valueId.conf)
this.zwave.writeValue(valueId, payload)
await this.zwave.writeValue(valueId, payload)
}
}

async function onMulticastRequest (parts, payload) {
const topic = parts.join('/')
const values = Object.keys(this.topicValues).filter(t => t.endsWith(topic))

const nodes = payload.nodes

if (!nodes || nodes.length === 0) {
debug('No nodes found in multicast request to ' + topic)
return
}

if (payload.value === undefined) {
debug('No value found to write in multicast request')
return
}

if (values.length > 0) {
// all values are the same type just different node,parse the Payload by using the first one
payload = this.parsePayload(
payload,
this.topicValues[values[0]],
this.topicValues[values[0]].conf
)
for (let i = 0; i < values.length; i++) {
const valueId = this.topicValues[values[i]]
if (nodes.indexOf(valueId.nodeId) >= 0) {
await this.zwave.writeValue(valueId, payload)
}
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion lib/MqttClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const BROADCAST_PREFIX = '_BROADCAST'

const NAME_PREFIX = 'ZWAVE_GATEWAY-'

const ACTIONS = ['broadcast', 'api']
const ACTIONS = ['broadcast', 'api', 'multicast']

const HASS_WILL = 'homeassistant/status'

Expand Down Expand Up @@ -217,6 +217,12 @@ function onMessageReceived (topic, payload) {
case 1: // api
this.emit('apiCall', parts.join('/'), parts[3], payload)
break
case 2: // multicast
this.emit('multicastRequest', parts.slice(3), payload)
// publish back to give a feedback the action is received
// same topic without /set suffix
this.publish(parts.join('/'), payload)
break
default:
debug('Unknown action received', action, topic)
}
Expand Down