appdaemon icon indicating copy to clipboard operation
appdaemon copied to clipboard

Documentation Enhancement: A working MQTT example app

Open rct opened this issue 2 years ago • 3 comments
trafficstars

Is there an existing feature request for this?

  • [X] I have searched the existing issues

Your feature request

Unless I've missed something, there isn't an example of a working MQTT app. I don't see any MQTT examples in: https://github.com/AppDaemon/appdaemon/tree/dev/conf/example_apps

There are snippets in the documentation, but not the full picture of what is needed in one place to have a working example. If one is considering using AppDaemon for the first time, it might dissuade them from choosing AppDaemon.

Based on some of the questions I've seen when searching, a good example MQTT AppDaemon app would:

  • Subscribe to multiple (sub)topics or a wildcard topic
    • Be able to distinguish which topic a message was received for
  • Maintain an internal state for a device that has subtopics: .../temperaure, .../humidity, .../battery, etc.
  • Do "something"
    • Calculate some stats: high, low, average, message counts, etc. and publish it back to MQTT.
    • publish a single JSON state record with all the subtopics in one record.

rct avatar Jul 15 '23 17:07 rct

I wasn't able to get anything working deriving from mqtt.Mqtt as in the snippet in the MQTT API reference. So I would have definitely found a working example in the docs very useful.

Following some examples in the HomeAssistant community forums, I came up with this working app. It doesn't cover all of what rct suggests, but could potentially be adjusted to be a useful example in the docs:

import json
from typing import Dict

import hassapi as hass


_MQTT_PLUGIN_NAMESPACE = "mqtt"  # matches appdeamon.yaml


class RemoteTemp(hass.Hass):
  """Forward a AM2301 temperature reading to a Tasmota MiElHVAC.

  This allows using a remote temperature sensor with a heat pump.
  """

  def initialize(self) -> None:
    self._mqtt = self.get_plugin_api("MQTT")
    self._sensor_topic = self.args["sensor_topic"]
    self._command_topic = self.args["command_topic"]
    self._mqtt.mqtt_subscribe(self._sensor_topic, namespace=_MQTT_PLUGIN_NAMESPACE)
    self._mqtt.listen_event( self._temperature_received, "MQTT_MESSAGE", topic=self._sensor_topic)

  def _temperature_received(
    self, event: str, data: Dict, unused_kwargs: Dict
  ) -> None:
    # Example MQTT message:
    # tele/tasmota_884477/SENSOR {"Time":"2023-11-09T22:11:05","Switch1":"OFF","Switch2":"OFF","Switch3":"OFF","Switch4":"OFF","AM2301":{"Temperature":22.2,"Humidity":35.5,"DewPoint":6.2},"TempUnit":"C"}
    payload = json.loads(data["payload"])
    temperature_c = payload["AM2301"]["Temperature"]
    unit = payload["TempUnit"]
    if unit != "C":
      self.log(f"Temperature from {self._sensor_topic} has unit {unit}, not C.")
    self._mqtt.mqtt_publish(
      self._command_topic,
      # Round the temperature value since the MiElHVAC Tasmota driver truncates
      # to an integer value.
      payload=str(round(temperature_c)),
      namespace=_MQTT_PLUGIN_NAMESPACE,
    )

markfickett avatar Nov 10 '23 00:11 markfickett