Patrick Schurig b60c74d2e4 docs(recap): la journée du 2026-08-27 — et le mot qui manquait : JOURNALIER
RECAP.md porte les neuf commits du jour et les trois défauts que seul l'appareil a montrés :
deux cibles tactiles hors norme (26 px, 18 px), et une correction naïve laide que le test
validait — c'est la capture qui a tranché, pas l'assertion.

Ce qui manquait dans le suivi, et qui est écrit noir sur blanc :

AUCUN BILAN JOURNALIER N'EXISTE DANS L'APP. Les quatre tuiles du tableau de bord 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. Les
deux dernières portaient « aujourd'hui » : totalReturn n'est pas remis à zéro chaque nuit,
d'où le « · cumulé ». Le bilan du jour reste à écrire, et le TODO porte les trois pièges de
sa mise en œuvre — bornes à minuit local, clés de log SANS le préfixe currentPower, et les
cumulés corrompus de .75.

Et la question ouverte au moteur, telle qu'elle s'est posée : « ECS d'abord, batterie
ensuite » n'est pas exprimable aujourd'hui, et batteryLevelConsideration fait l'inverse — il
annule le budget SOUS le seuil, donc la batterie passe avant. La cause est structurelle : la
batterie n'est dans aucun GetLoadConfig et le plugin ne la commande nulle part. Le levier
existe pourtant, la classe SunSpec Storage de .75 déclare enableCharging, chargingRate et
enableDischarging.

Deux entrées du TODO étaient devenues fausses : kCodesReserveBatterie est fait, et « Zones —
à ne pas entreprendre » est revu puisque le lot est cadré.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
2026-08-27 19:49:22 +02:00

106 lines
6.3 KiB
Markdown

# 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