Reflexion autour de l'API MQTT

Bonjour,
Comme évoqué ici [MQTT] Comment fonctionne le module mqtt? - #47 by pierre-gilles

Je me permets d’ouvrir un topic pour discuter de l’implémentation de l’API MQTT de Gladys.
Je pense qu’il faudrait se cantonner ici à discuter uniquement de l’interface que l’on souhaite proposer aux utilisateurs/devices plutôt que d’histoire de modules ou de compatibilité. Ca devrait gagner en clarté.

Pour rappel, l’implémentation actuelle est documentée ici : A privacy-first, open-source home assistant | Gladys Assistant

J’essaierai de référencer les idées/questions/choix effectués au travers de cette discussion, ci-dessous afin qu’un nouveau venu puisse avoir une idée des choses évoquées sans devoir se fader tous les messages :wink:


Pourquoi définir une API?

Une API permet de définir des règles de communication entre Gladys et des clients tiers.

Elle permet de définir un ensemble d’actions possibles au travers du protocole MQTT, ainsi que la manière dont les données doivent être formatées.

Que veut-on ?

  • Permettre la majeure partie des interactions entre Gladys et tout type de périphérique qui pourrait y être connecté :
    • Changer l’état d’un objet avec un message émis depuis Gladys
    • Faire remonter l’état d’un objet à Gladys depuis un objet
  • Etre agnostique des marques/modèles le plus possible
  • Permettre la communication entre Objets / PODS / Instance principale
  • Respecter “les standards IOT” (s’il en existe :slight_smile: )

Idées validées

Vide pour l’instant

Idées réfutées

Vide pour l’instant


Disclaimer: Je n’ai évidemment pas autorité pour définir tout celà, aussi, les idées mentionnées dans le Pourquoi définir une API? et Que veut-on? sont vouées à être modifiées. Fallait bien commencer par un bout :wink:

Ma première impression à la lecture de la doc de l’API est sur les topics liés aux devices features.
Je ne suis pas super fan de l’implémentation actuelle sur deux points:

  • Il n’y a pas de différenciation entre les topics “entrant” et “sortant”. On sait qu’un device doit publier son nouvel état sur gladys/master/device/state/update mais sur lequel doit-il écouter pour récupérer son état ? Ces topics devraient être standards.
  • Les ids des objets passent dans le payload et non l’url. Ainsi, si je veux souscrire à l’update d’un seul device… et bien j’peux pas :confused: L’intégralité des devices devrait souscrire à un topci du type “device/update” et chercher à l’intérieur du payload si le message le concerne. Ca me semble anti-pattern.

Une proposition qui pourrait respecter ces deux remarques:
Si j’ai un objet de ce type:

{
  "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
    }
  ]
}

Proposition de topics et de payloads:

  • Le device publie son état
topic: gladys/master/device/custom:12/feature/custom:12:temperature:1
payload: 19.8

topic: gladys/master/device/custom:12/feature/custom:12:onoff:1
payload: 1
  • Gladys publie des changements pour que le device réagisse
topic: gladys/device/custom:12/feature/custom:12:temperature:1 // Adresse inutile dans la mesure ou on ne peut pas setter la T° sur le device :D

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

Ainsi, mon device peut souscrire à un seul ou plusieurs topics en fonction de ce qu’il peut gérer:

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

Et gladys qu’à un seul pour l’ensemble des devices:

gladys/device/#

Je pense que prendre en compte les wildcards offertes par le protocol est nécessaire.
Vous me direz, ouais, mais ça fait beaucoup de topcis au final!
Oui, et alors ? On a le choix… plus de topics ou plus de listeners sur le même topics.
Je crois que la “norme” dans l’IOT, est de différencier les topics par device. Un argument qui va dans ce sens est que ce n’est pas à l’objet de savoir différencier le contenu des messages qu’il reçoit. On s’économise en plus la charge d’un JSON.encode / JSON.decode côté objet connecté.
On pourrait aussi imaginer côté sécurité, différencier les accès par topic, et ainsi ne pas permettre à un objet non autorisé à publier sur un topic qui ne le concerne pas.

Qu’en pensez-vous ?

1 « J'aime »

Salut @Boimb ! Top d’avoir pris l’initiative de créer ce sujet :slight_smile:

Les topics présentés dans la documentation ne sont que des topics entrants ! Nous ne gérons pas de flux sortant en MQTT pour l’instant :slight_smile:

Il y a une logique dans l’API actuelle.

Un exemple sur le topic dont tu as parlé :

Après je suis d’accord avec toi, passer l’external_id dans le topic ça fait sens et c’est plus clean.

J’aime bien l’implémentation que tu proposes ! L’idée de virer le json.encode/decode côté device est smart.

Tu as inversé avec le device je pense. Gladys écoute ce qui lui est destiné, c’est à dire « gladys/master » :slight_smile:

Après il y a des topics où on sera obligé de garder du JSON encode, je pense à device.create par exemple:

Si tu es dispo pour commencer à rédiger une doc un peu formel, voir proposer une implémentation, ça sera avec plaisir :smiley:

Désolé pour les réponses multiples, j’y réfléchis et je note les idées qui me viennent ici :smiley:

Dans ta proposition gladys/master/device/custom:12/feature/custom:12:temperature:1 par exemple, il n’y a pas dans le topic de mention de ce qu’on vient créer/mettre à jour.

Si je publie 19.8 dans le sujet, qu’est ce que 19.8? C’est un nouvel état ! Hors au nom du topic, on ne sait pas quel attribut est concerné par cette mise à jour. (Ok, on le sait parce que c’est nous qui codons le topic, mais rien ne le dit dans le nom du topic)

Si on suit la logique prise ici, il faudrait rajouter un dernier suffixe au topic type “state”, ou “last_value” si on prend l’attribue exact en DB pour avoir un truc genre:

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

Qu’en penses-tu ?

Je me dis qu’à l’avenir, sur d’autres types de topic, on peut avoir envie de faire un topic qui lui prend tout l’objet JSON, et met à jour en DB l’objet entier (par exemple dans le cas ou le périphérique distant s’est mis à jour, et désormais gère plus de fonctionnalités, il peut mettre à jour son “device” avec un nouveau device)

Salut,

Je me permet d’intégrer le sujet, le MQTT étant le cœur de ma gestion à 99%.
Je vois donne ici ma configuration fonctionnelle sur Gladys v3 depuis 2 ans :
Arduino (device) → Gladys = update des capteurs :
Topic = gladys/master/device/state/update/Arduino01_garage/Électricité Garage
Payload = {“Tension moyenne”:231 , “Intensite totale”:11.6 , “Puissance”:2691.2 }
Décomposition Topic =

  • gladys/master/device/state/update = topic suivi par Gladys pour l’update des valeur
  • Arduino01_garage = nom de l’Arduino dans son prog pour ne pas s’encombrer des messages envoyés aux autres Arduino dans le topic des commandes (ci-dessous). Il fait partie de l’identifiant du device.
  • Électricité Garage = Nom du device, contenu dans son identifier également : name = Électricité Garage identifier = Arduino01_garage/Électricité Garage
  • Tension moyenne = identifier du DeviceType (Feature dans la V4 donc).

Gladys → Arduino (device) = commande des device :
Topic = gladys/master/device/state/scan/Arduino01_garage/Eclairage Garage
Payload = {“Eclairage Garage”:1 }

Actuellement sur la V4, pour les update je suis donc obligé d’envoyer un message par feature, alors que sur la v3, 1 message suffit pour actuellement 3 à 6 deviceType. J’espère que ça pourra vous aider.

Merci @Terdious pour ton retour !

Remarque super intéressante :slight_smile: L’un n’empêche pas l’autre, on pourrait avoir un topic par device qui permettrait de mettre à jour plusieurs features (en plus de l’autre topic par feature)

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

body:

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

Pour le last_values je suis pas sûr, on peut choisir de rester au singulier dans l’API, après à voir si c’est clair pour le développeur que c’est un envoi de plusieurs données.

@Boimb Qu’en penses-tu ?

Salut @pierre-gilles @Terdious , cool, des retours :smiley:
Je vais essayer de partager mes impressions sur les points abordés.

Yep. On est bien d’accord que c’est tout l’intérêt de MQTT, pouvoir faire également du push. Il faut donc définir les topics ad’hoc.

Oui, oui, bien sûr! J’imagine que ce thread est là pour la valider/améliorer.

My bad. Bien sûr, l’idée serait que Glady écoute tout ce qui la concerne
gladys/master/device/# ou même juste gladys/master/# mais je ne vois pas de cas d’usage (peut-être pour les pods plus tard)

Effectivement, ça semble obligatoire pour ça. Pour autant, je pense qu’il faut permettre comme c’est le cas ajd de créer aussi les devices et ajouter des features via l’IHM de Gladys (ce topic ne devrait pas être le seul moyen d’ajouter un périphérique MQTT, mais ça doit êre une solution)

Oui, et c’est volontaire. Est-ce que le device fera passer autre chose qu’une MAJ du state de la feature concernée au travers de ce topic ? Ceci-dit, ce n’est pas bloquant d’ajouter la mention state, alors why not.

Je suis pas super fan de la nomenclature du topic mais je partage l’avis de PG:

D’après ces premiers échanges, et de ce que j’ai lu sur les différents types d’application du protocol chez les différents fabricant, on n’arrivera sans doute pas à définir une API compatible avec tous les constructeurs ou fournisseurs de firmware open-source (Tasmota, ESPEasy).

Une solution qu’on pourrait explorer serait le forwarding, à savoir, la possibilité pour le client MQTT Gladys de forwarder les messages vers des topics qu’il écoute également. Je m’explique:

On définit une seule et unique API MQTT Gladys en se basant sur nos premières reflexions (qu’on affine et valide). Par exemple, pour l’update d’une feature:

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

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

Ca permet d’avoir une API claire et précise, sans ambiguité, et qui ne nécessite pas de rocket science à implémenter.

Pour les cas ou on n’a pas la main, genre le sevice MQTT des Shelly non flashés. ici la doc L’intégration prend la forme de forward des topics « propriétaires ». Par exemple:

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

Ainsi, on ne multiplie pas les routes « publiques » de l’API, et pour autant, on est capable de gérer des appareils « non compliant ». Sans compter que l’intégration n’est ni plus ni moins que l’ajout que de forward. On peut d’ailleurs imaginer que le module MQTT, face à un message sur topic propriétaire, est capable de créer le device associé s’il n’existe pas en DB avant de le forwarder.

Bien-sûr! Je n’avais pas défini les routes sortantes par simple manque de temps. Dans la v4 tu l’as vu je prend le temps de bien définir les choses et de faire ça bien, je préfère ne pas développer quelque chose plutôt que de le développer vite et mal :slight_smile:

Je suis d’accord, à mon avis il vaut mieux recommander aux développeurs de code MQTT de demander à l’utilisateur de créer les devices dans l’UI (comme c’est le cas pour les autres services), sinon dès que ça se passe en background, l’utilisateur n’a aucun feedback si ça réussit ou ça rate. Et si ça rate, à part regarder les logs (ce qu’on ne veut pas dans la v4), il ne peut rien faire.

Pour ce topic, non, mais pour d’autres topics oui. Et comme on veut garder les mêmes conventions de nommage partout pour rester consistent, si on le met autre part, il faut le mettre ici aussi.

Je propose d’utiliser les attributs exactes pour rester consistent.

Le

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

Me plaisait bien ! :slight_smile:

Oui ! Appelle ça forwarding si tu veux, après dernière ça sera juste l’implémentation de quelques routes « custom » qui effectivement appelleront les mêmes méthodes dans Gladys :stuck_out_tongue:

Bon du coup c’est quoi la next step pour ça? @Boimb tu peux faire une proposition de doc qui recap ce qu’on s’est dit? (Ou tu veux: Ici, dans une issue github, comme tu le sens)

Hello à tous !

Etant donné que je vais en Normandie chez @Terdious la semaine prochaine (qui est en full MQTT), et qu’aujourd’hui est mon dernier jour de développement Gladys d’ici là, j’ai écris la spec et commencé à implémenter ce qu’on s’est dit dans ce topic.

La spécification :

https://github.com/GladysAssistant/Gladys/issues/685

La PR :

https://github.com/GladysAssistant/Gladys/pull/686

Une démo vidéo :

https://streamable.com/jbnu4

2 « J'aime »

Non ce n’est pas encore implémenté @CamilleB
Un peu de patience :wink:

Salut à tous !

J’ai quasiment fini les développements MQTT, la nouvelle API dont le contrôle des périphériques MQTT est désormais codé, pour l’instant c’est dans une branche et j’aimerais votre retour :slight_smile:

Voilà la spécification de la nouvelle API:

Une petite vidéo démo d’un envoi de valeur Device → Gladys:

https://streamable.com/audph

Mais il est possible aussi d’envoyer des données de Gladys → Device, en mettant la feature comme non “read-only”. Gladys enverra un message sur le topic spécifié.

La PR est disponible ici :

https://github.com/GladysAssistant/Gladys/pull/686

N’hésitez pas si vous avez des retours :slight_smile:

4 « J'aime »

Petite question qui va peut être sembler bête, mais pourquoi gladys/master/device ?
Le master est utilisé pour différentier les possibles pods/instances de gladys à venir ?

Sinon beau boulot :wink:

Le “gladys/master” permet effectivement d’indiquer que l’on s’adresse à l’instance “master” de Gladys. Lorsqu’on s’adresse à un device, le préfixe devient “gladys/device”, et effectivement à l’avenir quand il y aura la notion de “pod”, il y a aura probablement un préfix “gladys/pod” :slight_smile:

2 « J'aime »

10 posts were split to a new topic: Problèmes lenteur dashboard

@pierre-gilles, suite à nos échanges sur Slack, je copie/colle mon message ici :
Bon alors petits retours :

  • 1 - le create fonctionne bien à nouveau.
  • 2 - apres modification du prog Arduino la commande par l’intermédiaire de la page web fonctionne impec.
  • 3 - la commande par l’intermédiaire de Gladys Plus envoi 4 ordre - après 4h d’essais, envoi 6 ordre - l’Arduino n’aime pas trop…
  • 4 - apres modification du prog Arduino, les states sont bien envoyé sur le bon topic. Gladys n’affiche aucun logs dans docker logs gladys - Pour info, lorsqu’ils sont envoyés sur le mauvais topic, les logs Gladys affichent bien une erreur - et les states des features ne se mettent pas à jour. Je suppose donc, suite à ce que nous avons vu hier, que ma base de donnée est vérolée.
  • 5 - Suite à - 4 -, Y a-t-il moyen de reset la table t_device_feature state sans tout réinstaller ? C’est à dire stopper Gladys via docker, delete la table puis relancer Gladys ?? Sera-t-elle rétablie ?

Il me restera ensuite à tester le cas en écoutant les topic de chaque device, j’ai commencé le prog.

Merci d’avance pour ton retour concernant la db !!

Salut @pierre-gilles,

Désolé pour le temps de REX, j’ai pas mal été pris les 2 dernières semaines.
Je vois que tu n’as pas insérer la réponse que tu m’avais faite sur Slack, je me permet donc de te citer pour te répondre :

1 & 2: Top !
3 : Ah, ça m’est arrivé moi aussi, c’est un bug, je pense qu’il peut y avoir des situations ou ton instance Gladys se connecte plusieurs fois à Gladys Plus et du coup reçoit plusieurs fois les messages… J’ai créé une issue => https://github.com/GladysAssistant/Gladys/issues/744. Tu as plus d’informations sur ton setup ? C’était en développement local, ou sur ton Raspberry Pi de production? Tu avais redémarré Gladys fréquemment, ou au contraire jamais? J’aimerais savoir les circonstances du bug
4. Ah! Mince, c’est chaud qu’à ce niveau là ta DB soit à ce point là vérolée… Garde bien un backup pour qu’on puisse analyser ce qui ne va pas dans ta DB. C’est pas normal en vrai, normalement SQLite ça supporte jusqu’à des Tera en BDD… tu peux essayer de faire un docker restart gladys pour voir si ça résout le pb?
5. Ce qu’il est possible de faire, c’est un DELETE FROM t_device_feature_state; cela va supprimer toutes les entrées dans la table t_device_feature_state . Limite avant, fait un SELECT COUNT(id) FROMt_device_feature_state; pour qu’on sache combien il y a de row dans ta table
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 avr. | Ajouté(e) par GitHub

  1. C’était par l’intermédiaire de mon Gladys Prod sur Raspberry 4. Non je ne redémarre jamais Gladys. Mais suite à cela j’ai tenté de le faire et ça n’a rien changé. Bon du coup j’ai modifié le prog Arduino pour faire une pause de 500ms après un passage d’ordre et ça ne pose plus de soucis.

  2. Oui je me doute que le problème vient d’ailleurs et heureusement ^^ Pour info je tourne sur une carte SD Sandisk 64Go en V30, donc je ne pense pas que ça vienne de la non plus. J’ai fais le test de reboot, ça ne résout pas le soucis mais j’ai une belle erreur. Je te la posterais au besoin.

  3. Visiblement ma BD est verrouillée. Aucune des manip ne fonctionne. J’ai donc aujourd’hui tout réinstallé et ça ne fonctionnait toujours pas pour les states. Mais le problème est résolu. J’ai tout d’abord tenté comme on se l’était dit en conv-call, de remplacer le topic d’origine mqtt:Arduino01_Batiment:Energie Totale Phase 2:Centre Equithérapie par mqtt:Arduino01_Batiment/Energie Totale Phase 2:Centre Equithérapie (soit les « : » après le nom de l’Arduino par « / »). Ca ne fonctionnait pas (je parle seulement des states, la commande fonctionnait bien)
    Je suis donc passé au second essai, soit faire souscrire l’Arduino à tout les topics de chaque device en gardant donc le topic sous la forme mqtt:Arduino01_Batiment:Energie Totale Phase 2:Centre Equithérapie. J’ai rajouté des micro tempos avant les souscriptions, et écoute ça passe pour le moment avec 15 topics souscrits. Les commandes et les states fonctionnent parfaitement.

A post was split to a new topic: Détection présence dans Gladys 4