Aujourd’hui, les widgets du dashboard sont tous développés dans le cœur de Gladys. Une intégration (notamment externe, via le SDK) ne peut pas proposer de widget spécifique à son domaine, alors que c’est souvent là que la donnée prend tout son sens : suivi de production solaire, état d’un robot aspirateur, planning de charge d’une voiture électrique, etc.
La proposition
Permettre à une intégration de déclarer un ou plusieurs widgets via un schéma JSON, sur le même principe que les pages de configuration des intégrations externes. L’intégration décrit quoi afficher (valeurs, graphiques, boutons, états, jauges…), et c’est Gladys qui décide comment l’afficher.
Volontairement, pas de HTML custom ni d’iframe : c’est un choix de philosophie. En restant déclaratif, le cœur de Gladys garantit pour tous les widgets, y compris tiers : la cohérence visuelle, le dark mode, le responsive mobile/tablette, les traductions, les performances et la non-régression quand l’interface évolue. C’est le même modèle que les widgets iOS : purement déclaratifs, et personne ne trouve que l’écosystème manque de richesse
Les bénéfices
Les intégrations deviennent complètes : leurs données ont enfin une vraie place sur le dashboard
L’expérience reste propre et unifiée, quel que soit l’auteur du widget
Le JSON étant validable, une IA pourra générer ou réparer ces widgets de manière fiable (et à terme, construire un dashboard entier à la demande)
Le vocabulaire de composants peut s’enrichir progressivement dans le core, sans jamais rien casser, et chaque ajout profite à toutes les intégrations d’un coup
Pistes de réflexion
Définir le vocabulaire de départ : quels composants de base ? (valeur + unité, graphique historique, bouton d’action, liste d’états…)
Comment le widget récupère ses données : features d’appareils existantes, ou endpoint exposé par l’intégration ?
Versionner le schéma pour que les widgets restent compatibles au fil des mises à jour
Le développement est prêt à être testé, et j’aimerais beaucoup avoir les retours de plusieurs développeurs d’intégrations pour vérifier que l’API répond bien aux cas d’usage réels avant la sortie.
si ça peut t’aider voila ce que dit claude : Le détail technique :
Le core plafonne les images de widget à 300 Ko (MAX_WIDGET_IMAGE_BYTES = 300 * 1024 dans externalIntegration.normalizeWidgetImage.js), une limite volontaire pour éviter qu’un widget charge une image énorme dans le navigateur.
J’ai vérifié en récupérant les posters directement depuis le CDN AlloCiné (all.web.img.acsta.net) : ceux qui s’affichent bien font 182-241 Ko, les deux cassés font 341 Ko et 378 Ko — au-dessus du seuil.
Quand onWidgetGetImage renvoie une image trop lourde, le core rejette la réponse côté serveur et le front affiche juste l’icône d’image cassée, avec un message d’erreur générique (REQUEST_TO_THIRD_PARTY_FAILED) qui ne dit pas explicitement « trop lourde » — ça m’a pris un moment à tracer.
Ce n’est pas un bug dans le code de l’intégration : widget.js fait exactement la même chose pour toutes les images, c’est juste que le CDN AlloCiné sert parfois des posters plus lourds que la limite de Gladys pour certains films. Il n’y a pas de paramètre de redimensionnement dans l’URL du poster CGR pour demander une version plus légère, donc rien à corriger côté intégration
J’ai testé cette PR de bout en bout avec une fausse intégration provider qui sert un widget de marée en parlant le protocole WebSocket brut (sans Docker ni SDK — le SDK 0.13.0 n’expose pas encore les handlers widget). Le mécanisme fonctionne : le widget apparaît dans le picker, les settings sont validés, le contenu est normalisé, l’aller-retour d’action et le nudge widget.refresh se comportent comme spécifié. La plomberie est solide.
J’ai ensuite essayé un cas concret : le widget de marée de #3028. Le résultat est un point de donnée utile, parce que l’écart n’est pas cosmétique.
À premier, le widget cœur de #3028 ; ensuite, ce que j’ai pu faire:
Trois choses sont structurellement impossibles, et pas seulement moins jolies :
L’horloge de marée — un cadran avec une aiguille indiquant la position dans le cycle et le temps restant avant l’étale. Il n’existe pas de composant cadran, et ni value, ni gauge, ni chart ne l’approxime : une gauge est un arc radial pour un ratio, pas un cadran à deux pôles étiquetés PM/BM.
Les annotations sur la courbe — les marqueurs de pleine et basse mer avec leur heure et leur hauteur, les badges de coefficient, le repère pointillé de l’instant présent. chart ne prend que des séries de points, donc la courbe perd exactement l’information pour laquelle on la lit. On ne regarde pas une courbe de marée pour sa forme, mais pour y lire « 07h48, 10,69 m, coef. 74 ».
Les onglets de jours (aujourd’hui + 6 jours). Je comprends que celui-ci soit délibéré — « un widget se lit et se touche, ce n’est pas une page » — mais une marée sans le lendemain perd sa raison d’être : on consulte une marée pour préparer une sortie, donc à l’avance. Un card-list de 7 items ferait bien un focal, mais il mangerait la place de la courbe, qui est le cœur du widget.
Ce que ça ne remet pas en cause. La capability reste la bonne réponse pour le cas pilote TMDB et pour les widgets de domaine cités dans la spec. Mon point est plus étroit : la ligne de partage « widgets de domaine vs widgets de contrôle génériques » ne suffit pas à classer un widget comme la marée. Ce n’est pas un widget de contrôle, donc la section « hors périmètre » ne l’exclut pas ; mais son rendu repose sur une représentation spécifique au domaine (un cadran, une courbe annotée) que le vocabulaire ne peut pas décrire par construction. La spec dit « le pire widget tiers possible ressemble à un widget cœur un peu chargé » — ici, le problème inverse se produit : le meilleur widget que le vocabulaire permette est nettement en dessous de ce que le cœur sait déjà faire.
Deux suggestions, dans l’esprit additif du principe 7 :
Un composant dial (valeur, min, max, deux étiquettes de pôles, libellé central) rejoindrait gauge comme second composant radial. Il sert la marée, mais aussi une rose des vents, une position dans un cycle de charge, une phase lunaire…
Des annotations optionnelles sur chart : un tableau borné de { t, label, value, color } que le cœur rend en marqueurs, plus un booléen now_marker. Additif, ignoré par un cœur plus ancien, et utile bien au-delà de la marée (pics de production solaire, créneaux tarifaires, fin de charge prévue).
Édit : Je voulais fixer le graphique sur 30 jours mais Claude m’a fais ce retour :
Sur les 30 jours, à être clair : le titre reste « 30 derniers jours » et l’axe s’étend vers la gauche au fil des relevés jusqu’à couvrir un mois. Je ne peux pas afficher un axe de 30 jours dès le premier jour sans inventer des prix qu’on n’a jamais mesurés — le front ne permet pas de forcer un min/max d’axe, et fabriquer des points serait de la fausse donnée sur un graphique de prix. Si tu préfères quand même un axe figé à 30 jours, la seule voie honnête serait un patch côté core (exposer xaxis.min/max dans le composant chart)
Pour les 30 jours « non remplis », ça ne me choque pas et c’est déjà ce qu’il se passe avec d’autres capteurs quand tu n’as que quelques jours de données et que tu les affichent sur 30, le graphique ne met que ce qu’il a et ne démarre à gauche qu’à la première date des données en base.