Intégrations externes dans Gladys Assistant

Trop cool ces investigations, franchement c’est bluffant on dirait des intégrations natives !

Ce qui est cool, c’est qu’une fois que tu te sens prêt sur ces intégrations, il te suffit de mettre ton repo dans le topic github que le store scanne toutes les heures, et hop il sera publié sur les instances Gladys :grin:

Il y a de la validation automatique sur le manifest et l’image Docker, et c’est tout !

Tu peux le faire avec le bouton « forcer la mise à jour » ! :wink:

Je vais regarder !

Carrément !!

En effet, l’intégration visuelle est vraiment excellente, et pour 1 user, d’une simplicité extrême !

D’un autre côté, il va falloir bosser sur la doc, car ici toutefois, aucune possibilité d’afficher de l’information.

Et pour les systèmes plus complexes comme Tuya avec son implémentation locale, il manque encore des briques (ouh làlà !! aucune critique hein :sweat_smile: ce nouveau système à été implémenté avec une vitesse complètement folle et fait déjà ses preuves :joy: :clap:)

Alors il nous manque (à Fable et à moi) une connaissance sur ce point car non ça ne fonctionne pas sur une image dev, et si le manifest change !


De ce qu’on voit il se porte sur le numéro de version dans le nom de l’image ?

Logs :

2026-07-19T10:53:49+0200 <warn> externalIntegration.update.js:43 (ExternalIntegration.update) Unable to pull image ghcr.io/terdious/gladys-tuya:1.0.0 Error: (HTTP code 404) unexpected - manifest unknown
    at /home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/docker-modem/lib/modem.js:336:17
    at IncomingMessage.<anonymous> (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/docker-modem/lib/modem.js:363:9)
    at IncomingMessage.emit (node:events:530:35)
    at endReadableNT (node:internal/streams/readable:1698:12)
    at processTicksAndRejections (node:internal/process/task_queues:90:21) {
  reason: undefined,
  statusCode: 404,
  json: null
}
2026-07-19T10:53:49+0200 <debug> errorMiddleware.js:26 (errorMiddleware) BadParameters [Error]: UNABLE_TO_PULL_IMAGE: image may not exist or may not be available for your architecture
    at ExternalIntegration.update (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/external-integration/externalIntegration.update.js:44:11)
    at processTicksAndRejections (node:internal/process/task_queues:105:5)
    at update (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/controllers/externalIntegration.controller.js:97:25)
From previous event:
    at /home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/middlewares/asyncMiddleware.js:4:18
    at Layer.handle [as handle_request] (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/route.js:144:13)
    at adminMiddleware (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/middlewares/adminMiddleware.js:6:5)
    at Layer.handle [as handle_request] (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/route.js:144:13)
    at /home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/middlewares/authMiddleware.js:28:7

Mais peut-être dû au fait qu’on le fasse tourner pour les essais hors docker ?

J’aurais une autre question, pour les sonnettes Tuya, nous sommes en train de bosser avec @GBoulvin :
En creusant le SDK on a réalisé que Pulsar lui-même n’est pas bloquant : le container peut ouvrir la connexion temps réel Tuya et pousser les événements (dont l’appui sonnette) via publishState. Le vrai manque côté sonnette, c’est la publication d’une image de caméra depuis une intégration externe — le SDK ne gère aujourd’hui que number/text/état passé, pas l’équivalent de camera.setImage. J’ai préparé une PR en ce sens sur le SDK (enfin j’espère que c’est bien ce que tu souhaites que l’on fasse sur les choses qui peuvent etre manquante à l’heure actuelle :sweat_smile:)

Et pour le Tuya local (super complémentaire du cloud), deux briques nous aideraient — je vois qu’elles recoupent tes #2680 et #2677 :

  1. l’accès au réseau local depuis le container (pour joindre les appareils en 192.168.x) ;
  2. la possibilité d’exposer/éditer des params d’appareil (IP, protocole, bascule cloud/local) et de déclencher une action sur un appareil avec retour de résultat (ex. une « lecture locale des DP » — une opération qu’un utilisateur non initié ne devinera jamais, d’où l’intérêt d’un bouton dédié).

Et sinon :

  • côté Zendure, tout est maintenant fonctionnel pour cette V1 :partying_face: Prêt à la proposer.
    On a même créé les issues pour avancer et se donner une roadmap d’implémentation :heart_eyes:
  • côté Tuya, on est presque au bout !! Le local fonctionne pour les appareils trouvés en UDP (3 appareils / 10). Les commandes passent en local ET en cloud. Reste le retour d’état via cloud qui ne passe pas :sweat_smile:

EDIT : J’ai lancé la release gladys-zendure + ajouté le topic, rien d’autre à faire ?

Et une remarque de Claude après qu’on est rencontré des soucis de poll sur les 2 intégrations Zendure et Tuya, je ne sais pas si il a raison, mais ça semble avoir résolu le souci côté Zendure :

Salut Pierre-Gilles,

En finalisant ma première intégration externe (gladys-zendure), j’ai buté sur
le poll_frequency des appareils, et en comparant avec tes autres intégrations
je crois avoir trouvé une incohérence dans le template qui vaut le coup d’être
remontée.

Le constat : le poll_frequency de l’OBJET device (celui du payload de
publishDiscoveredDevices) est validé strictement par le core des intégrations
externes — il doit être une valeur de DEVICE_POLL_FREQUENCIES, EN
MILLISECONDES (1000/2000/10000/15000/30000/60000), et l’appareil doit porter
should_poll: true pour être pollé. Sinon : 422 « invalid poll frequency »
(ou pas de polling du tout si should_poll manque).

C’est exactement ce que fait gladys-melcloud, et c’est correct :
poll_frequency: POLL_FREQUENCY, // = 10 * 1000 ms
should_poll: true,

Mais le template integration-template-js, lui, est incohérent avec ce contrat :

  • ses devices de démo font poll_frequency: config.poll_frequency
  • avec un défaut poll_frequency: 300 dans config.js (donc 300, interprété
    comme des secondes… mais passé tel quel comme fréquence device)
  • et ils ne mettent pas should_poll.
    Donc si on installe le template tel quel et qu’on crée/polle un device de démo,
    le core le rejette (300 n’est pas une valeur de DEVICE_POLL_FREQUENCIES en ms)
    ou ne le polle jamais (should_poll absent).

La confusion vient surtout du fait qu’il y a DEUX « poll_frequency » homonymes :

  1. le champ config_schema du manifeste = une simple valeur de formulaire
    (secondes, choisie par l’intégrateur, sans lien automatique avec le polling) ;
  2. le champ poll_frequency de l’objet device = contraint à
    DEVICE_POLL_FREQUENCIES (ms) par le core.
    Le template les relie en passant (1) directement dans (2), ce qui ne peut pas
    marcher.

Trois pistes, au choix :

  • corriger le template pour qu’il soit fonctionnel « out of the box » : soit
    hardcoder une valeur ms valide + should_poll: true (comme melcloud), soit
    faire le snap secondes→ms comme j’ai dû le faire dans zendure ;
  • ou rendre le core plus tolérant : accepter des secondes et faire le snap
    côté serveur ;
  • ou au minimum documenter que le poll_frequency du payload device est
    contraint à DEVICE_POLL_FREQUENCIES (ms) et nécessite should_poll: true.

Pour info, côté zendure j’ai un petit helper qui snappe la valeur config
(secondes, 10–3600) vers la valeur ms autorisée la plus proche — je peux le
partager si ça t’intéresse pour le template.

Rien de bloquant pour moi (zendure fonctionne), c’est juste un retour de
première expérience qui pourra éviter le même piège aux prochains.

Egalement :

Retour sur le framework external-integration (en testant gladys-tuya en conditions réelles).

Constat : quand une intégration externe republie un appareil déjà créé via
publishDiscoveredDevices (même external_id) avec des params mis à jour, les
params de l’appareil existant ne sont PAS mis à jour côté Gladys, et aucune
option « Mettre à jour » n’apparaît dans l’écran de découverte pour les
intégrations externes (contrairement aux services natifs).

Cas d’usage concret : un appareil Tuya est créé en mode cloud. Plus tard,
l’utilisateur active le mode local (l’intégration redécouvre l’appareil avec
ip / local_key / protocol_version / local_override après un scan LAN). Mais
l’appareil déjà créé conserve ses anciens params : impossible de basculer en
local sans le supprimer puis le recréer.

Questions :

  • Est-ce voulu / prévu ? Un re-publish d’un device découvert existant
    devrait-il faire un upsert des params (par external_id) ?
  • Ou l’écran de découverte des intégrations externes pourrait-il exposer une
    action « Mettre à jour » (comme les services natifs le font sur leur page
    d’appareil) pour ré-appliquer les params issus d’une nouvelle découverte ?

Pour l’instant le contournement utilisateur est « supprimer + recréer »
l’appareil, ce qui est peu ergonomique dès qu’un paramètre local change
(IP, clé locale rotée, passage cloud→local).

Merci !

Je viens d’implémenter 4 types d’appareils supplémentaire + la gestion en mqtt local en 35 minutes !!! C’est bluffant !!

Ah bizarre, je vais regarder, effectivement il faudrait qu’il pull « dev » dans ce cas

Ah bien, merci ! En général je faisais dans le sens :

Modification Spec → Modification Gladys → Modification SDK → Modification template

Comme ça tout reste cohérent, mais je vais regarder ta PR :slight_smile:

Ok je vais voir avec Claude

C’est pas le cas ça ? Tu peux pas exposer params dans publishDiscoveredDevices ?

Tu peux préciser cette partie ? Notamment « DP » je ne sais pas ce que ça veut dire :sweat_smile:

Excellent !!

Tu peux voir l’état de ta release sur cette URL :

Après, dans ton cas tu te bases sur une version du SDK qui n’est pas encore mergé donc je pense que le store va rejeter car le manifest est pas valide pour lui (normal, il faut attendre que je merge la PR)

Bien vu, c’est une erreur côté template, je vais coriger ça !!

Incroyable !! J’ai hâte de mettre tout ça en prod :grin:

Salut @pierre-gilles , et merci pour tes retours

  1. Bouton « Mettre à jour » / version d’image
    Oui, c’est bien ça : le bouton tente de pull la version figée du manifest (ex. :1.0.0) au lieu de « :dev ». Pour l’itération en dev, pull « dev » serait parfait. Penser au fait qu’une image dev de PR puisse se nommer avec un tag allonger comme « :dev-test-mqtt-local » et l’inscrire dans la spec pour imposer les tags d’image de dev.

  2. « Exposer/éditer des params d’appareil » — je précise
    Exposer les params à la découverte : OK, ça marche, je le fais déjà dans publishDiscoveredDevices (ip, local_key, protocol_version, local_override…).
    Le manque n’est pas là : c’est la MISE À JOUR des params d’un appareil DÉJÀ CRÉÉ. Quand je republie un device découvert (même external_id) avec des params qui ont changé — typiquement l’IP LAN qui bouge en DHCP, ou un passage cloud→local après un nouveau scan — les params de l’appareil existant ne sont pas ré-appliqués, et l’utilisateur n’a aucun moyen de les éditer/rafraîchir.
    Aujourd’hui le seul contournement est supprimer + recréer l’appareil.
    → Besoin : soit un upsert des params sur re-publish (par external_id), soit une action « Mettre à jour » dans l’écran de découverte des intégrations externes

  3. « DP » et « action avec retour de résultat »
    DP = Data Point (terminologie Tuya). Chaque appareil Tuya expose des datapoints numérotés (DP 1 = interrupteur, DP 20 = luminosité, etc.) ; l’état complet = la « DPS map ». La « lecture locale des DP » consiste à lire cette map EN DIRECT sur le LAN via le protocole local Tuya. Deux usages :

  • valider que la connexion locale + la version de protocole fonctionnent
    AVANT d’activer le mode local (sinon on active un local qui timeout) ;
  • découvrir/confirmer le mapping DP→feature.

C’est une action déclenchée par l’utilisateur, AVEC un résultat renvoyé à l’UI (les DP lus, ou l’erreur). Le besoin générique pour les intégrations externes : pouvoir exposer une action utilisateur « exécute cette opération et montre-moi le résultat » (test de connexion locale, lecture DP, ré-appairage, identify…), puisqu’on n’a pas d’UI custom. Le SDK ne gère aujourd’hui que le push d’états et poll/setValue — pas ce type d’action requête/réponse à la demande.
On ne peut déclencher cette recherche de version de protocol et DP que si on connait l’IP, et si l’UDP ne l’as pas trouvé, on doit pouvoir renseigner l’IP manuellement pour lancer cette recherche automatique (il scan en tentant de lancer des poll pour chaque version) - Et c’est une opération assez longue - jusqu’à 15s je crois - pour éviter de spam l’appareil.

Autre retours :

  • Par erreur, j’ai pu installer un 2ème container dev alors que je n’avais pas désinstaller l’ancien. Aucune erreur, il s’est installer en « -2 ». A l’installation on devrait être averti et que ce soit impossible d’installer 2 images identiques (par contre on devrait pouvoir installer un prod et un dev comme actuellement)
    image

  • Installer un container avec tag « :dev » (ou tag « :dev-xxxxxx ») devrait afficher une alerte de prévention : si une instance prod tourne à côté cela peut impacter/casser l’autre instance, conseiller d’arreter le container prod avant installation du tag « :dev » - par contre une très bonne chose de pouvoir installer une dev et une prod cote à cote

  • Afficher dans la page de configuration l’heure de démarrage du container

  • Lorsqu’un container externe est arrêté, il faudrait arrêter le polling (et tout autre tentative d’échange si c’est le cas), pour la pollution de logs :

2026-07-20T07:43:19+0200 <error> device.poll.js:23 (DeviceManager.poll) There was an error while polling device ambiance-salon
2026-07-20T07:43:19+0200 <error> device.poll.js:24 (DeviceManager.poll) ExternalIntegrationUnavailableError: EXTERNAL_INTEGRATION_NOT_CONNECTED
    at ExternalIntegration.sendCommand (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/external-integration/externalIntegration.sendCommand.js:23:11)
    at Object.poll (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/external-integration/externalIntegration.registerProxyService.js:42:20)
    at DeviceManager.poll (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/device/device.poll.js:21:26)
    at Promise.map.concurrency (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/device/device.pollAll.js:13:87)
    at tryCatcher (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/util.js:16:23)
    at MappingPromiseArray._promiseFulfilled (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/map.js:68:38)
    at MappingPromiseArray.PromiseArray._iterate (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/promise_array.js:115:31)
    at MappingPromiseArray.init (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/promise_array.js:79:10)
    at MappingPromiseArray._asyncInit (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/map.js:37:10)
    at _drainQueueStep (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:97:12)
    at _drainQueue (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:86:9)
    at Async._drainQueues (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:102:5)
    at Immediate.Async.drainQueues (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:15:14)
    at processImmediate (node:internal/timers:491:21)

  • Lorsqu’une intégration peut etre cloud et local avec des appareils qui peuvent être local ou cloud, on devrait imposé une spec :
    • Global Intégration en local ou en cloud => un interrupteur « Mode local » qui privilégie pour tous les appareils le local si coché, fallback par appareil en cloud si pas de local ou cassé ; sinon 100% cloud
      image
    • un macaron global sur la vue Appareil qui spécifie si l’application est définie en local ou en cloud
    • un macaron par appareil qui spécifie si l’appareil est en local ou en cloud.
      On a vraiment besoin de ça pour Tuya par exemple. Et je rencontre le meme cas sur Zendure (les tests pendant l’implémentation du mqtt local sur zendure m’ont permis de me rendre compte que j’avais 2 appareils qui n’avaient pas le mqtt local activé, j’ai galéré 30 minutes, un macaron visible m’aurait fait comprendre et passer au vrai diag rapidement).
      Ensuite la documentation de l’integration externe pourra comporter une section « FAQ » ou d’aide aux recherches/résolutions de problèmes
  • D’ailleurs côté documentation ? Je n’ai pas cherché dans tes specs, mais as-tu prévu de l’imposer pour valider une intégration ?
    Ce serait bien de définir par exemple (comme HA), un format de fond de page et les chapitres principaux à imposer - avec un template comme pour le container. Possible ?

Salut @Terdious ce sont de très bons retours :slight_smile:

J’ai adapté la spec et l’implémentation va suivre :

Les sept retours sont intégrés, avec pour chacun la décision de design qui va avec :

1. Params après création — les deux mécanismes demandés, avec une frontière nette. Les params sont la donnée technique de l’intégration : sur re-publication d’un device déjà créé (même external_id), le superviseur les upsert silencieusement (l’IP DHCP qui bouge, le passage cloud→local), sans écho device-updated vers l’intégration (sinon boucle : republier → événement → republier). Le name, la pièce et les features restent propriété de l’utilisateur : si la structure publiée diffère, l’écran Découverte affiche un bouton « Mettre à jour » — geste utilisateur via le POST /api/v1/device standard. Fini le supprimer/recréer.

2. Boutons d’actions — nouveau champ actions du manifeste (max 10) : key, label/description multi-langues, et surtout deux choses dimensionnées sur le cas Tuya : un fields optionnel au format exact du config_schema (le mini-formulaire « saisir l’IP » est rendu par le même moteur, validé pareil) et un timeout_seconds par action (5–120, défaut 30) qui remplace la règle des 5 s pour action.run — le scan de versions de protocole à ~15 s passe. Chaîne complète : bouton → POST .../action/:key → WS action.run → résultat dans command-result.data.message (string ou multi-langue) affiché sous le bouton. SDK : onAction('detect_protocol', async (fields) => '...').

3. Double instance dev/prod : à l’installation (tous modes), si une intégration installée partage la même image sans le tag (:dev à côté de :1.2.0) ou le même name de manifeste → avertissement explicite (conflit possible sur le même compte cloud / les mêmes appareils, conseil d’arrêter l’autre instance pendant les tests), mais l’installation reste possible — dev à côté de prod est un usage voulu, les selectors ext-dev-* garantissent l’absence de collision technique.

4. Heure de démarrage : started_at du conteneur principal (et de chaque sous-conteneur) dans le détail admin, affiché dans le bloc de supervision (« en fonctionnement depuis… »).

5. Poll d’une intégration arrêtée : le poll planifié devient un no-op silencieux quand l’intégration n’est pas RUNNING/DEGRADED — ni throw ni ligne de log toutes les N secondes pour un état déjà connu. setValue continue de throw : l’utilisateur qui actionne doit voir l’erreur.

6. Types de champs : boolean existait déjà (rendu interrupteur/case à cocher — précisé) ; ajout de multi_select (cases à cocher, valeur = array) et de display: "radio" sur les select.

7. Documentation obligatoire sur le store : publier exige désormais docs/en.md et docs/fr.md, structurés selon le template livré dans le repo template (Présentation / Prérequis / Configuration / Dépannage). L’indexeur vérifie présence + taille minimale (rejet error sinon), re-héberge les fichiers sur Pages comme les covers (docs dans index.json), et l’écran d’installation gagne un lien « Documentation » (langue utilisateur, fallback en).

Je trouve que c’est un peu trop spécifique à ce que tu fais là, je préfère du générique :slight_smile:

Alors en effet, Zendure n’est ni dans https://integration-store-storage.gladysassistant.com/index.json, ni dans https://integration-store-storage.gladysassistant.com/rejected.json ^^
Par contre gladys-tuya (dont je n’ai pas encore mergé les PR) se retrouve bien dans https://integration-store-storage.gladysassistant.com/rejected.json :

[
  {
    "store_slug": "Terdious/gladys-tuya",
    "level": "error",
    "reason": "docker_image: image is not publicly pullable (registry auth denied, HTTP 403)",
    "checked_at": "2026-07-20T07:24:35.428Z"
  }
]

Par contre il va falloir que tu nous fasses une vrai page visuelle hein :face_with_peeking_eye: :sweat_smile:

Fable 5 nous fera ça, t’inquiète pas :rofl:

@Terdious du coup tu faisais des tests depuis la PR #2680 ? Tu me confirmes que ça fonctionnait bien ?

Ma foi, oui, je dirais parfaitement :slight_smile:

Pour les nouvelles features que tu as demandée, j’ai demandé à GPT 5.6 Sol 1M High de review, et il trouve que c’est trop générique Zendure, et je suis plutôt d’accord avec lui :slight_smile:

La review: Add solar-battery device feature category - gladys-zendure external integration by Terdious · Pull Request #2682 · GladysAssistant/Gladys · GitHub

Merci Pierre-Gilles, dans l’ensemble c’est tout bon pour moi :

Merci pour tous les correctifs / implémentations

Un seul point où je pousse un peu — le « trop spécifique / plutôt générique » sur
le mode local/cloud + macarons :

Je pense que c’est justement un besoin GÉNÉRIQUE, pas propre à Tuya. Le motif
récurrent = « une intégration qui couvre cloud ET local, avec des appareils dont
le transport effectif peut différer d’un appareil à l’autre et évoluer dans le
temps ». Exemple au pied levé :

Intégration Dualité cloud/local Statut
Tuya cloud + LAN par appareil dans Gladys (→ externe)
Netatmo météo = full cloud ; caméras/sonnette = cloud + local (local bien plus rapide sur l’image, plus fiable car le lien cloud saute) dans Gladys, en cours de passage externe
Zendure cloud + MQTT local externe (le tien)
Shelly HTTP/WS local + Shelly Cloud convertible / à venir
eWeLink / Sonoff cloud + mode LAN convertible / à venir
Somfy TaHoma cloud + API locale convertible / à venir

La brique à mettre dans le framework, ce n’est pas la logique Tuya,
c’est :

  1. un STATUT DE TRANSPORT PAR APPAREIL, standard et agnostique
    (local / cloud / injoignable), que l’intégration remonte et que Gladys
    affiche en macaron — + un macaron global sur la vue Appareils ;
  2. optionnellement une PRÉFÉRENCE DE CONNEXION standard (« privilégier le local
    quand disponible, cloud sinon ») — c’est exactement le libellé de ton toggle
    « Mode local (LAN) ».
    L’intégration remplit, Gladys rend → 100 % générique, zéro sémantique Tuya
    dans le core.

La valeur est surtout diagnostique et universelle : sans macaron visible,
l’utilisateur ne peut pas savoir pourquoi un appareil est lent ou figé. Mon
propre cas Zendure (2 appareils sans MQTT local, 30 min perdues) : un macaron
m’aurait mis sur la piste tout de suite.

Et ce n’est pas qu’une intégration, la même logique est valable pour tous les appareils du tableau ci-dessus. L’autre jour tu me parlais de Shelly, c’est pareil, il faut activer le mqtt sur l’interface web local http pour avoir le MQTT local et/ou le cloud. Le
motif revient dès qu’un fabricant offre les deux canaux — d’où l’intérêt d’une
primitive « transport status + preference » réutilisable plutôt qu’un traitement
par intégration.

En plus c’est assez simple à mettre en oeuvre côté Gladys core : Un toggle défini coté configuration (qui serait le meme pour tous) et un param côté device (idem défini en spec). Ensuite c’est de la tambouille interne au container !

Ok merci pour l’explication, c’est bon pour moi j’ai adapté la spec !

Je suis entrain de voir avec Claude pour l’accès au réseau local

Pour l’accès local normalement c’est déjà possible, tu es sûr que ça te bloquait ?

Mon avis : c’est déjà possible dans le design — et c’est important de ne pas « réparer » ce qui n’est pas cassé. Le bridge NAT ne bloque pas l’unicast sortant vers le LAN : un conteneur sur gladys-integrations peut ouvrir une connexion TCP/UDP vers 192.168.1.50 (le protocole local Tuya port 6668, l’API HTTP locale Shelly…) exactement comme il joint un cloud — Docker masquerade la sortie, rien n’est filtré. enable_icc=false n’isole que les conteneurs entre eux sur le bridge, pas le trafic vers l’extérieur. La seule vraie limite, déjà documentée en B.2/B.16, c’est la réception de broadcast/multicast (le scan UDP de découverte) — et je soupçonne que c’est la source du malentendu : « pas de broadcast » a été lu comme « pas de LAN ». S’il a réellement constaté un échec unicast dans son environnement de dev, la cause est extérieure au framework — le cas classique étant un pare-feu hôte strict (ufw) qui DROP le FORWARD bridge→interface LAN : symptôme exact « cloud OK, LAN KO ».

Je revérifie si ce n’est pas lui (et moi qui a mal guidé…) qui a mal fait !! Si c’est ça désolé du dérangement.

SDK en version 0.4.0 publié, Claude adapte le template-js, et je suis entrain de faire les merge Gladys vers la PR de base

Edit: merge effectué, tout est dans cette PR maintenant: External integrations (RFC phase 1): Docker supervisor, host API, integration WebSocket, decentralized store and generic frontend by Pierre-Gilles · Pull Request #2665 · GladysAssistant/Gladys · GitHub