From e3bcfb5ea0a3256bdbe19855d20c4431b57cd398 Mon Sep 17 00:00:00 2001 From: Matt White Date: Fri, 30 Dec 2022 05:13:25 -0600 Subject: [PATCH] mqtt: add option to publish states to individual mqtt topics This change allows the user to choose the strategy for publishing klipper states to MQTT. The original strategy where all state updates are published to a common topic is still the default, but can be turned off using the "publish_combined_status" config option. The newly added strategy is publishing individual state updates to separate mqtt topics. It is disabled by default, and can be enabled with the "publish_split_status" config option. Signed-off-by: Matt White --- docs/configuration.md | 23 +++++++++++++++++++++-- moonraker/components/mqtt.py | 18 ++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 35c5ff7..ea8904f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1567,8 +1567,10 @@ instance_name: # The default is the machine's hostname. status_objects: # A newline separated list of Klipper objects whose state will be -# published in the payload of the following topic: -# {instance_name}/klipper/status +# published. There are two different ways to publish the states - you +# can use either or both depending on your need. See the +# "publish_split_status" options for details. +# # For example, this option could be set as follows: # # status_objects: @@ -1589,6 +1591,23 @@ status_objects: # # If not configured then no objects will be tracked and published to # the klipper/status topic. +publish_split_status: False +# Configures how to publish status updates to MQTT. +# +# When set to False (default), all Klipper object state updates will be +# published to a single mqtt state with the following topic: +# {instance_name}/klipper/status +# +# When set to True, all Klipper object state updates will be published to +# separate mqtt topics derived from the object and item in the following +# format: +# {instance_name}/klipper/state/{objectname}/{statename} +# +# The actual value of the state is published as "value" to the topic above. +# For example, if the heater_bed temperature was 24.0, this is the payload: +# {"eventtime": {timestamp}, "value": 24.0} +# It would be published to this topic: +# {instance_name}/klipper/state/heater_bed/temperature default_qos: 0 # The default QOS level used when publishing or subscribing to topics. # Must be an integer value from 0 to 2. The default is 0. diff --git a/moonraker/components/mqtt.py b/moonraker/components/mqtt.py index efd593a..92af788 100644 --- a/moonraker/components/mqtt.py +++ b/moonraker/components/mqtt.py @@ -280,6 +280,8 @@ class MQTTClient(APITransport, Subscribable): raise config.error( "Option 'default_qos' in section [mqtt] must be " "between 0 and 2") + self.publish_split_status = \ + config.getboolean("publish_split_status", False) self.client = ExtPahoClient(protocol=self.protocol) self.client.on_connect = self._on_connect self.client.on_message = self._on_message @@ -308,6 +310,7 @@ class MQTTClient(APITransport, Subscribable): self.api_request_topic = f"{self.instance_name}/moonraker/api/request" self.api_resp_topic = f"{self.instance_name}/moonraker/api/response" self.klipper_status_topic = f"{self.instance_name}/klipper/status" + self.klipper_state_prefix = f"{self.instance_name}/klipper/state" self.moonraker_status_topic = f"{self.instance_name}/moonraker/status" status_cfg: Dict[str, Any] = config.getdict("status_objects", {}, allow_empty_fields=True) @@ -726,8 +729,19 @@ class MQTTClient(APITransport, Subscribable): ) -> None: if not status or not self.is_connected(): return - payload = {'eventtime': eventtime, 'status': status} - self.publish_topic(self.klipper_status_topic, payload) + if self.publish_split_status: + for objkey in status: + objval = status[objkey] + for statekey in objval: + payload = {'eventtime': eventtime, + 'value': objval[statekey]} + self.publish_topic( + f"{self.klipper_state_prefix}/{objkey}/{statekey}", + payload, retain=True) + else: + payload = {'eventtime': eventtime, 'status': status} + self.publish_topic(self.klipper_status_topic, payload) + def get_instance_name(self) -> str: return self.instance_name