# TODO — ETM PowerSync App ## Ouvert au 2026-08-27 - [ ] 🔴 **Bilan JOURNALIER — il n'en existe aucun dans l'app.** Les quatre tuiles du tableau de bord (`features/dashboard/widgets/kpi_row.dart`) mêlent deux natures de grandeur, et aucune n'est journalière : *Autoconsommation* et *Autonomie* sont des **pourcentages instantanés**, *Vers réseau* et *Depuis réseau* des **kWh cumulés depuis l'origine** — `totalReturn` / `totalAcquisition` ne sont pas remis à zéro chaque nuit. Le libellé disait « aujourd'hui » ; il porte « · cumulé » depuis, mais **le bilan du jour reste à écrire**. - il passe par `Energy.GetPowerBalanceLogs` avec des bornes à **minuit local**, jamais par les totaux de `GetPowerBalance` ; - piège vérifié : les entrées de log portent `production` / `consumption` / `acquisition` / `storage`, **sans** le préfixe `currentPower` de `GetPowerBalance`. Réutiliser les mauvaises clés ne produit **aucune erreur** — tout tombe sur `0` ; - `.75` publie des cumulés corrompus (9,1e+33 kWh exportés) : `EnergyData.isPlausibleKwh` existe pour ça, ne pas le contourner. - [ ] ❓ **« 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