Erlauben Sie Integrationen, eigene Dashboard-Widgets zu deklarieren (JSON-Schema)

Der Bedarf

Heute werden alle Widgets des Dashboards im Kern von Gladys entwickelt. Eine Integration (insbesondere externe, über das SDK) kann kein spezifisches Widget für ihren Bereich anbieten, obwohl die Daten dort oft erst ihren vollen Sinn ergeben: Überwachung der Solarstromproduktion, Status eines Robotersaugers, Ladeplanung eines Elektroautos usw.

Der Vorschlag

Es sollte einer Integration ermöglicht werden, ein oder mehrere Widgets über ein JSON-Schema zu deklarieren, nach demselben Prinzip wie die Konfigurationsseiten der externen Integrationen. Die Integration beschreibt, was angezeigt werden soll (Werte, Diagramme, Schaltflächen, Zustände, Anzeigen…), und Gladys entscheidet, wie es angezeigt wird.

Absichtlich gibt es kein benutzerdefiniertes HTML oder iframes: Das ist eine philosophische Entscheidung. Indem es deklarativ bleibt, garantiert der Kern von Gladys für alle Widgets, einschließlich Dritthersteller: visuelle Konsistenz, Dark Mode, Responsivität für Mobilgeräte/Tablets, Übersetzungen, Performance und keine Rückwärtsinkompatibilität, wenn sich die Oberfläche weiterentwickelt. Es ist dasselbe Modell wie die Widgets von iOS: rein deklarativ, und niemand findet, dass das Ökosystem an Vielfalt mangelt :slightly_smiling_face:

Die Vorteile

  • Die Integrationen werden vollständig: Ihre Daten haben endlich einen echten Platz auf dem Dashboard
  • Das Erlebnis bleibt sauber und einheitlich, unabhängig vom Autor des Widgets
  • Da das JSON validierbar ist, kann eine KI diese Widgets zuverlässig generieren oder reparieren (und langfristig ein komplettes Dashboard auf Anfrage erstellen)
  • Der Wortschatz der Komponenten kann sich schrittweise im Kern erweitern, ohne jemals etwas zu brechen, und jede Ergänzung profitiert allen Integrationen auf einmal

Gedanken zur Umsetzung

  • Den Anfangsvokabular definieren: Welche Grundkomponenten? (Wert + Einheit, historisches Diagramm, Aktionsschaltfläche, Zustandsliste…)
  • Wie das Widget seine Daten abruft: vorhandene Gerätefunktionen oder von der Integration bereitgestellte Endpunkte?
  • Das Schema versionieren, damit die Widgets mit den Updates kompatibel bleiben

Sehr sehr gute Idee, das erinnert an den Prinzip der Android-Apps, die Widgets vorschlagen können :+1:

Hallo zusammen!

Dieses Thema ist nun in Entwicklung.

Ein PR wurde eröffnet, um die Dashboard-Widgets zu spezifizieren, die von den Integrationen deklariert werden (JSON-Schema):

Zögert nicht, dem PR zu folgen, zu testen (optional, vor allem für kleine Anfragen) und euer Feedback hier zu hinterlassen, falls nötig.

Hallo zusammen! :waving_hand:

Die Entwicklung ist bereit zum Testen, und ich würde mich sehr über Feedback von mehreren Integrationsentwicklern freuen, um sicherzustellen, dass die API den tatsächlichen Anwendungsfällen entspricht, bevor sie veröffentlicht wird.

Hier ein Vorschau des Ergebnisses:

Der PR enthält die API-Spezifikation (du kannst sie auch an Claude weitergeben, um eine Integration zu entwickeln, bis sie gemerged wird): https://github.com/GladysAssistant/Gladys/pull/3109

Das Docker-Image zum Testen:

ghcr.io/gladysassistant/gladys-preview:claude-exciting-babbage-waa7zm

@spenceur Könntest du deine Kinofilm-Integration als externe Integration mit dieser API neu erstellen und mir sagen, ob sie deinen Anforderungen entspricht? :slightly_smiling_face:

Der Zeitplan ist recht knapp, daher bin ich für jedes Feedback schnell dankbar. Das Ziel ist es, das Ganze am Montag zu veröffentlichen, wenn alles gut läuft! :rocket:

Ich werde das am Wochenende testen, aber die Screenshots gefallen mir schon mal :+1:

Arfffff danke @pierre-gilles ich bin vor Sonntag nicht so verfügbar :sweat_smile::sweat_smile: versprochen, ich schaue mir das am Sonntagabend an (falls es bis dahin warten kann?)

Ps: Ich schaue mir das heute Abend an, sobald ich kann, ich habe das Ende der Nachricht gesehen :smiling_face_with_tear:

@pierre-gilles
Widget:



Auslöser


Falls das Plugin abstürzt:


Der Auslöser wird immer angezeigt, aber Gladys funktioniert korrekt :slight_smile:

Alles funktioniert.
Kannst du mir eine Filterfunktion vom Typ « contains » (Groß-/Kleinschreibung irrelevant) für die Auslöser hinzufügen? :sweat_smile: :smiley:

Ausgezeichnet, danke für den Test! Das macht die Filme richtig gut :grin:

Das kann ich hinzufügen!

Ich habe ein Problem mit bestimmten Bildern, die die von Gladys festgelegte Größenbegrenzung überschreiten

Falls es hilft, hier ist, was Claude sagt:
Technische Details:

  • Der Core begrenzt die Widget-Bilder auf 300 KB (MAX_WIDGET_IMAGE_BYTES = 300 * 1024 in externalIntegration.normalizeWidgetImage.js), eine bewusste Grenze, um zu verhindern, dass ein Widget ein riesiges Bild im Browser lädt.
  • Ich habe die Poster direkt vom AlloCiné-CDN (all.web.img.acsta.net) überprüft: Diejenigen, die gut angezeigt werden, sind 182-241 KB groß, die beiden defekten sind 341 KB und 378 KB — also über dem Limit.
  • Wenn onWidgetGetImage ein zu großes Bild zurückgibt, lehnt der Core die Antwort auf Serverseite ab und die Frontend-Anzeige zeigt nur das Symbol für ein defektes Bild mit einer generischen Fehlermeldung (REQUEST_TO_THIRD_PARTY_FAILED), die nicht explizit „zu groß“ sagt — das hat mich eine Weile gekostet, um das zu verfolgen.

Das ist kein Bug im Integrationscode: widget.js behandelt alle Bilder genau gleich, es ist nur so, dass das AlloCiné-CDN manchmal Poster liefert, die größer sind als die Gladys-Grenze für bestimmte Filme. Es gibt keinen Parameter in der CGR-Poster-URL, um eine leichtere Version anzufordern, also nichts zu korrigieren auf Integrationsseite.

Getestet mit Intégration externe - Prochains passages de l'ISS :rocket:
Keine Probleme, weder bei der Entwicklung noch bei den Tests (es ist ein benutzerdefiniertes Widget, aber ohne Konfiguration)

Ich habe diesen PR von vorne bis hinten mit einem gefälschten Integrationsprovider getestet, der ein Gezeiten-Widget über das WebSocket-Protokoll bereitstellt (ohne Docker oder SDK — das SDK 0.13.0 stellt die Widget-Handler noch nicht bereit). Der Mechanismus funktioniert: Das Widget erscheint im Picker, die Einstellungen werden validiert, der Inhalt wird normalisiert, die Aktion Hin- und Rückfahrt und der Widget.nudge-Refresh verhalten sich wie spezifiziert. Die Rohrleitungen sind solide.

Dann habe ich einen konkreten Fall ausprobiert: das Gezeiten-Widget von #3028. Das Ergebnis ist ein nützlicher Datenpunkt, weil der Unterschied nicht kosmetisch ist.

Zuerst das Kern-Widget von #3028; dann, was ich tun konnte:


Drei Dinge sind strukturell unmöglich, und nicht nur weniger schön:

  1. Die Gezeitenuhr — ein Zifferblatt mit einem Zeiger, der die Position im Zyklus und die verbleibende Zeit bis zur Flut anzeigt. Es gibt keine Zifferblattkomponente, und weder value, noch gauge, noch chart approximieren dies: Ein gauge ist ein radialer Bogen für ein Verhältnis, kein Zifferblatt mit zwei beschrifteten Polen PM/BM.

  2. Die Anmerkungen auf der Kurve — die Marker für Hoch- und Niedrigwasser mit ihrer Uhrzeit und Höhe, die Koeffizienten-Badges, der gestrichelte Bezugspunkt des aktuellen Moments. Chart nimmt nur Punkteserien, daher verliert die Kurve genau die Information, für die man sie liest. Man betrachtet eine Gezeitenkurve nicht wegen ihrer Form, sondern um darin « 07:48, 10,69 m, Koeff. 74 » zu lesen.

  3. Die Registerkarten der Tage (heute + 6 Tage). Ich verstehe, dass dies absichtlich ist — « ein Widget wird gelesen und berührt, es ist keine Seite » — aber eine Gezeit ohne den nächsten Tag verliert ihren Sinn: Man konsultiert eine Gezeit, um einen Ausflug vorzubereiten, also im Voraus. Eine Card-List von 7 Elementen würde gut zu einem Fokus passen, aber sie würde den Platz der Kurve einnehmen, die das Herz des Widgets ist.

Was das nicht infrage stellt. Die Fähigkeit bleibt die richtige Antwort für den Pilotfall TMDB und für die in der Spezifikation genannten Domänen-Widgets. Mein Punkt ist enger: Die Trennlinie « Domänen-Widgets vs. generische Steuerungs-Widgets » reicht nicht aus, um ein Widget wie die Gezeit zu klassifizieren. Es ist kein Steuerungs-Widget, daher schließt die Sektion « außerhalb des Umfangs » es nicht aus; aber seine Darstellung basiert auf einer domänenspezifischen Darstellung (ein Zifferblatt, eine annotierte Kurve), die das Vokabular nicht beschreiben kann.

Die Spezifikation sagt « das schlechteste mögliche Drittanbieter-Widget ähnelt einem etwas überladenen Kern-Widget » — hier tritt das Gegenteil auf: Das beste Widget, das das Vokabular zulässt, liegt deutlich unter dem, was das Kern-Widget bereits kann.

Zwei Vorschläge im Sinne des additiven Prinzips 7:

  • Eine Dial-Komponente (Wert, min, max, zwei Pol-Beschriftungen, zentrale Beschriftung) würde gauge als zweite radiale Komponente ergänzen. Sie dient der Gezeit, aber auch einer Windrose, einer Position in einem Ladezyklus, einer Mondphase…
  • Optionale Anmerkungen auf chart: eine begrenzte Tabelle von { t, label, value, color }, die das Kern-Widget in Markierungen rendert, plus ein Boolean now_marker. Additiv, von einem älteren Kern ignoriert, und nützlich weit über die Gezeit hinaus (Solarerzeugungs-Spitzen, Tarifzeitfenster, geplante Ladeende).

@pierre-gilles Alles klar bei der Integration der Kraftstoffpreise:

Edit: Ich wollte den Graphen auf 30 Tage festlegen, aber Claude hat mir Folgendes zurückgemeldet:

Bei den 30 Tagen, um es klar zu sagen: Der Titel bleibt „Letzte 30 Tage“ und die Achse erstreckt sich nach links mit den Messungen, bis ein Monat abgedeckt ist. Ich kann keine 30-Tage-Achse ab dem ersten Tag anzeigen, ohne Preise zu erfinden, die wir nie gemessen haben — die Frontend-Schnittstelle erlaubt es nicht, ein Min/Max der Achse zu erzwingen, und erfundene Punkte wären falsche Daten in einem Preischart. Wenn du trotzdem eine feste 30-Tage-Achse möchtest, wäre der einzige ehrliche Weg ein Patch auf der Core-Seite (xaxis.min/max im Chart-Komponente freilegen).

@pierre-gilles Was denkst du? Ist das relevant?

Bei den 30 Tagen „nicht ausgefüllt“ stört mich das nicht und das ist bereits das, was mit anderen Sensoren passiert, wenn du nur wenige Tage Daten hast und diese auf 30 anzeigst. Das Diagramm zeigt nur, was es hat, und beginnt links erst am ersten Datum der Datenbank.