# 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à. - [ ] **`EVChargingCard` n'est montée par AUCUN écran** (constaté le 2026-08-27 — le tableau de bord utilise `features/dashboard/widgets/`). Soit elle rejoint un écran, soit elle part ; en l'état c'est du code mort qui a l'air vivant. - [ ] **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