From 393dc4c8477f65c1df914623629a1b2552790070 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 8 Jan 2024 09:27:41 -0700 Subject: [PATCH 1/4] feat: configure availability topics for MQTT discovery require the client to be connected (LWT will set to false when MQTT connection is lost because the process goes down), the driver to be ready, and the individual node to be online --- api/lib/Gateway.ts | 27 +++++++++++++++++++++++---- api/lib/MqttClient.ts | 9 ++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/api/lib/Gateway.ts b/api/lib/Gateway.ts index 84aa190130..dfd894f79c 100644 --- a/api/lib/Gateway.ts +++ b/api/lib/Gateway.ts @@ -1671,10 +1671,29 @@ export default class Gateway { payload.command_topic = setTopic || getTopic + '/set' } - // Set availability topic using node status topic - // payload.availability_topic = this.mqtt.getTopic(this.nodeTopic(node)) + '/status/hass' - // payload.payload_available = true - // payload.payload_not_available = false + // Set availability config using node status topic, client status topic + // (which is the LWT), and driver status topic + payload.availability = [ + { + payload_available: 'true', + payload_not_available: 'false', + topic: this.mqtt.getTopic(this.nodeTopic(node)) + '/status' + }, + { + topic: this.mqtt.getStatusTopic(), + value_template: "{{'online' if value_json.value else 'offline'}}" + }, + { + payload_available: 'true', + payload_not_available: 'false', + topic: this.mqtt.getTopic('driver/status') + } + ] + if (this.config.payloadType !== PAYLOAD_TYPE.RAW) { + payload.availability[0].value_template = "{{'true' if value_json.value else 'false'}}" + } + payload.availability_mode = "all" + if ( ['binary_sensor', 'sensor', 'lock', 'climate', 'fan'].includes( cfg.type, diff --git a/api/lib/MqttClient.ts b/api/lib/MqttClient.ts index 43d7944e8d..17c02efd47 100644 --- a/api/lib/MqttClient.ts +++ b/api/lib/MqttClient.ts @@ -115,6 +115,13 @@ class MqttClient extends TypedEventEmitter { return `${this.config.prefix}/${MqttClient.CLIENTS_PREFIX}/${this._clientID}/${suffix}` } + /** + * Returns the topic used to report client status + */ + getStatusTopic() { + return this.getClientTopic(MqttClient.STATUS_TOPIC) + } + /** * Method used to close clients connection, use this before destroy */ @@ -352,7 +359,7 @@ class MqttClient extends TypedEventEmitter { clean: config.clean, rejectUnauthorized: !config.allowSelfsigned, will: { - topic: this.getClientTopic(MqttClient.STATUS_TOPIC), + topic: this.getStatusTopic(), payload: JSON.stringify({ value: false }) as any, qos: this.config.qos, retain: this.config.retain, From 45bb903e5f6dbf25d1bbeb1a36a76ec0d5feb9cd Mon Sep 17 00:00:00 2001 From: Z-Wave JS Bot Date: Tue, 9 Jan 2024 15:27:58 +0000 Subject: [PATCH 2/4] style: fix lint --- api/lib/Gateway.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/api/lib/Gateway.ts b/api/lib/Gateway.ts index dfd894f79c..b7094658da 100644 --- a/api/lib/Gateway.ts +++ b/api/lib/Gateway.ts @@ -1677,22 +1677,24 @@ export default class Gateway { { payload_available: 'true', payload_not_available: 'false', - topic: this.mqtt.getTopic(this.nodeTopic(node)) + '/status' + topic: this.mqtt.getTopic(this.nodeTopic(node)) + '/status', }, { topic: this.mqtt.getStatusTopic(), - value_template: "{{'online' if value_json.value else 'offline'}}" + value_template: + "{{'online' if value_json.value else 'offline'}}", }, { payload_available: 'true', payload_not_available: 'false', - topic: this.mqtt.getTopic('driver/status') - } + topic: this.mqtt.getTopic('driver/status'), + }, ] if (this.config.payloadType !== PAYLOAD_TYPE.RAW) { - payload.availability[0].value_template = "{{'true' if value_json.value else 'false'}}" + payload.availability[0].value_template = + "{{'true' if value_json.value else 'false'}}" } - payload.availability_mode = "all" + payload.availability_mode = 'all' if ( ['binary_sensor', 'sensor', 'lock', 'climate', 'fan'].includes( From 1edb6ab6a78af0f9ffb6f25a81264876832b9e68 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 9 Jan 2024 08:38:16 -0700 Subject: [PATCH 3/4] extract availability config to separate method so it can also be configured on custom devices --- api/lib/Gateway.ts | 57 +++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/api/lib/Gateway.ts b/api/lib/Gateway.ts index b7094658da..b51a1c0c15 100644 --- a/api/lib/Gateway.ts +++ b/api/lib/Gateway.ts @@ -931,6 +931,8 @@ export default class Gateway { // Set device information using node info payload.device = this._deviceInfo(node, nodeName) + this._availabilityConfig(node, payload) + hassDevice.object_id = utils .sanitizeTopic(hassDevice.object_id, true) .toLocaleLowerCase() @@ -1671,30 +1673,7 @@ export default class Gateway { payload.command_topic = setTopic || getTopic + '/set' } - // Set availability config using node status topic, client status topic - // (which is the LWT), and driver status topic - payload.availability = [ - { - payload_available: 'true', - payload_not_available: 'false', - topic: this.mqtt.getTopic(this.nodeTopic(node)) + '/status', - }, - { - topic: this.mqtt.getStatusTopic(), - value_template: - "{{'online' if value_json.value else 'offline'}}", - }, - { - payload_available: 'true', - payload_not_available: 'false', - topic: this.mqtt.getTopic('driver/status'), - }, - ] - if (this.config.payloadType !== PAYLOAD_TYPE.RAW) { - payload.availability[0].value_template = - "{{'true' if value_json.value else 'false'}}" - } - payload.availability_mode = 'all' + this._availabilityConfig(node, payload) if ( ['binary_sensor', 'sensor', 'lock', 'climate', 'fan'].includes( @@ -2391,6 +2370,36 @@ export default class Gateway { } } + private _availabilityConfig( + node: ZUINode, + payload: { [key: string]: any }, + ) { + // Set availability config using node status topic, client status topic + // (which is the LWT), and driver status topic + payload.availability = [ + { + payload_available: 'true', + payload_not_available: 'false', + topic: this.mqtt.getTopic(this.nodeTopic(node)) + '/status', + }, + { + topic: this.mqtt.getStatusTopic(), + value_template: + "{{'online' if value_json.value else 'offline'}}", + }, + { + payload_available: 'true', + payload_not_available: 'false', + topic: this.mqtt.getTopic('driver/status'), + }, + ] + if (this.config.payloadType !== PAYLOAD_TYPE.RAW) { + payload.availability[0].value_template = + "{{'true' if value_json.value else 'false'}}" + } + payload.availability_mode = 'all' + } + /** * Get the Hass discovery topic for the specific node and hassDevice */ From ed4dad4a13cfe46a07d40e059718cbff25df53f1 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 9 Jan 2024 10:47:19 -0700 Subject: [PATCH 4/4] rename new method --- api/lib/Gateway.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/lib/Gateway.ts b/api/lib/Gateway.ts index b51a1c0c15..637d80da74 100644 --- a/api/lib/Gateway.ts +++ b/api/lib/Gateway.ts @@ -931,7 +931,7 @@ export default class Gateway { // Set device information using node info payload.device = this._deviceInfo(node, nodeName) - this._availabilityConfig(node, payload) + this.setDiscoveryAvailability(node, payload) hassDevice.object_id = utils .sanitizeTopic(hassDevice.object_id, true) @@ -1673,7 +1673,7 @@ export default class Gateway { payload.command_topic = setTopic || getTopic + '/set' } - this._availabilityConfig(node, payload) + this.setDiscoveryAvailability(node, payload) if ( ['binary_sensor', 'sensor', 'lock', 'climate', 'fan'].includes( @@ -2370,7 +2370,7 @@ export default class Gateway { } } - private _availabilityConfig( + private setDiscoveryAvailability( node: ZUINode, payload: { [key: string]: any }, ) {