mirror of https://github.com/Hypfer/Valetudo.git
227 lines
10 KiB
Markdown
227 lines
10 KiB
Markdown
---
|
|
title: MQTT
|
|
category: Development
|
|
order: 43
|
|
---
|
|
|
|
# MQTT
|
|
|
|
Valetudo supports publishing status data and receiving commands from MQTT.
|
|
|
|
The following autodiscovery protocols are supported:
|
|
|
|
- Home Assistant
|
|
- Homie
|
|
|
|
They are both optional and mutually compatible: you can enable both at the same time or none at all.
|
|
|
|
## Main concepts
|
|
|
|
Since the MQTT code is heavily based on the capabilities system you may want to first give a look to how capabilities,
|
|
status attributes, and the robot object work.
|
|
|
|
The MQTT OOP structure is heavily influenced by the Homie convention. This page will also contain lots of references to
|
|
it, so make sure you grasp the fundamental concepts of the convention before proceeding further:
|
|
|
|
- Overview: [https://homieiot.github.io/](https://homieiot.github.io/)
|
|
- Specification: [https://homieiot.github.io/specification/spec-core-v4_0_0/](https://homieiot.github.io/specification/spec-core-v4_0_0/)
|
|
|
|
### Responsibilities
|
|
|
|
To keep the codebase maintainable and prevent entire classes of potential issues, the code tries to define and restrict
|
|
the responsibilities of each component as follows:
|
|
|
|
- `MqttHandle` and subclasses
|
|
- Provide infrastructure to receive events from status attributes and send commands to capabilities
|
|
- Manage Homie attributes
|
|
- Parse and validate incoming and outgoing payloads
|
|
- `HassComponent` and subclasses
|
|
- Provide infrastructure to allow sharing as much data as possible with `MqttHandle`s
|
|
- Manage Hass autoconfiguration payloads
|
|
- `MqttController`
|
|
- Handle the MQTT configuration
|
|
- Handle all aspects of the MQTT communication, such as connection, disconnection, publication, subscriptions
|
|
- Enforce a well-defined procedure for Homie state changes and reconfiguration
|
|
- Handle and dispatch events that can't be handled by handles directly
|
|
- `HassController`
|
|
- Act as a middleware between `MqttController` and `HassComponent`s
|
|
|
|
### Handles
|
|
|
|
Handles are subclasses of `MqttHandle`. They are designed to map exactly to the levels of the Homie convention topology.
|
|
Specifically:
|
|
|
|
- `RobotMqttHandle` ⇒ Homie device
|
|
- `NodeMqttHandle` ⇒ Homie node
|
|
- `PropertyMqttHandle` ⇒ Homie property
|
|
|
|
Two more classes are present that further extend from these and bridge the handles to Valetudo's internals:
|
|
|
|
- `RobotStateNodeMqttHandle` maps to `StatusStateAttribute`
|
|
- `CapabilityMqttHandle` maps to `Capability`
|
|
- (`RobotMqttHandle` maps to `ValetudoRobot`)
|
|
|
|
#### Handle tree and data publication
|
|
|
|
Handles are assigned into a tree structure:
|
|
|
|
- `RobotMqttHandle`, being the root handle, registers to the `MqttController`
|
|
- `NodeMqttHandle`s register to `RobotMqttHandle`
|
|
- `PropertyMqttHandle`s register to `NodeMqttHandle`
|
|
|
|
This structure simplifies and unifies publication to the MQTT broker, Instead of publishing data directly, any handle
|
|
may "refresh" itself.
|
|
|
|
When a handle is refreshed it will ask the `MqttController` to retrieve a fresh payload from it and publish it to its
|
|
designated topic.
|
|
|
|
Refreshing is recursive: whenever a handle is refreshed all children are refreshed as well. Attached Home Assistant
|
|
components will also be refreshed, but we will discuss this later.
|
|
|
|
#### The robot handle
|
|
|
|
`RobotMqttHandle` is special since it is the root handle. It maps to a Homie device. It is mainly responsible for
|
|
checking which capabilities the robot supports and registering the corresponding handles.
|
|
|
|
It will also subscribe to the robot status and register matching handles **as soon as the corresponding attributes are
|
|
added** (therefore it's normal if they don't show up until the robot is fully connected and the status is polled).
|
|
|
|
Finally, it handles the registration of `MapNodeMqttHandle`, which as the name suggests provides map data.
|
|
|
|
#### The state handles
|
|
|
|
`RobotStateNodeMqttHandle` children all map to Homie nodes. Their peculiarity is that they provide infrastructure to
|
|
subscribe to the robot state.
|
|
|
|
This is accomplished by providing a list of status attribute matchers, which the handle will subscribe to.
|
|
|
|
When a status event occurs, the handle and all its children are refreshed.
|
|
|
|
#### The capability handles
|
|
|
|
`CapabilityMqttHAndle` children also map to Homie nodes. The class itself inherits from `RobotStateNodeMqttHandle`,
|
|
therefore it is as well able to subscribe to robot status events.
|
|
|
|
It is encouraged, whenever possible, to handle status data in a capability handle as opposed to a status handle whenever
|
|
actions performed on the capability will be reflected into and match exactly to a status attribute event.
|
|
|
|
This is true, for example, for both `PresetSelectionCapability`s, since setting a preset will usually result in the same
|
|
value being reflected back to the status attribute: you send `low` and you get back `low`.
|
|
|
|
This is NOT true, for example, for `BasicControlCapability`. While actions performed on it will directly affect
|
|
`StatusStateAttribute`, the status event won't match exactly: you perform `START` but you get back `CLEANING`.
|
|
|
|
#### The property handles
|
|
|
|
`PropertyMqttHandle`, as the name suggests, maps to Homie properties. However, unlike the previously discussed handles,
|
|
property handles do not have subclasses and should not be subclassed.
|
|
|
|
Property handles are in fact defined and registered in-line in the capability/status handle constructors. All data is
|
|
provided as parameters to the constructor.
|
|
|
|
These handles handle the actual data to be published and receive commands. However, they never interact directly with
|
|
the MQTT client. They instead must be provided with at least one of the following callbacks: a `getter` to retrieve
|
|
fresh data to be published; a `setter` to perform operations with the data received from MQTT.
|
|
|
|
When a `setter` callback is provided, property handles will be subscribed to a `/set` topic.
|
|
|
|
When a `setter` is provided but a `getter` is not provided, the property will act as a *command property* according to
|
|
the Homie convention: the data received in the `/set` topic will automatically be reflected back to the main topic to
|
|
acknowledge the command.
|
|
|
|
### Home Assistant components
|
|
|
|
Home Assistant uses a very different workflow. Instead of defining a structure, it defines components which may or may
|
|
not map to some capabilities' behavior.
|
|
|
|
In some instances it will try to adapt to an existing MQTT structure and allow you to provide topics and payloads for
|
|
accomplishing different tasks. In some other cases it will try to impose its own structure.
|
|
|
|
This makes it difficult to share data with the previously defined structure. However, Valetudo provides enough
|
|
abstraction to make this easier: `HassComponent`.
|
|
|
|
Home Assistant components may subscribe to topics. However, this should be avoided when possible: most features can and
|
|
should be implemented by providing Hass with the handle topics.
|
|
|
|
#### Components and the handle tree
|
|
|
|
Components are not handles, they are a separate entity. However, in order to share data with handles, they are attached
|
|
to and managed by the handle they share information with.
|
|
|
|
Components may be attached to any type of handle, from the robot handle to the property handle. For instance, the map
|
|
component is attached to the map handle.
|
|
|
|
Whenever an handle is refreshed, the attached Hass components are refreshed as well.
|
|
|
|
#### Sharing topics and data through anchors
|
|
|
|
Some components are trickier. For example, the `VacuumHassComponent` is attached to the robot handle since that's what
|
|
it shares most data with. However, it also needs access to the fan speed and a reference to the `BasicControlCapability`
|
|
command topic in order to send cleaning control commands.
|
|
|
|
This is accomplished using `HassAnchor`.
|
|
|
|
`HassAnchor` is a utility to share data from the handle that manages some type of information to the hass component that
|
|
needs it.
|
|
|
|
There are two types of `HassAnchor`: plain anchors and topic references. The implementation is exactly the same, the
|
|
distinction has been put in place, once again, to separate responsibilities: "plain" anchors may only be used in
|
|
payloads, topic references may only be used in autoconfiguration payloads.
|
|
|
|
`HassAnchor`s can be thought of as fancy "template variables". Hass component payloads are in fact provided as regular
|
|
JavaScript objects. For any value that is not immediately available, an `HassAnchor` may be retrieved and used in its
|
|
place.
|
|
|
|
Of course, the value isn't going to jump into existence on its own: some other component needs to provide it. This is a
|
|
responsibility of handles: whenever they share some value with a hass component, they should also `post()` it into the
|
|
anchor every time they retrieve it for their own needs.
|
|
|
|
`HassAnchor`s are eventful: whenever a handle posts a value into an anchor, the hass component that uses it is
|
|
automatically refreshed and its payload published to MQTT.
|
|
|
|
If for whatever reason an anchor doesn't hold any value when the hass component payload is requested for publication,
|
|
the publication will be delayed. This means that **you should only use anchors if you are sure they will be populated**,
|
|
otherwise the hass component will stay in a limbo forever.
|
|
|
|
## Dos and don'ts
|
|
|
|
Here's a bunch of things to keep in mind when adding new MQTT handles and Home Assistant components.
|
|
|
|
- **Don't** try to bypass the API restrictions: if you find it hard to get something done you're probably not doing it
|
|
correctly
|
|
- **Do** suggest and discuss API changes that accommodate your use case
|
|
|
|
----
|
|
|
|
- **Don't** implement something only for Homie or for Home Assistant
|
|
- **Do** take your time and do things properly
|
|
|
|
----
|
|
|
|
- **Don't** abuse `HassAnchor`
|
|
- **Do** try to define your hass component close to its handle, so that most data you need will be in the same scope
|
|
|
|
----
|
|
|
|
- **Don't** link Home Assistant fields to Homie `$attributes` - they won't be available if Homie is disabled
|
|
- **Do** define a payload for the Home Assistant component with all the data you need, using anchors if needed
|
|
|
|
## Troubleshooting
|
|
|
|
This section does not describe general MQTT troubleshooting, but rather troubleshooting of problems that can occur when
|
|
writing new code.
|
|
|
|
### A status/capability handle does not get published
|
|
|
|
Status handles are only published once the `StatusAttribute` they subscribe to first appears. If it does appear, but the
|
|
handle isn't published, you may have an incorrect attribute matcher.
|
|
|
|
Both status and capability handles have to be registered into the designated lists inside the `HandleMappings.js` file
|
|
for them to be loaded.
|
|
|
|
### Anchors are not updating but handles are
|
|
|
|
You can enable `debug.debugHassAnchors` in the configuration and set the log level to `trace`. It will print a report
|
|
whenever an anchor is blocking publication for a Hass component.
|
|
|