Patrick Schurig e45ca67151 fix(dashboard): les trois branches inventées du Sankey s'annoncent comme estimées
Marquage, pas refonte. PAC = 38 % de la consommation maison, eau chaude = 18 %, autres
= 22 % : trois ratios posés à la maquette, jamais remplacés, et un seul (« Autres »)
portait le cercle pointillé. Les trois le portent maintenant, leurs flux passent en
pointillé, et une légende sous le schéma nomme ce qui est quoi :

  « Répartition estimée : PAC, eau chaude et autres usages sont des parts types, pas des
    mesures. Seule la voiture est mesurée à la borne. »

Le trait pointillé seul ne suffisait pas : quatre chiffres en kW se ressemblent tous et se
lisent tous comme des mesures. Nommer les branches évite de faire chercher lesquelles.

Brancher les trois sur GetLoadTelemetry.measuredW reste le vrai lot — la source existe
(chauffe-eau 1500 W, PAC 800 W au relevé du jour), mais c'est une refonte de l'écran
d'accueil et le banc est pris par la campagne V2C.

Quatre tests montés sur la vue, dont deux qui figent ce que la voiture affiche : « non lu »
sans borne — jamais 0,0 kW, qui échangerait une fiction contre une autre — et la mesure
quand il y en a une. Deux pièges de harnais notés dans le fichier : pumpAndSettle est
inutilisable (les particules tournent en boucle, disableAnimations ne change que la valeur
lue), et toucher un nœud à la première trame le rate en silence — les nœuds entrent en
ScaleTransition depuis une échelle nulle.

PREPA_BANC_20260828.md corrigé en parallèle (dépôt plugin) : trois de ses affirmations sont
tombées avec le déploiement du matin.

158 tests, flutter analyze inchangé à 27 remarques.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQbZKrWqsMFP1Lh2jjjd9f
2026-08-28 12:59:58 +02:00

149 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TODO — ETM PowerSync App
## Ouvert au 2026-08-27
- [x] ~~🔴 **Bilan JOURNALIER**~~ — **fait le 2026-08-28.** Les quatre tuiles montrent la
journée, sous un en-tête « Aujourd'hui » qui le dit une fois. Méthode : différence des
compteurs entre minuit local et maintenant, jamais l'intégration des puissances —
mesurée contre estimée, 0,8 % d'écart sur `.75`. Le contrôle qui la valide est
l'identité *production + soutiré == consommation + injecté*, vérifiée exactement sur le
banc.
- **Deux bugs d'unité trouvés en chemin** : les compteurs `total*` sont en **kWh** et le
modèle les nommait `Wh`. L'écran Énergie les divisait donc encore par mille — une
journée de 78 kWh s'y lisait « 78 Wh » — et la simulation accumulait des Wh.
- Vérifié sur appareil contre `.75` en lecture seule. Le passage a trouvé quatre libellés
tronqués qu'aucun des 138 tests unitaires ne pouvait voir.
- [ ] ❓ **« ECS d'abord, batterie ensuite » — à porter au moteur.** Demandé le 2026-08-27,
et **pas exprimable aujourd'hui**. `batteryLevelConsideration` fait l'inverse : il annule
le budget *sous* le seuil, donc la batterie passe avant. La batterie n'est pas dans le
waterfall — aucune entrée `GetLoadConfig`, et `enableCharging` / `chargingRate` /
`enableDischarging` n'apparaissent nulle part dans le plugin ETM. Le levier existe
pourtant : la classe **SunSpec Storage** de `.75` déclare ces cinq actions. Question :
*le stockage est-il une charge arbitrable, ou reste-t-il hors du budget ?* Une règle
`Rules.*` serait un contournement — deux commandeurs sur la batterie, exactement le
défaut que 3g-2 vient de supprimer sur les bornes.
- [ ] **Zones de climatisation — cadrage écrit, banc à préparer.** Voir
`docs/CADRAGE_zones_clim.md`. Le blocage n'est pas « aucune zone déclarée » : sur les 58
classes de `.75`, **aucune ne porte `thermostat`, `closablesensor`, `temperaturesensor`
ni `notifications`** — aucun plugin installé ne sait en créer une. Deux paquets du dépôt
apt du banc suffisent (`nymea-plugin-generic-heatingcooling`,
`nymea-plugin-generic-sensors`). Six décisions à prendre avant de coder.
- [ ] **`EV_GRID_START` / `PHASE_LIMIT` — éprouvés par test seulement.** Les clés ARB sont
prêtes, les motifs n'ont **jamais été vus sur machine**. Signaler la première occurrence
réelle. Ne rien bâtir autour d'ici là.
- [ ] ⏳ **`sessionEnergy` existe maintenant — mais rien ne prouve encore qu'il survive à
une interruption.** Relevé du 2026-08-28 après-midi sur `.75` (`Integrations.GetThings`) :
la V2C publie `sessionEnergy` **et** `chargeEnergy`, tous deux à **4,229 kWh** — égaux,
ce qui est exactement ce qu'on attend d'une session sans interruption. Le test qui
tranche est une **remise à zéro observée de `chargeEnergy` avec un `sessionEnergy` qui
continue**. Tant qu'elle n'est pas vue, LM-1009 §C1 reste entière : **pas d'avancement
affiché**. À vérifier sur la campagne de mesures en cours.
- Deux attentes **levées** au même relevé : le renommage `currentL1/L2/L3` (unité
Ampère) **est déployé** — plus de `powerL1/L2/L3` — et `phaseCount` vaut **3** sur la
Trydan, le réglage ayant été posé. `PREPA_BANC_20260828.md` (dépôt plugin) est corrigé
en conséquence : il décrivait l'état de la veille au soir.
- [x] ~~**`EVChargingCard` n'est montée par AUCUN écran**~~ — **tranché le 2026-08-28 :
elle part.** La monter aurait publié de la fiction. Trois des grandeurs qu'elle affiche
ne sont **jamais lues sur la box** : `chargingMode`, `chargingPower` (3,6 kW) et
`solarSourcePercent` (82 %) sont des valeurs par défaut de `EnergyData`, et le mode n'est
écrit qu'en optimiste après une commande — jamais relu par `GetChargingInfos`. Sa feuille
« Paramètres de charge » proposait en plus un **courant minimum**, qui n'existe dans aucun
contrat, et son bouton « Enregistrer » ne faisait que `Navigator.pop` : rien n'était écrit.
- [x] ~~**La tuile EV des favoris affiche des valeurs par défaut comme des mesures**~~ —
**fait le 2026-08-28 : rendue muette, pas retirée.** La retirer aurait laissé un trou
dans une liste que le client a composée lui-même. `EvFavoriteTile`
(`widgets/ev_favorite_tile.dart`) lit la borne — `pluggedIn`, `charging`, `currentPower`
du Thing, mode par `GetChargingInfos` + `ChargingInfoChanged` — et **dit** ce qui n'est
pas publié plutôt que d'afficher zéro. L'écriture du mode **restitue le refus** et les
cibles tactiles passent de 7–10 px à 44 px. Les trois champs fictifs de `EnergyData`
(`chargingMode`, `chargingPower`, `solarSourcePercent`) sont **supprimés du modèle** :
la fiction n'a plus de porte d'entrée.
- [ ] **La vue « consommation » du Sankey reste une ESTIMATION — elle le dit désormais.**
`features/dashboard/widgets/energy_flow_card.dart` : *PAC = 38 % de la maison*,
*Eau chaude = 18 %*, *Autres = 22 %* sont trois ratios en dur posés à la maquette. Depuis
le 2026-08-28 les **trois** portent le cercle pointillé (`estimated`) — un seul l'avait —
et une légende sous le schéma nomme les branches concernées : « seule la voiture est
mesurée à la borne ». La voiture, elle, lit la mesure réelle (`currentPower`, « non lu »
sans borne) au lieu des 3,6 kW en dur qu'elle affichait en permanence.
**Reste le vrai lot** : brancher les trois branches sur `GetLoadTelemetry.measuredW`
(relevé du jour : chauffe-eau 1500 W, PAC 800 W). C'est une refonte de l'écran d'accueil,
pas un correctif, et le banc est pris par la campagne V2C.
Ce qu'une carte de recharge dédiée pourrait dire, si elle revient un jour :
- le **mode courant**, lu par `NymeaEnergy.GetChargingInfos` et tenu à jour par
`ChargingInfoChanged` — l'écriture existe déjà et est vérifiée
(`nymea_service.setChargingInfo`, lecture → patch → réécriture complète) ;
- la **puissance mesurée** de la borne, prise sur le Thing (`currentPower`) ;
- l'**échéance** (`targetPercentage` + `endDateTime`) — mais uniquement comme **intention
saisie**, jamais comme un avancement : LM-1009 §C1. Pas de SOC véhicule (il n'existe
pas), pas de `chargeEnergy` (remis à zéro à chaque interruption), pas de courant
minimum (aucun champ), pas de `phaseCount` (la V2C ne le publie pas).
- [ ] **Le passage sur appareil doit devenir une habitude, pas un rattrapage.** Il a glissé
deux fois, et au premier passage il a trouvé deux cibles tactiles hors norme (26 px, 18 px)
qu'aucun des 129 tests unitaires ne pouvait voir. `integration_test/ui_3g2_on_device_test.dart`
est le harnais. Rappel MIUI : la confirmation d'installation est redemandée à **chaque**
fois — faire un `adb install -r -t` préalable, puis lancer le test.
## Charges pilotables — ce qui attend la box (2026-08-26)
- [x] ~~**`kCodesReserveBatterie`**~~ — **fait le 2026-08-27.** Le motif est publié :
`BATTERY_RESERVE`, params `socPercent` / `reservePercent` / `withheldW`. Les clés que le
fichier cherchait (`batteryLevel`, `threshold`) n'avaient **jamais** existé côté plugin —
le motif sortait donc en repli anglais sur le seul cas qu'aucune mesure ne révèle.
- [ ] **`minPowerW == null` sans véhicule branché** — couvert en test unitaire
(`test/ev_runtime_test.dart`), **pas exercé sur appareil** : la borne n'entre pas dans
`loads[]` avant `3g`. À reprendre quand l'arbitrage prendra la recharge en charge.
- [ ] **Réserve batterie — portée élargie.** Le jour où le seuil gouverne tout le budget,
reprendre le texte de `BatteryReserveCard` : il annonce aujourd'hui un futur, il devra
décrire un présent.
- [ ] ~~Zones — à ne pas entreprendre~~ — **décision revue le 2026-08-27** : le lot est
cadré (`docs/CADRAGE_zones_clim.md`), voir l'entrée en tête de ce fichier. `ac_screen.dart`
reste de la donnée fictive — quatre pièces, huit températures crédibles affichées comme
des mesures.
## En cours / priorité haute
- [ ] **TariffProvider — connexion RPC réelle**
- `load()` appelle encore un `Future.delayed` fictif
- Brancher sur nymea JSON-RPC pour récupérer les prix live (ENTSO-E, Tibber, etc.)
- [ ] **EnergySetupProvider — persistance**
- Les assignations de rôles (EV, PAC, batterie…) ne survivent pas au redémarrage
- Sérialiser en SharedPreferences ou JSON local
- [ ] **SchedulerProvider — exécution réelle**
- `forceRecalc()` ne fait rien pour l'instant
- Brancher sur l'API nymea Energy Scheduler (si disponible)
## Screens à implémenter
- [ ] **DeveloperScreen** — logs, introspection JSON-RPC, test ping nymea
- [ ] **AboutScreen** — version app, version nymea, licences
- [ ] **SystemSettingsScreen** — adresse serveur nymea, port, transport (TCP/WS)
## Amélioration UX
- [ ] **Timeline** — remplacer la simulation bell-curve par les vraies créneaux du scheduler nymea
- [ ] **RoleConfigFlow Step 3** — test de connexion sur RPC réel au lieu du délai simulé
- [ ] **InstallerModeProvider** — persister le hash PIN dans SharedPreferences (actuellement en mémoire)
- [ ] **AppSettingsProvider** — persister les préférences (thème, densité, écrans actifs) dans SharedPreferences
## Intégration nymea live
- [ ] Mapping `EmsRole → nymea thingClass` pour les installations réelles
- [ ] Vérifier les interfaces nymea sur serveur réel (comparer avec `docs/nymea_integrations.md`)
- [ ] `Energy.GetSchedule` / `Energy.SetSchedule` si l'API le supporte
## Technique / dette
- [ ] Ajouter des tests unitaires pour `TariffProvider` et `EnergySetupProvider`
- [ ] Gérer le cas où `NymeaService.thingClasses` est vide en mode live (classes non chargées)
- [ ] Revoir `_Step1ThingList` : passer en `context.watch` pour réactivité si les things changent