WomoNET Protocol
Das WomoNET-Netzwerk verwendet MQTT als primäres Protokoll für die Kommunikation zwischen verschiedenen Geräten. Diese Einrichtung ermöglicht effizienten und flexiblen Datenaustausch innerhalb des Netzwerks.
Struktur
Die MQTT-Topics im WomoNET-Netzwerk sind systematisch in die folgenden Hauptkategorien gegliedert:
c (config)
conf (Konfiguration)
Dieses Topic speichert Konfigurationseinstellungen, einschließlich Benutzeroberflächeneinstellungen, Geräteparametern und anderen wichtigen Konfigurationen. Das Hauptmerkmal dieses Topics ist, dass die Einstellungen persistent sind, was bedeutet, dass sie auch nach einem Systemneustart erhalten bleiben.
- ui: Enthält UI-Layout von der WomoNET.app. Layouts werden als JSON für jedes verfügbare Layout geschrieben
- driver: Kann verwendet werden, um Konfigurationen für Treiber zu speichern
dev (Virtuelle Geräte, Auto-Discovery)
Dieses Topic ist für Treiber reserviert, um ihre Auto-Discovery-Konfiguration zu veröffentlichen. Die Konfigurationsdaten für die Geräteerkennung werden hier definiert, wie unten detailliert beschrieben.
s (state/Zustand)
Jeder MQTT-Client innerhalb des WomoNET-Netzwerks hat ein dediziertes Zustands-Topic, wo er Nachrichten veröffentlicht, die seinen aktuellen Zustand oder Daten widerspiegeln. Alle Daten müssen menschenlesbare UTF-8-Daten sein. Numerische Werte müssen als UTF-8-String gesendet werden. Dezimaltrennzeichen ist .
(Punkt). Wir parsen mit Dart’s double.tryParse.
Zustandsupdates und Befehle sollten QoS 1
verwenden.
Struktur
Der Zustandsbaum ist wie folgt strukturiert: s/<client_id>/<device_id>/<object_id>/[set/]<state/value/command>
, wobei:
client_id
ist die MQTT-Client-IDdevice_id
ist die Kennung des virtuellen Gerätsobject_id
ist die Objekteinheit eines Typ-Wertsset
(optional) ist eine Konstante, die nur bereitgestellt wird, wenn dies ein Befehl iststate/value/command
ist der tatsächliche Zustand, Wert oder Befehl
Offene Fragen
- TODO Fehlerbehandlung: Wir könnten
s/error/<dev-id>
für Fehlermeldungen einführen - TODO Geräte gehen Offline: Last Will Messages für Clients. Verfügbarkeit für Gruppen aktiv pushen? Ideen:
- Behaltene Auto-Discovery-Nachrichten löschen
available_t
für jeden Auto-Discovery-Typ
Auto-Discovery
Die Konfigurationsdaten müssen als MQTT-Nachrichten in einem spezifischen JSON-Format veröffentlicht werden. Jede Entität benötigt einen eindeutigen Topic-Namen und eine JSON-Konfiguration. Das Topic-Format ist c/dev/<client_id>/<device_id>/<type>/<entity>
, wobei:
<client_id>
ist die MQTT-Client-ID (global eindeutig)<device_id>
ist eine Kennung für das virtuelle Gerät (Treiber-Ebene). Diese ID muss für den Client eindeutig sein, um Kollisionen zu vermeiden<type>
ist der Typ der Entität. Siehe unten für verfügbare Typen<entity>
ist der Name der Entität. Er muss für alle Entitäten des gegebenen Typs dieses virtuellen Geräts eindeutig sein
Beim Veröffentlichen von Auto-Discovery-Konfigurationen muss das retain
-Flag für die MQTT-Nachricht gesetzt werden.
Auto-Discovery ist anders organisiert als bei Home Assistant: Wir verwenden dev_id
und dann type
anstatt umgekehrt. Dies ermöglicht es uns, WidgetGroups
in der App für jede dev_id
zu verwenden.
Verfügbare Typen
toggle
Eine schaltbare Einheit, die ein- oder ausgeschaltet werden kann.
Name | Beschreibung |
---|---|
state_t | Topic mit dem aktuellen Zustand, nur true und false sind erlaubt |
cmd_t | Befehls-Topic zum Setzen des Toggle-Zustands. false , 0 , off (alle groß-/kleinschreibungsunabhängig) werden als false interpretiert, alles andere als true . Wenn allow_timed true ist, wird das Setzen dieses Werts einen laufenden Timer abbrechen |
allow_timed | true oder false , gibt an, ob der Wert zeitgesteuert ausgelöst werden kann, indem ein CSV-String geschrieben wird, der den Zustand und die Dauer in Millisekunden repräsentiert (z.B. true,5000 , Ausgang für 5000 Millisekunden einschalten). Wenn der zeitgesteuerte Zustand bereits der gewünschte Zustand ist (z.B. true,5000 wird gesendet, aber der Zustand ist bereits true ), wird der Befehl ignoriert |
actuator
Ein Aktuator, der in zwei Richtungen geschaltet werden kann, z.B. schieben/ziehen, vorwärts/rückwärts und hat auch einen neutralen Stopp-Zustand. Wichtig: Der Treiber muss sicherstellen, dass die Bewegung automatisch stoppt, wenn keine gültigen Nachrichten mehr empfangen werden.
Name | Description |
---|---|
state_t | Topic holding the numeric value representing the actuator’s state. Can be 1 (push/forward), 0 (neutral/stop), or -1 (pull/reverse) |
cmd_t | Command topic for setting the actuator’s state. Can be 1 (push/forward), 0 (neutral/stop), or -1 (pull/reverse). If allow_timed is true , setting this value will cancel any running timer |
allow_timed | Optional (defaults to false ). true or false , representing whether the value can be triggered in a timed manner by writing a CSV string representing the state and the duration it should be held in milliseconds (e.g., -1,5000 , setting the direction to pull on for 5000 milliseconds). If the timed state is already the requested state (e.g -1,5000 is sent, but the state is already -1 ), the command is ignored |
binary
A boolean sensor with states on and off.
Name | Description |
---|---|
state_t | Topic holding the current state, only true and false are allowed |
sensor
A sensor publishing a value.
Name | Description |
---|---|
state_t | Topic holding the value. The value is UTF-8 (e.g 42.1234 or heating ) |
unit_of_measurement | Optional. Specifies the unit of the sensor’s value for display purposes. Examples include °C for temperature, % for humidity, m/s for speed, kWh for energy, etc. If not provided, the value is treated as unitless. Use standardized units (SI) whenever possible to ensure compatibility |
mode
Selectable options for a dropdown.
Name | Description |
---|---|
state_t | Topic holding the current mode. The value is a UTF-8 string representing the current selection (e.g. heat , cool , auto , etc.) |
cmd_t | Topic used to send commands to change the mode. The value should be a UTF-8 string corresponding to one of the selectable options (e.g. heat , cool , off , etc.). NOTE: for translations, we could define common english options and provide translations via a lookup table for them. |
options | List of available modes for the dropdown, provided as a JSON array of strings (e.g. ["off", "heat", "cool", "auto"] ). User interfaces should respect the order of the options |
slider
A controllable level value (slider in UI), which can be changed between a min and max value with a specified number of steps, e.g. for controlling heater/cooler temperatures, light dimmers, power consumption, charger output, …
Name | Description |
---|---|
state_t | Topic holding the current value |
cmd_t | Topic used to send commands for setting the target value |
min_value | The minimum value for the slider |
max_value | The maximum value for the slider |
step_size | The size of a step, e.g. 0.5 |
Example Structure
c/
├── conf/
│ ├── ui/
│ │ ├── 1 = "{ layout JSON }"
│ │ ├── main = "{ layout JSON }"
│ │ └── heater_only = "{ layout JSON }"
│ └── driver/
│ ├── core-gpio = "{ driver configuration JSON }"
│ └── core-adc = "{ driver configuration JSON }"
└── dev/
├── womonet-core-123.../
│ ├── adc/
│ │ └── sensor/
│ │ ├── vin0 = "{'unit_of_measurement': 'V', 'state_t': 's/womonet-core-123.../adc/0/state'}"
│ │ ├── vin1 = "{'unit_of_measurement': 'V', 'state_t': 's/womonet-core-123.../adc/1/state'}"
│ │ └── ...
│ ├── extrelay/
│ │ └── toggle/
│ │ ├── relay0 = "{'state_t': 's/womonet-core-123.../extrelay/0/state', 'cmd_t': 's/womonet-core-123.../extrelay/0/set/state', 'allow_timed': true}"
│ │ ├── relay1 = "{'state_t': 's/womonet-core-123.../extrelay/1/state', 'cmd_t': 's/womonet-core-123.../extrelay/1/set/state', 'allow_timed': true}"
│ │ └── ...
│ ├── truma-d4e-651.../
│ │ ├── mode/
│ │ │ └── heat = "{'state_t': 's/womonet-core-123.../truma-d4e-651.../heat/state', 'cmd_t': 's/womonet-core-123.../truma-d4e-651.../heat/set/state', 'options': '[\"off\", \"heating\", \"venting\"]'}"
│ │ ├── sensor/
│ │ │ ├── current_temp = "{'state_t': 's/womonet-core-123.../truma-d4e-651.../current_temp/state'}"
│ │ │ └── boiler_temp = "{'state_t': 's/womonet-core-123.../truma-d4e-651.../boiler_temp/state'}"
│ │ └── slider/
│ │ └── target_temp = "{'state_t': 's/womonet-core-123.../truma-d4e-651.../target_temp/state', 'cmd_t': 's/womonet-core-123.../truma-d4e-651.../target_temp/set/state', 'min_value': 16, 'max_value': 36, 'step_size': 0.5}"
│ └── ...
└── womonet-power-524.../
├── toggle/
│ └── pump = "{'state_t': 's/womonet-power-524.../pump/state', 'cmd_t': 's/womonet-power-524.../pump/set/state'}"
├── actuator/
│ ├── step = "{'state_t': 's/womonet-power-524.../step/state', 'cmd_t': 's/womonet-power-524.../step/set/state', 'allow_timed': true}"
│ ├── awning = "{'state_t': 's/womonet-power-524.../awning/state', 'cmd_t': 's/womonet-power-524.../awning/set/state'}"
│ ├── valve0 = "{'state_t': 's/womonet-power-524.../valve0/state', 'cmd_t': 's/womonet-power-524.../valve0/set/state', 'allow_timed': true}"
│ └── valve1 = "{'state_t': 's/womonet-power-524.../valve1/state', 'cmd_t': 's/womonet-power-524.../valve1/set/state', 'allow_timed': true}"
└── ...
s/
├── womonet-core-123.../
│ ├── adc/
│ │ ├── 0/
│ │ │ └── state = "1.33"
│ │ ├── 1/
│ │ │ └── state = "0.452"
│ │ └── ...
│ ├── extrelay/
│ │ ├── 0/
│ │ │ ├── state = "ON"
│ │ │ └── set/
│ │ │ └── state (writable only, no retain)
│ │ ├── 1/
│ │ │ ├── state = "OFF"
│ │ │ └── set/
│ │ │ └── state (writable only, no retain)
│ │ └── ...
│ ├── truma-d4e-651.../
│ │ ├── heat/
│ │ │ ├── state = "heating"
│ │ │ └── set/
│ │ │ └── state (writable only, no retain)
│ │ ├── current_temp/
│ │ │ └── state = "18.62"
│ │ └── target_temp/
│ │ ├── state = "22.0"
│ │ └── set/
│ │ └── state (writable only, no retain)
│ └── ...
└── womonet-power-524.../
├── pump/
│ ├── state = "ON"
│ └── set/
│ └── state (writable only, no retain)
├── step/
│ ├── state = "0"
│ └── set/
│ └── state (writable only, no retain)
└── ...
Cloud Connectivity
For cloud connectivity, a service is syncing the desired topics (states/
) between the local and a cloud broker. This is only done when a sub topic in sync_requested
exists. Each client connecting to the cloud broker writes its name into the sync_requested
topic and defines a Last Will Message to delete it.
When the last client deletes its sync topic, the broker in the cloud deletes all state topics, after a predefined timeout. (Maybe by not accepting the retain flag)
An active connection between the local and cloud broker is signaled by a flag synced
(with a timestamp of the last message from the local broker.)
All states should be synced in a given timeframe to ensure consistency between local and cloud environments.