You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Quatrième issue du milestone v10.0.0. Rend l'éclairage configurable et associable par
l'utilisateur final, sans qu'aucun événement de jeu ne soit encore câblé.
Re-planifiée le 2026-09-03 : la voie BLE est abandonnée (spike #204 — défaut systémique entre
la pile Bluetooth de Windows 11 et les ampoules Hue). L'appairage hors bande par bluetoothctl /
Paramètres Windows qui figurait ici est caduc. La voie retenue est le pont Hue en REST
local, validée sur matériel réel.
Contrat : contracts/hue-bridge.md §6 et §7.
🎨 Maquette — à valider AVANT tout développement frontend
docs/mockups/lighting-hue-config-207.html
Règle projet : une maquette d'interface est validée par l'utilisateur avant le dispatch de dev-frontend. Révision 2 (2026-09-03) : la configuration vit désormais dans une page dédiée atteignable
par une entrée « Ambiance » du menu abeille, et non plus dans une section de /admin/settings.
La maquette couvre les quatre états, les trois étapes du parcours, l'aperçu de ce que #213
ajoutera, les cas dégradés et les décisions de conception.
Révision 3 (2026-09-04) — maquette VALIDÉE, gate UI levé. Les deux points d'arbitrage sont
tranchés et intégrés :
✅ Ampoule d'état sur l'entrée de menu « Ambiance », et non la pastille booléenne de
« Mises à jour ». Révision 4 : trois glyphes distincts, pas une même forme recolorée —
ampoule pleine avec rayons (verte) = pont configuré et qui répond · ampoule en contour +
pastille d'alerte (orange) = pont configuré mais qui ne répond plus, ou qui refuse la clé ·
ampoule en contour nu (grise) = aucun pont configuré. Correspondance normative des états de GET /api/lighting/status en contracts/hue-bridge.md§7.1 ; tracés de référence en §01 de
la maquette. C'est la forme qui porte l'état, la couleur ne fait que renforcer : la distinction reste
lisible en niveaux de gris et pour un daltonien, là où trois teintes sur une forme unique
auraient été indiscernables à 15 pixels. ⚠️Ni la couleur ni la forme d'un emoji ne sont pilotables — elles viennent de la police
système. Les trois glyphes sont donc des SVG en ligne tracés en currentColor. Aucune modification du code de rendu du menu : Navbar.jsx écrit déjà <span className="menu-icon">{item.icon}</span> et React y accepte un élément comme une
chaîne — seule la définition de l'entrée change. ⚠️title en toutes lettres malgré tout, SVG aria-hidden : un lecteur d'écran ne voit aucune
forme.
Rafraîchissement au montage, toutes les 30 s, et après tout enregistrement — l'endpoint ne
fait aucune I/O. Le précédent useUpdates (appel unique au montage, Navbar.jsx:87-89) ne
suffit pas : un pont peut devenir injoignable pendant une session.
Contrainte de déploiement qui simplifie l'écran
Le pont de production sera dédié à BuzzMaster — pas le pont domestique de l'utilisateur. Il n'y
a donc pas de lumières étrangères à filtrer. L'écran propose toutes les ampoules cochées par
défaut, mais les affiche explicitement et permet de les décocher : on ne suppose jamais en
silence que tout ce qui est sur le pont nous appartient.
La clé du pont est un secret, même régime que les clés IA : masquée dans GET /config.json
(maskedConfigJSON, http.go:1320), motif « absente ⇒ préservée, clear_api_key ⇒ effacée, api_key_configured dérivé jamais persisté », surcharge BUZZCONTROL_HUE_API_KEYsans
écriture disque, jamais journalisée. ⚠️Aucun champ de saisie de clé dans l'écran : elle s'obtient par l'appui sur le bouton du
pont, pas au clavier. Un champ vide proposerait un geste impossible.
Endpoints (§7 du contrat) : POST /api/lighting/discover, POST /api/lighting/register, GET /api/lighting/lights, POST /api/lighting/test, GET /api/lighting/status. GET /api/lighting/statusne fait aucun appel bloquant — il renvoie l'état connu du pilote.
Page dédiée /admin/ambiance, atteignable par une entrée « Ambiance » du menu abeille
(révision du 2026-09-03 — décision utilisateur). Concrètement :
une entrée dans menuItems (web/src/components/Navbar.jsx l. 146-155) : { path: 'ambiance', label: 'Ambiance', icon: '💡' }, placée juste après Config ;
une entrée dans adminRoutes (web/src/App.jsx l. 27-40) : { path: 'ambiance', element: <AmbiancePage /> } — mécanisme de routage existant, rien de neuf ;
web/src/pages/AmbiancePage.jsx + .css + .test.jsx, sur le squelette des autres pages
admin (header.page-header puis une <Card>).
ConfigPage.jsx n'est pas modifiée — c'est tout l'objet de la révision.
web/src/hooks/useLightingStatus.js — hook sur le modèle de useUpdates.js, alimentant
l'ampoule du menu.
web/src/pages/BackupPage.jsx — renommage du seul libellé « Configuration Ambiance » →
« Réglages de jeu » (3 occurrences : l. 79, 149, 227). Ne toucher ni la clé ambiance de
l'objet d'options, ni le paramètre ambiance=true de la requête.
Le POST de configuration ré-émet toujours la section lighting complète (précédent normatif
de la section IA : un payload partiel remet les autres champs à leurs défauts).
Badge d'état à quatre valeurs : Non configuré / Pont connecté / Pont injoignable /
Association refusée. Forme reprise du badge tri-état des clés IA (ai-key-badge). ⚠️Ne jamais fondre « injoignable » et « refusée » — les gestes correctifs sont opposés.
Parcours d'association en ligne, pas en modale : l'appui sur le bouton du pont est une attente temporisée, pas une décision binaire. Le projet ne réserve la modale qu'à l'aide
statique (ApiKeyHelpModal) et aux décisions bloquantes (ApiKeyValidationDialog) ; il n'existe
aucun précédent de modale d'attente dans cette page.
L'erreur « bouton non pressé » (Hue 101) est un cas nominal : l'écran attend et propose de
réessayer, il ne s'alarme pas.
Test d'éclairage par ampoule et global : un flash bref, puis retour à l'état antérieur.
À corriger au passage : .wifi-toast-warning n'est défini nulle part dans server-go/web/src/ — un toast d'avertissement s'affiche aujourd'hui en texte blanc sur fond
transparent. Cet écran en a besoin.
Hors portée
Câblage sur les événements de jeu (#205, livré). Affectation par équipe (#213). Conduite manuelle
et restitution d'état (#208).
Critère de fait
Maquette validée par l'utilisateur avant le dispatch de dev-frontend.
Serveur sans section lighting : démarrage, sauvegarde et rechargement de config.json
inchangés, aucune découverte réseau, aucune ligne de log. La page /admin/ambiance s'affiche
quand même, réduite à son bouton « Rechercher un pont ».
Aucune régression sur ConfigPage.jsx, qui n'est pas touchée par cette issue.
L'ampoule du menu montre les trois glyphes conformément à hue-bridge.md §7.1, et le title
dit l'état en toutes lettres. Vérifié en niveaux de gris : les trois états restent
distinguables sans la couleur. Vérifié aussi sans pont configuré : contour nu, sans pastille,
et aucune apparence d'alerte.
BackupPage.jsx : libellé renommé, et une archive produite avant le renommage se
restaure toujours (le format n'a pas changé).
Round-trip de config.json : enregistrer une autre section (IA, serveur) ne détruit ni ne
videlighting, et réciproquement.
Aucun secret dans GET /config.json, dans les logs, ni dans une archive de sauvegarde. (Vérifié au cadrage : config.json vit à la racine du serveur, hors du dataDir archivé par handleFSBackup — ne pas déplacer la section vers game-config.json.)
Clé fournie par BUZZCONTROL_HUE_API_KEY : rien n'est écrit sur disque (précédent explicite, docs/TEST_PROCEDURE.md §9 scénario 7).
Aucun pont sur le réseau ⇒ message explicite et repli « saisir l'adresse manuellement » ; l'écran
ne reste jamais à charger indéfiniment.
Le bouton « Tester » allume réellement l'ampoule — seul scénario exigeant du matériel.
Dépendances
contracts/hue-bridge.md (écrit). Parallélisable avec #206 : le front travaille depuis le contrat,
et test-writer depuis la maquette.
Contexte
Quatrième issue du milestone v10.0.0. Rend l'éclairage configurable et associable par
l'utilisateur final, sans qu'aucun événement de jeu ne soit encore câblé.
Contrat :
contracts/hue-bridge.md§6 et §7.🎨 Maquette — à valider AVANT tout développement frontend
docs/mockups/lighting-hue-config-207.htmlRègle projet : une maquette d'interface est validée par l'utilisateur avant le dispatch de
dev-frontend. Révision 2 (2026-09-03) : la configuration vit désormais dans une page dédiée atteignablepar une entrée « Ambiance » du menu abeille, et non plus dans une section de
/admin/settings.La maquette couvre les quatre états, les trois étapes du parcours, l'aperçu de ce que #213
ajoutera, les cas dégradés et les décisions de conception.
Révision 3 (2026-09-04) — maquette VALIDÉE, gate UI levé. Les deux points d'arbitrage sont
tranchés et intégrés :
BackupPage.jsx(chore(admin): game-config.json rattaché au flag history dans backup/restore — checkbox dédiée à envisager #152) devient« Réglages de jeu ». Libellé visible seul : la clé de l'option, le paramètre
ambiance=truede la requête et le format des archives sont inchangés, donc aucune archiveexistante n'est invalidée. (La clé serveur reste
lighting.)« Mises à jour ». Révision 4 : trois glyphes distincts, pas une même forme recolorée —
ampoule pleine avec rayons (verte) = pont configuré et qui répond · ampoule en contour +
pastille d'alerte (orange) = pont configuré mais qui ne répond plus, ou qui refuse la clé ·
ampoule en contour nu (grise) = aucun pont configuré. Correspondance normative des états de
GET /api/lighting/statusencontracts/hue-bridge.md§7.1 ; tracés de référence en §01 dela maquette.
C'est la forme qui porte l'état, la couleur ne fait que renforcer : la distinction reste
lisible en niveaux de gris et pour un daltonien, là où trois teintes sur une forme unique
auraient été indiscernables à 15 pixels.
système. Les trois glyphes sont donc des SVG en ligne tracés en
currentColor.Aucune modification du code de rendu du menu :
Navbar.jsxécrit déjà<span className="menu-icon">{item.icon}</span>et React y accepte un élément comme unechaîne — seule la définition de l'entrée change.
titleen toutes lettres malgré tout, SVGaria-hidden: un lecteur d'écran ne voit aucuneforme.
Rafraîchissement au montage, toutes les 30 s, et après tout enregistrement — l'endpoint ne
fait aucune I/O. Le précédent
useUpdates(appel unique au montage,Navbar.jsx:87-89) nesuffit pas : un pont peut devenir injoignable pendant une session.
Contrainte de déploiement qui simplifie l'écran
Le pont de production sera dédié à BuzzMaster — pas le pont domestique de l'utilisateur. Il n'y
a donc pas de lumières étrangères à filtrer. L'écran propose toutes les ampoules cochées par
défaut, mais les affiche explicitement et permet de les décocher : on ne suppose jamais en
silence que tout ce qui est sur le pont nous appartient.
Portée
lightingdeconfig.json(config système), déclarée selon la procédure établie :struct + champ dans
Config, défauts dansApplyDefaults, bloc de décodage additif parsection dans
handleConfig(internal/server/http.go~1400-1440).role: "team"compris (§6 du contrat), mêmesi feat(ambiance): configuration serveur et écran d'administration Éclairage #207 n'expose que
role: "general". Le casser en feat(ambiance): éclairage différencié par équipe #213 imposerait une migration deconfig.jsonsur une section livrée quelques jours plus tôt.lighting, jamaisambiance: ce mot désigne déjà la catégorie desauvegarde couvrant
game-config.json(BackupPage.jsx, chore(admin): game-config.json rattaché au flag history dans backup/restore — checkbox dédiée à envisager #152).GET /config.json(
maskedConfigJSON,http.go:1320), motif « absente ⇒ préservée,clear_api_key⇒ effacée,api_key_configureddérivé jamais persisté », surchargeBUZZCONTROL_HUE_API_KEYsansécriture disque, jamais journalisée.
pont, pas au clavier. Un champ vide proposerait un geste impossible.
POST /api/lighting/discover,POST /api/lighting/register,GET /api/lighting/lights,POST /api/lighting/test,GET /api/lighting/status.GET /api/lighting/statusne fait aucun appel bloquant — il renvoie l'état connu du pilote./admin/ambiance, atteignable par une entrée « Ambiance » du menu abeille(révision du 2026-09-03 — décision utilisateur). Concrètement :
menuItems(web/src/components/Navbar.jsxl. 146-155) :{ path: 'ambiance', label: 'Ambiance', icon: '💡' }, placée juste aprèsConfig;adminRoutes(web/src/App.jsxl. 27-40) :{ path: 'ambiance', element: <AmbiancePage /> }— mécanisme de routage existant, rien de neuf ;web/src/pages/AmbiancePage.jsx+.css+.test.jsx, sur le squelette des autres pagesadmin (
header.page-headerpuis une<Card>).ConfigPage.jsxn'est pas modifiée — c'est tout l'objet de la révision.web/src/hooks/useLightingStatus.js— hook sur le modèle deuseUpdates.js, alimentantl'ampoule du menu.
web/src/pages/BackupPage.jsx— renommage du seul libellé « Configuration Ambiance » →« Réglages de jeu » (3 occurrences : l. 79, 149, 227). Ne toucher ni la clé
ambiancedel'objet d'options, ni le paramètre
ambiance=truede la requête.Le POST de configuration ré-émet toujours la section
lightingcomplète (précédent normatifde la section IA : un payload partiel remet les autres champs à leurs défauts).
Association refusée. Forme reprise du badge tri-état des clés IA (
ai-key-badge).attente temporisée, pas une décision binaire. Le projet ne réserve la modale qu'à l'aide
statique (
ApiKeyHelpModal) et aux décisions bloquantes (ApiKeyValidationDialog) ; il n'existeaucun précédent de modale d'attente dans cette page.
L'erreur « bouton non pressé » (Hue
101) est un cas nominal : l'écran attend et propose deréessayer, il ne s'alarme pas.
.wifi-toast-warningn'est défini nulle part dansserver-go/web/src/— un toast d'avertissement s'affiche aujourd'hui en texte blanc sur fondtransparent. Cet écran en a besoin.
Hors portée
Câblage sur les événements de jeu (#205, livré). Affectation par équipe (#213). Conduite manuelle
et restitution d'état (#208).
Critère de fait
dev-frontend.lighting: démarrage, sauvegarde et rechargement deconfig.jsoninchangés, aucune découverte réseau, aucune ligne de log. La page
/admin/ambiances'affichequand même, réduite à son bouton « Rechercher un pont ».
ConfigPage.jsx, qui n'est pas touchée par cette issue.hue-bridge.md§7.1, et letitledit l'état en toutes lettres. Vérifié en niveaux de gris : les trois états restent
distinguables sans la couleur. Vérifié aussi sans pont configuré : contour nu, sans pastille,
et aucune apparence d'alerte.
BackupPage.jsx: libellé renommé, et une archive produite avant le renommage serestaure toujours (le format n'a pas changé).
config.json: enregistrer une autre section (IA, serveur) ne détruit ni nevide
lighting, et réciproquement.GET /config.json, dans les logs, ni dans une archive de sauvegarde.(Vérifié au cadrage :
config.jsonvit à la racine du serveur, hors dudataDirarchivé parhandleFSBackup— ne pas déplacer la section versgame-config.json.)BUZZCONTROL_HUE_API_KEY: rien n'est écrit sur disque (précédent explicite,docs/TEST_PROCEDURE.md§9 scénario 7).ne reste jamais à charger indéfiniment.
Dépendances
contracts/hue-bridge.md(écrit). Parallélisable avec #206 : le front travaille depuis le contrat,et
test-writerdepuis la maquette.