Überlegungen zur MQTT-API

Hallo,
Wie hier besprochen https://community.gladysassistant.com/t/mqtt-comment-fonctionne-le-module-mqtt/5000/47

Ich erlaube mir, ein Thema zu eröffnen, um über die Implementierung der MQTT-API von Gladys zu diskutieren.
Ich denke, wir sollten uns hier darauf beschränken, nur über die Schnittstelle zu diskutieren, die wir den Benutzern/Geräten anbieten möchten, anstatt über Module oder Kompatibilität. Das sollte die Klarheit erhöhen.

Zur Erinnerung: Die aktuelle Implementierung ist hier dokumentiert: Private, self-hosted smart home with AI | Gladys Assistant

Ich werde versuchen, die Ideen/Fragen/Entscheidungen, die durch diese Diskussion entstehen, unten zu referenzieren, damit ein Neuling eine Idee von den besprochenen Dingen hat, ohne alle Nachrichten lesen zu müssen :wink:


Warum eine API definieren?

Eine API ermöglicht es, Kommunikationsregeln zwischen Gladys und Drittanbieter-Clients zu definieren.

Sie ermöglicht es, eine Reihe von möglichen Aktionen über das MQTT-Protokoll zu definieren sowie die Art und Weise, wie die Daten formatiert werden müssen.

Was wollen wir?

  • Die meisten Interaktionen zwischen Gladys und jedem Gerät, das damit verbunden sein könnte, ermöglichen:
    • Den Zustand eines Objekts mit einer Nachricht von Gladys ändern
    • Den Zustand eines Objekts an Gladys von einem Objekt zurückmelden
  • So marken-/modellunabhängig wie möglich sein
  • Die Kommunikation zwischen Objekten / PODS / Hauptinstanz ermöglichen
  • « IoT-Standards » (falls es welche gibt :slight_smile: ) respektieren

Bestätigte Ideen

Derzeit leer

Abgelehnte Ideen

Derzeit leer


Hinweis: Ich habe natürlich nicht die Autorität, all das zu definieren, daher sind die in Warum eine API definieren? und Was wollen wir? genannten Ideen dazu bestimmt, geändert zu werden. Man muss irgendwo anfangen :wink:

Mein erster Eindruck beim Lesen der API-Dokumentation betrifft die Themen, die sich auf die Gerätefunktionen beziehen.
Ich bin nicht besonders begeistert von der aktuellen Implementierung in zwei Punkten:

  • Es gibt keine Unterscheidung zwischen den « eingehenden » und « ausgehenden » Themen. Wir wissen, dass ein Gerät seinen neuen Zustand auf gladys/master/device/state/update veröffentlichen muss, aber auf welchem Thema sollte es zuhören, um seinen Zustand abzurufen? Diese Themen sollten standardisiert sein.
  • Die IDs der Objekte werden im Payload und nicht in der URL übergeben. Wenn ich also Updates eines einzelnen Geräts abonnieren möchte… nun, das geht nicht :confused: Alle Geräte sollten einem Thema wie « device/update » abonnieren und im Payload nachschauen, ob die Nachricht sie betrifft. Das erscheint mir als Anti-Pattern.

Ein Vorschlag, der diese beiden Punkte berücksichtigen könnte:
Wenn ich ein Objekt dieser Art habe:

{
  "name": "Living room connected object",
  "external_id": "custom:12",
  "should_poll": false,
  "features": [
    {
      "external_id": "custom:12:temperature:1",
      "name": "temperature",
      "category": "temperature-sensor",
      "type": "decimal",
      "read_only": false,
      "has_feedback": true,
      "min": -50,
      "max": 80
    },
    {
      "external_id": "custom:12:onOff:1",
      "name": "On/Off",
      "category": "light",
      "type": "binary",
      "read_only": false,
      "has_feedback": true,
      "min": 0,
      "max": 1
    }
  ]
}

Vorschlag für Themen und Payloads:

  • Das Gerät veröffentlicht seinen Zustand
thema: gladys/master/device/custom:12/feature/custom:12:temperature:1
payload: 19.8

thema: gladys/master/device/custom:12/feature/custom:12:onoff:1
payload: 1
  • Gladys veröffentlicht Änderungen, damit das Gerät reagieren kann
thema: gladys/device/custom:12/feature/custom:12:temperature:1 // Adresse unnötig, da man die Temperatur am Gerät nicht setzen kann :D

thema: gladys/device/custom:12/feature/custom:12:onoff:1
payload: 0

Dadurch kann mein Gerät einem oder mehreren Themen abonnieren, je nachdem, was es verarbeiten kann:

gladys/device/custom:12/feature/custom:12:temperature:1
gladys/device/custom:12/feature/custom:12:onoff:1
gladys/device/custom:12/feature/#

Und Gladys hat nur ein einziges für alle Geräte:

gladys/device/#

Ich denke, es ist notwendig, die Wildcards zu berücksichtigen, die das Protokoll bietet.
Sie werden sagen, ja, aber das sind am Ende viele Themen!
Ja, und? Wir haben die Wahl… mehr Themen oder mehr Listener auf demselben Thema.
Ich glaube, die « Norm » im IoT ist es, die Themen nach Gerät zu unterscheiden. Ein Argument dafür ist, dass es nicht die Aufgabe des Objekts ist, den Inhalt der empfangenen Nachrichten zu unterscheiden. Man spart sich außerdem die Last eines JSON.encode / JSON.decode auf der Seite des verbundenen Objekts.
Man könnte sich auch vorstellen, dass man die Zugriffe auf Themen aus Sicherheitsgründen unterscheidet und einem nicht autorisierten Objekt nicht erlaubt, auf einem Thema zu veröffentlichen, das es nicht betrifft.

Was denken Sie?

Hallo @Boimb! Super, dass du die Initiative ergriffen hast, dieses Thema zu erstellen :slight_smile:

Die in der Dokumentation aufgeführten Themen sind nur eingehende Themen! Wir verwalten derzeit keine ausgehenden MQTT-Ströme :slight_smile:

Es gibt eine Logik in der aktuellen API.

Ein Beispiel zum Thema, über das du gesprochen hast:

Ich stimme dir zu, dass das Übertragen der external_id im Thema Sinn macht und es sauberer ist.

Mir gefällt die Implementierung, die du vorschlägst! Der Gedanke, das json.encode/decode auf der Geräteseite zu entfernen, ist clever.

Ich denke, du hast es mit dem Gerät vertauscht. Gladys hört das, was für sie bestimmt ist, also « gladys/master » :slight_smile:

Dann gibt es Themen, bei denen wir gezwungen sind, JSON-Code beizubehalten, ich denke da zum Beispiel an device.create:

Falls du Zeit hast, um mit der Erstellung einer etwas formellen Dokumentation zu beginnen oder sogar eine Implementierung vorzuschlagen, wäre das super :smiley:

Entschuldigung für die mehrfachen Antworten, ich denke darüber nach und notiere hier die Ideen, die mir kommen :smiley:

In deinem Vorschlag gladys/master/device/custom:12/feature/custom:12:temperature:1 zum Beispiel gibt es im Thema keine Erwähnung, was man erstellt/aktualisiert.

Wenn ich 19.8 im Thema veröffentliche, was ist 19.8? Es ist ein neuer Zustand! Aber im Namen des Themas weiß man nicht, welches Attribut von diesem Update betroffen ist. (Okay, wir wissen es, weil wir das Thema codieren, aber der Name des Themas sagt nichts dazu)

Wenn wir der hier gewählten Logik folgen, müsste man dem Thema ein letztes Suffix hinzufügen, wie z.B. « state » oder « last_value », wenn man das exakte Attribut in der DB nimmt, um etwas wie Folgendes zu haben:

gladys/master/device/custom:12/feature/custom:12:temperature:1/last_value

Was denkst du darüber?

Ich denke, dass man in Zukunft bei anderen Arten von Themen vielleicht ein Thema haben möchte, das das gesamte JSON-Objekt übernimmt und das gesamte Objekt in der DB aktualisiert (z.B. in dem Fall, dass sich das entfernte Gerät aktualisiert hat und nun mehr Funktionen verwaltet, es kann sein « device » mit einem neuen Gerät aktualisieren)

Hallo,

ich erlaube mir, mich in das Thema einzubringen, da MQTT zu 99% das Herzstück meiner Verwaltung ist.
Hier ist meine funktionierende Konfiguration auf Gladys v3 seit 2 Jahren:
Arduino (Gerät) → Gladys = Aktualisierung der Sensoren:
Topic = gladys/master/device/state/update/Arduino01_garage/Électricité Garage
Payload = {« Tension moyenne »:231 , « Intensite totale »:11.6 , « Puissance »:2691.2 }
Aufschlüsselung des Topics:

  • gladys/master/device/state/update = Topic, das von Gladys für die Aktualisierung der Werte verfolgt wird
  • Arduino01_garage = Name des Arduino in seinem Programm, um nicht mit Nachrichten überflutet zu werden, die an andere Arduinos im Befehlstopic (unten) gesendet werden. Es ist Teil der Geräte-ID.
  • Électricité Garage = Name des Geräts, ebenfalls in seiner Kennung enthalten: name = Électricité Garage identifier = Arduino01_garage/Électricité Garage
  • Tension moyenne = identifier des DeviceTypes (Feature in V4).

Gladys → Arduino (Gerät) = Steuerung der Geräte:
Topic = gladys/master/device/state/scan/Arduino01_garage/Eclairage Garage
Payload = {« Eclairage Garage »:1 }

Aktuell in V4 muss ich für die Aktualisierungen eine Nachricht pro Feature senden, während in der v3 eine Nachricht für derzeit 3 bis 6 DeviceTypes ausreicht. Ich hoffe, das hilft euch weiter.

Danke @Terdious für dein Feedback!

Super interessante Anmerkung :slight_smile: Das eine schließt das andere nicht aus, man könnte ein Topic pro Gerät haben, das es ermöglicht, mehrere Features zu aktualisieren (zusätzlich zum anderen Topic pro Feature)

topic: gladys/master/device/:external_id/last_values

body:

{
  "feature_1_external_id": 1,
  "feature_2_external_id": 1,
}

Bei last_values bin ich mir nicht sicher, man kann sich dafür entscheiden, im API den Singular beizubehalten, aber man muss sehen, ob es für den Entwickler klar ist, dass es sich um mehrere Daten handelt.

@Boimb Was denkst du?

Salut @pierre-gilles @Terdious, cool, Feedback :smiley:
Ich werde versuchen, meine Eindrücke zu den angesprochenen Punkten zu teilen.

Ja. Wir sind uns einig, dass dies der ganze Sinn von MQTT ist, auch Push durchführen zu können. Daher müssen die Topics ad hoc definiert werden.

Ja, ja, natürlich! Ich nehme an, dass dieser Thread dazu da ist, sie zu validieren/zu verbessern.

Mein Fehler. Klar, die Idee wäre, dass Glady alles hört, was sie betrifft
gladys/master/device/# oder sogar nur gladys/master/# aber ich sehe keinen Anwendungsfall (vielleicht später für die Pods)

Tatsächlich scheint das dafür notwendig zu sein. Dennoch denke ich, dass wir es, wie es heute der Fall ist, ermöglichen sollten, auch die Geräte über die Gladys-Benutzeroberfläche zu erstellen und Funktionen hinzuzufügen (dieses Topic sollte nicht das einzige Mittel sein, um ein MQTT-Gerät hinzuzufügen, aber es muss eine Lösung sein)

Ja, und das ist Absicht. Wird das Gerät über dieses Topic etwas anderes als eine Aktualisierung des Status der betreffenden Funktion weiterleiten? Das heißt, es ist nicht blockierend, die Erwähnung des Status hinzuzufügen, also warum nicht.

Ich bin nicht besonders begeistert von der Nomenklatur des Topics, aber ich teile die Meinung von PG:

Nach diesen ersten Austauschen und dem, was ich über die verschiedenen Arten der Protokollanwendung bei den verschiedenen Herstellern gelesen habe, werden wir wahrscheinlich nicht in der Lage sein, eine API zu definieren, die mit allen Herstellern oder Anbietern von Open-Source-Firmware (Tasmota, ESPEasy) kompatibel ist.

Eine Lösung, die wir erkunden könnten, wäre das Weiterleiten, d. h. die Möglichkeit für den Gladys-MQTT-Client, Nachrichten an Topics weiterzuleiten, die er ebenfalls hört. Ich erkläre:

Wir definieren eine einzige und einzige Gladys-MQTT-API auf der Grundlage unserer ersten Überlegungen (die wir verfeinern und validieren). Zum Beispiel für die Aktualisierung einer Funktion:

topic: gladys/master/device/external_id/feature/external_id/state
payload: stateValue

// Oder auch
topic: gladys/master/device/external_id 
payload: { features: [
  {
    "external_id": feature_external_id,
    "state": stateValue
  }
]

Dadurch haben wir eine klare und präzise API ohne Mehrdeutigkeiten, die keine Raketenwissenschaft erfordert, um sie zu implementieren.

Für die Fälle, in denen wir nicht die Kontrolle haben, wie der MQTT-Dienst der nicht geflashten Shelly-Geräte. hier die Dokumentation Die Integration besteht aus dem Weiterleiten der „eigenen“ Topics. Zum Beispiel:

topic: shellies/shelly1-<deviceid>/relay/0
payload: "off"
// Weitergeleitet an
gladys/master/device/shelly:deviceid/feature/deviceid:onOff:1
payload: 0

Auf diese Weise vermehren wir nicht die „öffentlichen“ Routen der API, und dennoch sind wir in der Lage, „nicht konforme“ Geräte zu verwalten. Abgesehen davon ist die Integration nichts anderes als die Hinzufügung eines Weiterleiters. Man kann sich sogar vorstellen, dass das MQTT-Modul, wenn es eine Nachricht auf einem proprietären Topic erhält, in der Lage ist, das zugehörige Gerät zu erstellen, wenn es nicht in der Datenbank existiert, bevor es weitergeleitet wird.

Aber natürlich! Ich hatte die ausgehenden Routen einfach aus Zeitmangel nicht definiert. In der v4 nimmst du dir die Zeit, die Dinge richtig zu definieren und es richtig zu machen, ich bevorzuge es, etwas nicht zu entwickeln, anstatt es schnell und schlecht zu entwickeln :slight_smile:

Ich bin einverstanden, meiner Meinung nach ist es besser, Entwicklern von MQTT-Code zu empfehlen, den Benutzern zu sagen, dass sie die Geräte in der UI erstellen sollen (wie es bei den anderen Diensten der Fall ist), sonst gibt es, sobald es im Hintergrund abläuft, keine Rückmeldung für den Benutzer, ob es erfolgreich oder fehlgeschlagen ist. Und wenn es fehlschlägt, kann er außer dem Ansehen der Protokolle (was wir in der v4 nicht wollen) nichts tun.

Für dieses Topic nein, aber für andere Topics ja. Und da wir die gleichen Namenskonventionen überall beibehalten wollen, um konsistent zu bleiben, wenn wir es woanders hinlegen, müssen wir es auch hier hinlegen.

Ich schlage vor, die genauen Attribute zu verwenden, um konsistent zu bleiben.

Das

gladys/master/device/custom:12/feature/custom:12:temperature:1/last_value

gefiel mir gut! :slight_smile:

Ja! Nenne es Weiterleitung, wenn du willst, am Ende wird es nur die Implementierung einiger « benutzerdefinierter » Routen sein, die tatsächlich die gleichen Methoden in Gladys aufrufen :stuck_out_tongue:

Also, was ist der nächste Schritt dafür? @Boimb kannst du einen Vorschlag für eine Dokumentation machen, die zusammenfasst, was wir besprochen haben? (Oder du kannst: Hier, in einem GitHub-Issue, wie du es fühlst)

Hallo zusammen!

Da ich nächste Woche nach Normandie zu @Terdious fahre (der voll auf MQTT setzt) und heute mein letzter Entwicklungstag für Gladys ist, habe ich die Spezifikation geschrieben und begonnen, das umzusetzen, worüber wir in diesem Thema gesprochen haben.

Die Spezifikation:

Der Pull Request:

Ein Video-Demo:

https://streamable.com/jbnu4

Das ist noch nicht implementiert @CamilleB
Ein bisschen Geduld :wink:

Hallo zusammen!

Ich habe die MQTT-Entwicklungen fast abgeschlossen, die neue API, in der die MQTT-Gerätesteuerung nun codiert ist. Derzeit ist es in einem Branch und ich würde euer Feedback gerne haben :slight_smile:

Hier ist die Spezifikation der neuen API:

Ein kleines Demo-Video zum Senden eines Wertes von Device → Gladys:

https://streamable.com/audph

Aber es ist auch möglich, Daten von Gladys → Device zu senden, indem man die Funktion als nicht „schreibgeschützt“ einstellt. Gladys wird eine Nachricht auf das spezifizierte Topic senden.

Der PR ist hier verfügbar:

Zögert nicht, wenn ihr Feedback habt :slight_smile:

Kleine Frage, die vielleicht dumm erscheinen mag, aber warum gladys/master/device?
Wird der Master verwendet, um die möglichen Pods/Instanzen von Gladys zu unterscheiden, die noch kommen?

Ansonsten tolle Arbeit :wink:

Der « gladys/master » erlaubt tatsächlich anzuzeigen, dass man sich an die « master »-Instanz von Gladys richtet. Wenn man sich an ein Gerät wendet, wird das Präfix zu « gladys/device », und tatsächlich wird es in Zukunft, wenn es die Vorstellung von « pod » gibt, wahrscheinlich ein Präfix « gladys/pod » geben :slight_smile:

@pierre-gilles, nach unseren Slack-Gesprächen kopiere ich hier meine Nachricht:
Also, kleine Rückmeldungen:

  • 1 - das Erstellen funktioniert wieder.
  • 2 - nach der Änderung des Arduino-Programms funktioniert der Befehl über die Webseite perfekt.
  • 3 - der Befehl über Gladys Plus sendet 4 Befehle - nach 4 Stunden Testen sendet es 6 Befehle - der Arduino mag das nicht so sehr…
  • 4 - nach der Änderung des Arduino-Programms werden die States korrekt auf das richtige Topic gesendet. Gladys zeigt keine Logs in docker logs gladys an - Zur Info, wenn sie auf das falsche Topic gesendet werden, zeigt Gladys Logs einen Fehler an - und die States der Features werden nicht aktualisiert. Ich nehme also an, dass, wie wir gestern gesehen haben, meine Datenbank korrumpiert ist.
  • 5 - Aufgrund von - 4 - Gibt es eine Möglichkeit, die Tabelle t_device_feature state zurückzusetzen, ohne alles neu zu installieren? Das heißt, Gladys über Docker stoppen, die Tabelle löschen und Gladys neu starten?? Wird sie wiederhergestellt?

Danach muss ich noch den Fall testen, in dem die Topics jedes Geräts abgehört werden, ich habe mit dem Programm begonnen.

Vielen Dank im Voraus für dein Feedback zur Datenbank!!

Hallo @pierre-gilles,

Entschuldige die Verzögerung, ich war in den letzten zwei Wochen sehr beschäftigt.
Ich sehe, dass du die Antwort, die du mir auf Slack gegeben hast, nicht eingefügt hast, also erlaube ich mir, dich zu zitieren, um zu antworten:

1 & 2: Top!
3: Ah, das ist mir auch passiert, das ist ein Bug, ich denke, es kann Situationen geben, in denen sich deine Gladys-Instanz mehrmals mit Gladys Plus verbindet und dadurch mehrmals die Nachrichten erhält… Ich habe eine Issue erstellt => https://github.com/GladysAssistant/Gladys/issues/744. Hast du mehr Informationen über deine Konfiguration? War es in der lokalen Entwicklung oder auf deinem Raspberry Pi in der Produktion? Hast du Gladys häufig neu gestartet oder im Gegenteil nie? Ich möchte die Umstände des Bugs wissen
4. Ah! Mist, es ist schon schlimm, dass deine DB so sehr korrumpiert ist… Bewahre unbedingt ein Backup auf, damit wir analysieren können, was mit deiner DB nicht stimmt. Das ist eigentlich nicht normal, normalerweise unterstützt SQLite bis zu Terabyte in der DB… du kannst versuchen, einen docker restart gladys durchzuführen, um zu sehen, ob das das Problem löst?
5. Was möglich ist, ist ein DELETE FROM t_device_feature_state; Dies löscht alle Einträge in der Tabelle t_device_feature_state. Vorher mache ein SELECT COUNT(id) FROM t_device_feature_state; damit wir wissen, wie viele Zeilen in deiner Tabelle sind
Pierre-GillesPierre-Gilles
#744 Gladys sometimes connect multiple time to the Gladys Gateway and receive multiple time new messages
Gladys 4 RC
https://github.com/GladysAssistant/Gladys|GladysAssistant/GladysGladysAssistant/Gladys | 15. Apr. | Hingugefügt von GitHub

  1. Das war über mein Gladys Prod auf Raspberry 4. Nein, ich starte Gladys nie neu. Aber danach habe ich versucht, es zu tun, und es hat nichts geändert. Also habe ich das Arduino-Programm geändert, um nach einem Befehl eine Pause von 500 ms einzufügen, und es gibt keine Probleme mehr.

  2. Ja, ich vermute, dass das Problem woanders liegt und zum Glück ^^ Für die Info, ich laufe auf einer Sandisk 64GB-Karte V30, also denke ich, dass es nicht davon kommt. Ich habe den Neustart-Test gemacht, das löst das Problem nicht, aber ich habe einen schönen Fehler. Ich werde ihn dir bei Bedarf posten.

  3. Offensichtlich ist meine DB gesperrt. Keine der Manipulationen funktioniert. Also habe ich heute alles neu installiert und es funktionierte immer noch nicht für die States. Aber das Problem ist gelöst. Zuerst habe ich versucht, wie wir es in der Telefonkonferenz besprochen haben, das ursprüngliche Topic mqtt:Arduino01_Batiment:Energie Totale Phase 2:Centre Equithérapie durch mqtt:Arduino01_Batiment/Energie Totale Phase 2:Centre Equithérapie zu ersetzen (also die « : » nach dem Arduino-Namen durch « / »). Das funktionierte nicht (ich spreche nur über die States, der Befehl funktionierte gut)
    Also bin ich zum zweiten Versuch übergegangen, also das Arduino für alle Topics jedes Geräts abonnieren zu lassen, wobei das Topic in der Form mqtt:Arduino01_Batiment:Energie Totale Phase 2:Centre Equithérapie bleibt. Ich habe Mikro-Tempi vor den Abonnements hinzugefügt, und hör mal, es funktioniert für den Moment mit 15 abonnierten Topics. Die Befehle und die States funktionieren perfekt.