Reflexion autour de l'API MQTT

Bonjour,
Comme évoqué ici [MQTT] Comment fonctionne le module mqtt?

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 : https://documentation.gladysassistant.com/en/api#mqtt-api

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 Like

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 :

La PR :

Une démo vidéo :

2 Likes

Bonsoir,
je suis intéressé afin de savoir si vous avez implémenté la commande qui permet à Gladys de publier sur un broker MQTT ?
Merci votre réponse

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

ok merci. C’est déjà super le travail accompli.
Désolé ne pas pouvoir vous aider sur la partie logicielle.
Mais si vous avez besoin de petite main pour faire des essais de débeugage ou rédaction de doc, si je peux aider, n’hésitez pas à me solliciter ce sera avec plaisir.

1 Like

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:

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 :

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

2 Likes

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:

1 Like