# 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` **existe** sur la Trydan (valeur 3), contrairement à ce que `docs/PREPA_BANC_20260828.md` avait conclu. - [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 est INVENTÉE — tableau de bord, écran d'accueil.** `features/dashboard/widgets/energy_flow_card.dart` : au drill-down consommation, *PAC = 38 % de la maison*, *Eau chaude = 18 %*, *Autres = 22 %* — trois ratios en dur, dont un seul (`Autres`) porte le marqueur `estimated`. La voiture, elle, a été branchée sur la mesure réelle de la borne le 2026-08-28 (`currentPower`, « non lu » sans borne). Le reste attend une décision : la source existe — `GetLoadTelemetry` publie `measuredW` **par charge** (relevé du jour : chauffe-eau 1500 W, PAC 800 W) — mais brancher le Sankey dessus est une refonte de l'écran d'accueil, pas un correctif. 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