Externe Integrationen in Gladys Assistant

Trop cool diese Untersuchungen, ehrlich gesagt ist das beeindruckend, man könnte meinen, es handelt sich um native Integrationen!

Das Coole daran ist, dass du, sobald du dich mit diesen Integrationen bereit fühlst, einfach deinen Repo in das GitHub-Topic stellen musst, das der Store alle Stunden scannt, und schon wird es auf den Gladys-Instanzen veröffentlicht :grin:

Es gibt eine automatische Validierung für das Manifest und das Docker-Image, und das war’s!

Das kannst du mit dem „Update erzwingen“-Button machen! :wink:

Ich werde das prüfen!

Auf jeden Fall!!

In der Tat ist die visuelle Integration wirklich ausgezeichnet und für 1 Benutzer extrem einfach!

Auf der anderen Seite müssen wir an der Dokumentation arbeiten, denn hier gibt es leider keine Möglichkeit, Informationen anzuzeigen.

Und für komplexere Systeme wie Tuya mit seiner lokalen Implementierung fehlen noch einige Bausteine (oh làlà!! Keine Kritik, nein :sweat_smile: Dieses neue System wurde mit einer völlig verrückten Geschwindigkeit implementiert und hat sich bereits bewährt :joy: :clap:)

Dann fehlt uns (Fable und mir) das Wissen an dieser Stelle, denn nein, es funktioniert nicht auf einem Entwicklungsbild, und wenn sich das Manifest ändert!


Aus dem, was wir sehen, scheint es sich auf die Versionsnummer im Bildnamen zu beziehen?

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

Vielleicht liegt es aber auch daran, dass wir es für Tests außerhalb von Docker laufen lassen?

Ich hätte noch eine Frage zu den Tuya-Klingeln. Wir arbeiten mit @GBoulvin daran:
Beim Durchforsten des SDK haben wir festgestellt, dass Pulsar selbst nicht blockierend ist: Der Container kann die Tuya-Echtzeitverbindung öffnen und Ereignisse (einschließlich Klingeldruck) über publishState pushen. Das echte Problem bei der Klingel ist die Veröffentlichung eines Kamerabildes aus einer externen Integration — das SDK unterstützt derzeit nur Zahlen/Text/Zustand, aber nicht das Äquivalent von camera.setImage. Ich habe einen PR in dieser Richtung für das SDK vorbereitet (hoffentlich ist das das, was du von uns in Bezug auf Dinge, die aktuell fehlen, machen möchtest :sweat_smile:)

Und für das lokale Tuya (super komplementär zur Cloud), würden uns zwei Bausteine helfen — ich sehe, dass sie deine #2680 und #2677 schneiden:

  1. Zugriff auf das lokale Netzwerk vom Container aus (um Geräte unter 192.168.x zu erreichen);
  2. die Möglichkeit, Geräteparameter (params) anzugeben/bearbeiten (IP, Protokoll, Cloud/Lokal-Umschaltung) und eine Aktion auf einem Gerät auszulösen mit Rückgabe des Ergebnisses (z. B. eine „lokale DP-Lektüre“ — eine Operation, die ein unerfahrener Benutzer nie erraten würde, daher der Bedarf an einem dedizierten Knopf).

Oder anders:

  • bei Zendure ist jetzt alles für die V1 funktionsfähig :partying_face: Bereit zur Veröffentlichung.
    Wir haben sogar die Issues erstellt, um voranzukommen und eine Implementierungs-Roadmap zu haben :heart_eyes:
  • bei Tuya sind wir fast fertig!! Der lokale Modus funktioniert für Geräte, die über UDP gefunden werden (3 Geräte / 10). Die Befehle gehen lokal UND in die Cloud. Es fehlt nur noch der Statusrücklauf über die Cloud :sweat_smile:

EDIT: Ich habe die Release von gladys-zendure gestartet + das Topic hinzugefügt, gibt es sonst noch etwas zu tun?

Und ein Kommentar von Claude, nachdem wir Probleme mit dem Polling bei den beiden Integrationen Zendure und Tuya hatten, ich weiß nicht, ob er Recht hat, aber es scheint das Problem auf der Zendure-Seite gelöst zu haben:

Hallo Pierre-Gilles,

Bei der Finalisierung meiner ersten externen Integration (gladys-zendure) bin ich auf
poll_frequency der Geräte gestoßen, und beim Vergleich mit deinen anderen Integrationen
glaube ich, eine Inkonsistenz im Template gefunden zu haben, die es wert ist, weitergegeben zu werden.

Die Feststellung: Die poll_frequency des Geräteobjekts (das im Payload von
publishDiscoveredDevices) wird streng vom Core der externen Integrationen überprüft —
es muss ein Wert von DEVICE_POLL_FREQUENCIES sein, IN MILLISEKUNDEN (1000/2000/10000/15000/30000/60000), und das Gerät muss
should_poll: true tragen, um gepollt zu werden. Andernfalls: 422 « invalid poll frequency »
(oder kein Polling, wenn should_poll fehlt).

Genau das macht gladys-melcloud, und das ist korrekt:
poll_frequency: POLL_FREQUENCY, // = 10 * 1000 ms
should_poll: true,

Aber das Template integration-template-js ist inkonsistent mit diesem Vertrag:

  • seine Demo-Geräte haben poll_frequency: config.poll_frequency
  • mit einem Standardwert poll_frequency: 300 in config.js (also 300, interpretiert
    als Sekunden… aber direkt als Gerätefrequenz weitergegeben)
  • und sie setzen nicht should_poll.
    Daher wird das Template, wenn es so installiert und ein Demo-Gerät erstellt/pollt wird,
    durch den Core abgelehnt (300 ist kein Wert von DEVICE_POLL_FREQUENCIES in ms)
    oder nie gepollt (should_poll fehlt).

Die Verwirrung kommt vor allem daher, dass es zwei homonyme « poll_frequency » gibt:

  1. das config_schema-Feld des Manifests = ein einfacher Formularwert
    (Sekunden, vom Integrator gewählt, ohne automatische Verbindung zum Polling);
  2. das poll_frequency-Feld des Geräteobjekts = eingeschränkt auf
    DEVICE_POLL_FREQUENCIES (ms) durch den Core.
    Das Template verbindet sie, indem es (1) direkt in (2) überträgt, was nicht funktionieren kann.

Drei Ansätze zur Auswahl:

  • das Template korrigieren, damit es « out of the box » funktioniert: entweder
    einen gültigen ms-Wert + should_poll: true hardcodieren (wie melcloud), oder
    die Umrechnung Sekunden→ms vornehmen, wie ich es in zendure tun musste;
  • oder den Core toleranter machen: Sekunden akzeptieren und die Umrechnung
    auf Serverseite vornehmen;
  • oder zumindest dokumentieren, dass die poll_frequency des Geräte-Payloads
    auf DEVICE_POLL_FREQUENCIES (ms) beschränkt ist und should_poll: true erfordert.

Zur Information: Auf der Zendure-Seite habe ich einen kleinen Helper, der den config-Wert
(Sekunden, 10–3600) in den nächsten zulässigen ms-Wert umrechnet — ich kann ihn teilen, wenn dich das für das Template interessiert.

Nichts Blockierendes für mich (Zendure funktioniert), es ist nur ein Feedback aus der ersten Erfahrung, das den nächsten die gleiche Falle ersparen könnte.

Ebenfalls:

Rückblick auf das Framework external-integration (mit gladys-tuya unter realen Bedingungen getestet).

Feststellung: Wenn eine externe Integration ein bereits über
publishDiscoveredDevices erstelltes Gerät (mit derselben external_id) mit aktualisierten
Parametern erneut veröffentlicht, werden die Parameter des bestehenden Geräts nicht
auf der Gladys-Seite aktualisiert, und es erscheint keine Option „Aktualisieren“
dem Entdeckungsschirm für externe Integrationen (im Gegensatz zu den nativen
Diensten).

Konkreter Anwendungsfall: Ein Tuya-Gerät wird im Cloud-Modus erstellt. Später
aktiviert der Benutzer den lokalen Modus (die Integration entdeckt das Gerät erneut
mit ip / local_key / protocol_version / local_override nach einem LAN-Scan). Aber
das bereits erstellte Gerät behält seine alten Parameter bei: Es ist unmöglich,
ohne es zu löschen und neu zu erstellen, in den lokalen Modus zu wechseln.

Fragen:

  • Ist das beabsichtigt / geplant? Sollte ein erneutes Veröffentlichen eines
    bereits entdeckten Geräts ein Upsert der Parameter (nach external_id) durchführen?
  • Oder könnte der Entdeckungsschirm der externen Integrationen eine Aktion „Aktualisieren“
    anbieten (wie die nativen Dienste es auf ihrer Geräte-Seite tun), um die
    Parameter aus einer neuen Entdeckung erneut anzuwenden?

Derzeit ist die Benutzerumgehung „Löschen + Neuerstellen“ des Geräts, was
schon wenig ergonomisch ist, sobald sich ein lokaler Parameter ändert
(IP, lokale Schlüsselrotation, Wechsel von Cloud→lokal).

Danke!

Ich habe gerade 4 weitere Gerätetypen + die lokale MQTT-Verwaltung in 35 Minuten implementiert!!! Das ist unglaublich!!

Ah komisch, ich schaue mal, tatsächlich müsste er in diesem Fall « dev » pullen

Ah gut, danke! Normalerweise mache ich das so:

Änderung Spec → Änderung Gladys → Änderung SDK → Änderung Template

Damit alles konsistent bleibt, aber ich schaue mir deine PR an :slight_smile:

Ok, ich schaue mit Claude

Ist das nicht der Fall? Kannst du nicht params in publishDiscoveredDevices freigeben?

Kannst du diesen Teil genauer beschreiben? Besonders « DP » weiß ich nicht, was das bedeutet :sweat_smile:

Ausgezeichnet!!

Du kannst den Status deiner Release auf dieser URL sehen:

Danach basierst du in deinem Fall auf einer Version des SDK, die noch nicht gemerged wurde, also denke ich, dass der Store sie ablehnt, weil das Manifest für ihn nicht gültig ist (normal, man muss warten, bis ich die PR merge)

Gut beobachtet, das ist ein Fehler im Template, ich werde das korrigieren!!

Unglaublich!! Ich kann es kaum erwarten, das alles in die Produktion zu bringen :grin:

Hallo @pierre-gilles, und vielen Dank für dein Feedback

  1. „Update“-Button / Bildversion
    Ja, genau: Der Button versucht, die festgelegte Version des Manifests (z. B. 1.0.0) zu pullen, anstatt « :dev ». Für die Dev-Iteration wäre ein Pull von „dev“ perfekt. Man sollte bedenken, dass ein Dev-Bild eines PRs einen längeren Tag wie « :dev-test-mqtt-local » tragen könnte und dies in der Spezifikation festlegen, um die Dev-Image-Tags zu erzwingen.

  2. „Geräteparameter anzeigen/bearbeiten“ — ich präzisiere
    Parameter für die Entdeckung anzeigen: OK, das funktioniert, ich mache das bereits in publishDiscoveredDevices (ip, local_key, protocol_version, local_override…).
    Der Mangel liegt nicht dort: Es geht um die AKTUALISIERUNG der Parameter eines BEREITS ERSTELLTEN Geräts. Wenn ich ein entdecktes Gerät (selbst mit derselben external_id) mit geänderten Parametern neu veröffentliche — typischerweise die LAN-IP, die sich bei DHCP ändert, oder ein Wechsel von Cloud→Lokal nach einem neuen Scan — werden die Parameter des bestehenden Geräts nicht erneut angewendet, und der Benutzer hat keine Möglichkeit, sie zu bearbeiten/aktualisieren.
    Heute ist der einzige Workaround, das Gerät zu löschen und neu zu erstellen.
    → Bedarf: Entweder ein Upsert der Parameter beim erneuten Veröffentlichen (über external_id), oder eine „Aktualisieren“-Aktion im Entdeckungsbildschirm der externen Integrationen

  3. „DP“ und „Aktion mit Rückgabewert“
    DP = Data Point (Tuya-Terminologie). Jedes Tuya-Gerät stellt nummerierte Datapoints (DP 1 = Schalter, DP 20 = Helligkeit usw.) bereit; der vollständige Zustand = die „DPS map“. Das „Lokale Lesen der DP“ besteht darin, diese Map DIREKT über das LAN mit dem lokalen Tuya-Protokoll zu lesen. Zwei Verwendungszwecke:

  • Validieren, dass die lokale Verbindung und die Protokollversion funktionieren
    BEVOR der lokale Modus aktiviert wird (sonst aktiviert man einen lokalen Modus, der timeout wird);
  • Entdecken/Bestätigen der DP→Feature-Zuordnung.

Es handelt sich um eine Aktion, die vom Benutzer ausgelöst wird, MIT einem Ergebnis, das an die UI zurückgesendet wird (die gelesenen DP oder der Fehler). Der generische Bedarf für externe Integrationen: Die Möglichkeit, eine Benutzeraktion „Führe diese Operation aus und zeige mir das Ergebnis“ (Lokale Verbindungstests, DP-Lesen, Neupairing, Identifizierung…) bereitzustellen, da keine benutzerdefinierte UI vorhanden ist. Das SDK verwaltet heute nur das Pushen von Zuständen und Poll/setValue — nicht diese Art von Anforderungs/Antwort-Aktion auf Anfrage.
Diese Protokollversion und DP-Suche kann nur ausgelöst werden, wenn die IP bekannt ist, und wenn UDP sie nicht gefunden hat, sollte man die IP manuell angeben können, um diese automatische Suche zu starten (es scannt, indem es versucht, für jede Version Polls zu starten) - Und es ist ein ziemlich langer Vorgang - bis zu 15s, glaube ich - um das Gerät nicht zu spammen.

Weitere Rückmeldungen:

  • Durch einen Fehler konnte ich einen zweiten Dev-Container installieren, obwohl ich den alten nicht deinstalliert hatte. Kein Fehler, er wurde als « -2 » installiert. Bei der Installation sollte man gewarnt werden und es sollte unmöglich sein, zwei identische Bilder zu installieren (man sollte jedoch eine Prod- und eine Dev-Version wie derzeit installieren können)
    image

  • Die Installation eines Containers mit dem Tag « :dev » (oder dem Tag « :dev-xxxxxx ») sollte eine Warnung anzeigen: Wenn eine Prod-Instanz daneben läuft, kann dies die andere Instanz beeinträchtigen/zerschießen, es wird empfohlen, den Prod-Container vor der Installation des Tags « :dev » zu stoppen - aber es ist eine sehr gute Sache, eine Dev- und eine Prod-Version nebeneinander installieren zu können

  • Anzeigen der Startzeit des Containers auf der Konfigurationsseite

  • Wenn ein externer Container gestoppt wird, sollte das Polling (und jeder andere Austauschversuch, falls vorhanden) gestoppt werden, um die Log-Pollution zu vermeiden:

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)

  • Wenn eine Integration Cloud und lokal sein kann, mit Geräten, die lokal oder Cloud sein können, sollte eine Spezifikation erzwungen werden:
    • Globale Integration lokal oder Cloud => ein „Lokaler Modus“-Schalter, der für alle Geräte den lokalen Modus priorisiert, wenn aktiviert, Fallback pro Gerät in die Cloud, wenn kein lokaler Modus oder defekt; andernfalls 100% Cloud
      image
    • Ein globales Etikett in der Geräteansicht, das angibt, ob die Anwendung lokal oder in der Cloud definiert ist
    • Ein Etikett pro Gerät, das angibt, ob das Gerät lokal oder in der Cloud ist.
      Das brauchen wir wirklich für Tuya zum Beispiel. Und ich habe denselben Fall bei Zendure (die Tests während der Implementierung des lokalen MQTT bei Zendure haben mir gezeigt, dass ich zwei Geräte hatte, die nicht das lokale MQTT aktiviert hatten, ich habe 30 Minuten gebraucht, ein sichtbares Etikett hätte mir geholfen, das schnell zu verstehen und zur echten Diagnose überzugehen).
      Anschließend kann die Dokumentation der externen Integration einen Abschnitt „FAQ“ oder Hilfe bei der Suche/Lösung von Problemen enthalten
  • Übrigens, was ist mit der Dokumentation? Ich habe nicht in deinen Spezifikationen gesucht, aber hast du vor, sie zu erzwingen, um eine Integration zu validieren?
    Es wäre gut, zum Beispiel (wie bei HA) ein Format für den Seitenfuß und die Hauptkapitel zu definieren - mit einer Vorlage wie für den Container. Mögliche?

Hallo @Terdious, das sind wirklich sehr gute Rückmeldungen :slight_smile:

Ich habe die Spezifikation angepasst und die Implementierung wird folgen:

Die sieben Rückmeldungen sind integriert, jeweils mit der dazugehörigen Design-Entscheidung:

1. Parameter nach der Erstellung — beide geforderten Mechanismen, mit einer klaren Grenze. Die params sind die technischen Integrationsdaten: Beim erneuten Veröffentlichen eines bereits erstellten Geräts (selbe external_id) werden die Parameter vom Supervisor stillschweigend aktualisiert (z. B. die DHCP-IP-Adresse, der Wechsel von Cloud zu lokal), ohne dass ein device-updated-Ereignis an die Integration gesendet wird (sonst würde eine Schleife entstehen: Veröffentlichen → Ereignis → Veröffentlichen). Der name, der Raum und die Features bleiben Eigentum des Benutzers: Wenn sich die veröffentlichte Struktur unterscheidet, zeigt der Entdeckungsbildschirm einen Button „Aktualisieren“ — eine Benutzeraktion über den Standard-POST /api/v1/device. Kein Löschen/Neuerstellen mehr.

2. Aktionsbuttons — neues Feld actions im Manifest (max. 10): key, mehrsprachige Beschriftung/Beschreibung und vor allem zwei Dinge, die auf den Tuya-Fall zugeschnitten sind: ein optionales fields im exakten Format des config_schema (das Mini-Formular „IP eingeben“ wird vom selben Motor gerendert und validiert) und eine timeout_seconds pro Aktion (5–120, Standard 30), die die 5-Sekunden-Regel für action.run ersetzt — das Scannen der Protokollversionen bei ~15 s funktioniert. Vollständige Kette: Button → POST .../action/:key → WS action.run → Ergebnis in command-result.data.message (String oder mehrsprachig) wird unter dem Button angezeigt. SDK: onAction('detect_protocol', async (fields) => '...').

3. Doppelte Instanz für Entwicklung/Produktion: Bei der Installation (alle Modi), wenn eine installierte Integration dasselbe Bild ohne Tag (:dev neben :1.2.0) oder denselben Manifest-name teilt → explizierte Warnung (möglicher Konflikt auf demselben Cloud-Konto / denselben Geräten, Empfehlung, die andere Instanz während der Tests zu stoppen), aber die Installation bleibt möglich — Entwicklung neben Produktion ist ein gewollter Gebrauch, die Selektoren ext-dev-* garantieren die Abwesenheit technischer Kollisionen.

4. Startzeit: started_at des Hauptcontainers (und jedes Untercontainers) im Admin-Detail, angezeigt im Überwachungsblock („läuft seit…“).

5. Polling einer gestoppten Integration: Das geplante poll wird zu einem stillschweigenden No-Op, wenn die Integration nicht RUNNING/DEGRADED ist — weder ein Throw noch eine Logzeile alle N Sekunden für einen bereits bekannten Zustand. setValue wirft weiterhin einen Fehler: Der Benutzer, der die Aktion auslöst, muss den Fehler sehen.

6. Feldtypen: boolean gab es bereits (als Schalter/Checkbox gerendert — präzisiert); Hinzufügen von multi_select (Checkboxen, Wert = Array) und display: "radio" für select.

7. Verpflichtende Dokumentation im Store: Das Veröffentlichen erfordert nun docs/en.md und docs/fr.md, strukturiert nach der Vorlage im Template-Repo (Einführung / Voraussetzungen / Konfiguration / Fehlerbehebung). Der Indexer überprüft Anwesenheit + Mindestgröße (sonst error-Ablehnung), rehostet die Dateien auf Pages wie die Covers (docs in index.json), und der Installationsbildschirm erhält einen Link „Dokumentation“ (Benutzersprache, Fallback en).

Ich finde, das ist ein bisschen zu spezifisch für das, was du machst. Ich bevorzuge etwas Generisches :slight_smile:

Also tatsächlich, Zendure ist weder in https://integration-store-storage.gladysassistant.com/index.json noch in https://integration-store-storage.gladysassistant.com/rejected.json ^^
Dagegen ist gladys-tuya (dessen PR ich noch nicht gemerged habe) tatsächlich in 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"
  }
]

Aber du musst uns eine echte visuelle Seite machen, oder? :face_with_peeking_eye: :sweat_smile:

Fable 5 wird das für euch machen, keine Sorge :rofl:

@Terdious hast du also Tests seit dem PR #2680 durchgeführt? Bestätigst du mir, dass es gut funktioniert hat?

Na klar, ja, ich würde sagen: perfekt :slight_smile:

Für die neuen Features, die du angefragt hast, habe ich GPT 5.6 Sol 1M High gebeten, sie zu überprüfen, und er findet, dass es zu generisch ist, Zendure, und ich bin eher seiner Meinung :slight_smile:

Die Überprüfung: Add battery-storage device feature category by Terdious · Pull Request #2682 · GladysAssistant/Gladys · GitHub

Danke Pierre-Gilles, im Großen und Ganzen ist alles gut für mich:

Danke für alle Korrekturen / Implementierungen

Ein einziger Punkt, bei dem ich etwas nachdrücke — das „zu spezifisch / eher generisch“ in Bezug auf
Modus lokal/cloud + Makronen:

Ich denke, das ist genau ein GENERISCHES Bedürfnis, nicht spezifisch für Tuya. Das wiederkehrende
Muster = „eine Integration, die sowohl Cloud als auch lokal abdeckt, mit Geräten, deren
effektiver Transport von einem Gerät zum anderen variieren und sich im Laufe der Zeit ändern kann“. Beispiel
aus dem Stegreif:

Integration Dualität Cloud/Lokal Status
Tuya Cloud + LAN pro Gerät in Gladys (→ extern)
Netatmo Wetter = Voll-Cloud; Kameras/Klingel = Cloud + lokal (lokal viel schneller beim Bild, zuverlässiger, da die Cloud-Verbindung abreißen kann) in Gladys, im Übergang zu extern
Zendure Cloud + lokale MQTT extern (deins)
Shelly HTTP/WS lokal + Shelly Cloud umwandelbar / in Arbeit
eWeLink / Sonoff Cloud + LAN-Modus umwandelbar / in Arbeit
Somfy TaHoma Cloud + lokale API umwandelbar / in Arbeit

Der Baustein, der in den Framework gehört, ist nicht die Tuya-Logik,
es ist:

  1. ein TRANSPORTSTATUS PRO GERÄT, standardisiert und agnostisch
    (lokal / Cloud / nicht erreichbar), den die Integration meldet und den Gladys
    als Makro anzeigt — + ein globales Makro in der Geräteansicht;
  2. optional eine STANDARDISIERTE VERBINDUNGSVORKEHR („lokal bevorzugen, wenn verfügbar, sonst Cloud“) — das ist genau die Bezeichnung deines
    Toggles „Lokaler Modus (LAN)“.
    Die Integration füllt aus, Gladys gibt an → 100 % generisch, null Tuya-Semantik
    im Core.

Der Wert ist vor allem diagnostisch und universell: Ohne sichtbares Makro
kann der Benutzer nicht erkennen, warum ein Gerät langsam oder eingefroren ist. Mein
eigenes Zendure-Beispiel (2 Geräte ohne lokales MQTT, 30 Minuten verloren): Ein Makro
hätte mich sofort auf die richtige Spur gebracht.

Und es ist nicht nur eine Integration, dieselbe Logik gilt für alle Geräte in der obigen Tabelle. Neulich hast du mir von Shelly erzählt, es ist dasselbe, man muss das MQTT auf der lokalen Weboberfläche http aktivieren, um das lokale MQTT und/oder die Cloud zu haben. Das Muster taucht auf, sobald ein Hersteller beide Kanäle anbietet — daher das Interesse an einer wiederverwendbaren „Transportstatus + Präferenz“-Primitivfunktion statt einer Behandlung pro Integration.

Außerdem ist es relativ einfach, es auf Gladys Core umzusetzen: Ein Toggle, das auf der Konfiguration definiert ist (das gleiche für alle) und ein Parameter auf der Geräteseite (ebenso in der Spezifikation definiert). Danach ist es nur noch eine interne Angelegenheit im Container!

Okay, danke für die Erklärung, das passt für mich, ich habe die Spezifikation angepasst!

Ich kläre gerade mit Claude den Zugang zum lokalen Netzwerk.

Für den normalen lokalen Zugriff ist es eigentlich schon möglich, bist du sicher, dass es dich blockiert hat?

Meine Meinung: Es ist bereits im Design möglich — und es ist wichtig, nichts zu „reparieren“, was nicht kaputt ist. Der NAT-Bridge blockiert kein ausgehendes Unicast zum LAN: Ein Container auf gladys-integrations kann eine TCP/UDP-Verbindung zu 192.168.1.50 (lokales Tuya-Protokoll Port 6668, lokale Shelly-HTTP-API…) öffnen, genau wie er eine Cloud verbindet — Docker maskiert die Ausgabe, nichts wird gefiltert. enable_icc=false isoliert nur die Container untereinander auf der Bridge, nicht den Verkehr nach außen. Die einzige echte Grenze, bereits in B.2/B.16 dokumentiert, ist der Empfang von Broadcast/Multicast (UDP-Scan zur Entdeckung) — und ich vermute, dass dies die Quelle des Missverständnisses ist: « Kein Broadcast » wurde als « Kein LAN » gelesen. Wenn er tatsächlich einen Unicast-Fehler in seiner Entwicklungsumgebung festgestellt hat, liegt die Ursache außerhalb des Frameworks — der klassische Fall ist ein strenges Host-Firewall (ufw), das den FORWARD von Bridge→LAN-Interface DROP: genaues Symptom « Cloud OK, LAN KO ».

Ich überprüfe nochmal, ob nicht er (und ich, der falsch geführt hat…) es falsch gemacht hat!! Wenn es so ist, entschuldige ich mich für die Störung.

SDK in Version 0.4.0 veröffentlicht, Claude passt das template-js an, und ich führe gerade die Merges von Gladys in die Basis-PR durch

Edit: Merge durchgeführt, alles ist jetzt in dieser PR: https://github.com/GladysAssistant/Gladys/pull/2665