From c1af47476d668937822a08078ab3af1f23a1f1d1 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Wed, 26 Aug 2026 18:06:54 +0200 Subject: [PATCH] =?UTF-8?q?docs(recap):=20la=20journ=C3=A9e=20du=202026-08?= =?UTF-8?q?-26=20=E2=80=94=20trois=20recalages=20de=20contrat,=20et=20la?= =?UTF-8?q?=20r=C3=A9serve=20batterie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le fil conducteur des six commits du jour tient en une phrase : l'app ne recalcule pas ce que la box publie, et n'anticipe pas ce qu'elle n'a pas encore publié. Le point à retenir est le seuil de réserve batterie : 0,9 en usine, batterie du banc à 50 %. Quand la règle montera dans le budget, `.75` ne pilotera plus rien par grand soleil, sans erreur ni panne. Le RECAP le dit, et le TODO garde les trois crochets laissés ouverts — dont le `Set` volontairement vide qui attend le code du moteur. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff --- RECAP.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- TODO.md | 15 ++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/RECAP.md b/RECAP.md index 07997c1..19d726e 100644 --- a/RECAP.md +++ b/RECAP.md @@ -1,8 +1,112 @@ # RECAP — etm-powersync-app · reprise de session -> **Session 2026-08-25.** Branche **`feature/beta-add-config`**, **poussée**. -> La section « État au 2026-06-30 » plus bas reste valable pour tout ce qu'elle décrit. -> La **soirée du 25** (lot C) est en tête ci-dessous ; le reste de la journée suit. +> **Session 2026-08-26.** Branche **`feature/beta-add-config`**, **poussée** (`801ba15`). +> La journée du **26** est en tête ci-dessous ; la soirée du **25** (lot C) suit ; la section +> « État au 2026-06-30 » plus bas reste valable pour tout ce qu'elle décrit. + +--- + +## Journée 2026-08-26 — se recaler sur la box, trois fois de suite + +La box a bougé trois fois dans la journée (`+etm16`, `+etm19`, puis `3g`). Le fil conducteur +de tout ce qui suit tient en une phrase : **l'app ne recalcule pas ce que la box publie, et +n'anticipe pas ce qu'elle n'a pas encore publié.** + +| Commit | Contenu | +|---|---| +| `aada8e2` | Les 4 points ouverts du RECAP : glissement, sélecteur de mécanisme, chip de domaine, thème | +| `7089148` | Le harnais vérifie lui-même le chip et l'écran de mécanisme | +| `cd76df3` | **`+etm16`** — table de paliers, `stagesW`, compteur et sonde | +| `ab2a27b` | **`+etm19`** — `BELOW_MIN_POWER`, compteurs rattachés depuis l'écran, glissement exercé | +| `449c6ac` | **`3g`** — les valeurs de borne sont du RUNTIME | +| `801ba15` | **`3g`** — la réserve batterie sort de l'usine | + +### Ce qui a été corrigé, et pourquoi ça avait échoué + +- **Le glissement ne marchait pas** — et c'était ma régression : j'avais imbriqué un + `ReorderableListView` dans un autre, les deux se disputaient le geste. Le classement est + désormais **inversé à dessein** : on glisse les *domaines*, et les flèches ordonnent *dans* + un domaine. Restait une **zone morte de 3 px** dans la poignée : `ReorderableDragStartListener` + délègue le test de toucher à ses enfants, un `Container` transparent la referme. +- **Le test du glissement déclarait forfait** deux fois sur trois : le banc revient + régulièrement à une charge par domaine, et sans domaine partagé il n'y a rien à glisser. Le + test **pose maintenant sa propre condition** — il regroupe, exerce, puis rend le classement + d'origine. Un test qui dépend de l'état du terrain ne teste rien. +- **« la config du rootmeter est perdue »** — elle ne l'était pas : la box l'avait toujours, + l'app ne la **lisait** jamais (`syncRootMeter()`). Le symptôme et la cause étaient à deux + écrans de distance. +- **`/energy/setup` est gardé par le mode installateur** : le routeur renvoyait ailleurs, et le + symptôme affiché était « aucune charge lue ». Le harnais déverrouille au PIN `1234` et + **reverrouille après** le maintien d'écran — l'ordre inverse passait au vert en montrant le + tableau de bord. + +### Les trois recalages de contrat + +**`+etm16` — ce que la box compare vraiment.** `sameHardware()` est public, et la comparaison +porte sur la **table de paliers**, pas sur la liste de relais : réordonner `relays[]` ne +reconstruit plus rien (vérifié au banc — « 2 inchangée(s) », aucun état sûr). Mon premier +miroir comparait des listes et criait au loup ; corrigé après lecture de +`RelayStageTable::operator==` — chaque combinaison se compare **comme un ensemble**, mais les +paliers s'apparient par position. Les paliers dérivés arrivent maintenant en `stagesW` : un +miroir de moins à faire diverger. Les trois champs optionnels (`meterThingId`, `sensorThingId`, +plancher) sont traités **comme un seul objet**, grisage dérivé du schéma, jamais anticipés dans +le modèle. + +**`+etm19` — `BELOW_MIN_POWER`.** Deux variantes, à distinguer de `SURPLUS_INSUFFICIENT` et +`SG_NORMAL` qui ne se déclenchent plus que sur budget ≤ 0. Le repli aurait tenu : un code +inconnu n'a jamais affiché de vide. + +**`3g` — deux valeurs de runtime, et un réglage qui change de portée.** « Minimum » désigne +deux objets différents : le **plancher déclaré** d'une charge modulable (une propriété de +l'appareil, qui se règle) et le **minimum d'une borne**, `max(borne, voiture)`, qui dépend du +véhicule branché et du nombre de phases — de l'ordre de 1,4 kW en monophasé contre 4,1 kW en +triphasé, **pour le même courant**. L'app affiche la valeur telle que la box la donne : aucune +conversion côté app, sinon il faudrait deviner laquelle des deux règles s'applique. Et le +budget **ne se partage pas** entre bornes : aujourd'hui chacune reçoit l'enveloppe entière. + +### La réserve batterie — le point à retenir de la journée + +`batteryLevelConsideration` vaut **0,9** en usine. Le banc `.75` a sa batterie à **50 %** : le +seuil est au-dessus de ce que la batterie atteint. Aujourd'hui ça ne gèle que la recharge du +véhicule ; **quand la règle montera dans le budget, `.75` ne pilotera plus rien** — ni ECS, ni +PAC, ni bornes — par grand soleil, sans erreur ni panne, rien à lire dans un journal. + +Un paramètre qui peut produire ce symptôme n'est pas un paramètre d'usine. +`BatteryReserveCard` le lit, l'écrit, **alerte quand le seuil dépasse le SOC réellement +atteint**, et dit la portée à venir : régler ce seuil « pour la voiture » couperait le reste +demain. Elle n'invente aucun défaut — box muette, carte muette. + +Le motif d'arbitrage est **préparé, pas anticipé** : la clé ARB `decisionBatteryReserve` existe +et `kCodesReserveBatterie` est un `Set` **volontairement vide** dans `telemetry_text.dart`. Le +crochet attend le code que publiera le moteur ; deviner son nom aurait produit un affichage qui +ne se déclenche jamais. + +### Vérifié sur l'appareil, contre `.75` + +Campagne verte, zéro exception. La notification précède l'accusé (4/4 écritures). +`enabled:false` ouvre bien les contacts (ECS-413). Les deux compteurs **PAC-Meter** et +**ECS-Meter** sont rattachés **depuis l'écran**, et `measuredW` est désormais **distinct** par +charge — chauffe-eau 1 500 W, PAC 800 W, cette dernière affichant « Mesuré 800 W · sous la +consigne » face à son estimation de 3 000 W. + +> Deux pièges de méthode, notés parce qu'ils se représenteront : +> j'ai d'abord **affirmé des `measuredW` distincts** pour prouver le rattachement — deux +> compteurs au repos lisent légitimement 0,0 W tous les deux ; l'assertion porte maintenant sur +> des `meterThingId` distincts plus un détachement montrant `null` ≠ 0. Et une assertion +> cherchait les cartes **sans faire défiler** : dans un `ListView`, ce qui n'est pas à l'écran +> n'est pas construit — elle ne prouvait que la hauteur de l'écran. + +### Reste ouvert + +- `minPowerW == null` **sans véhicule branché** : pas exercé sur appareil, la borne n'entre pas + dans `loads[]` avant `3g`. +- `kCodesReserveBatterie` à câbler quand le moteur publiera le code du motif. +- **Les zones** (`ac_screen.dart` est encore de la donnée fictive, `.75` n'en déclare aucune) — + explicitement **à ne pas entreprendre**. + +--- + +## Soirée 2026-08-25 — LOT C (ci-dessous) --- diff --git a/TODO.md b/TODO.md index 524b16b..f407c94 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,20 @@ # TODO — ETM PowerSync App +## Charges pilotables — ce qui attend la box (2026-08-26) + +- [ ] **`kCodesReserveBatterie`** — le `Set` est volontairement VIDE dans + `lib/services/telemetry_text.dart`. Y mettre le code du motif « budget annulé par la réserve + batterie » **quand le moteur le publiera**, pas avant : deviner son nom produirait un + affichage qui ne se déclenche jamais. +- [ ] **`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~~ — `ac_screen.dart` est de la donnée fictive et `.75` n'en déclare aucune. + **Décidé : à ne pas entreprendre.** + ## En cours / priorité haute - [ ] **TariffProvider — connexion RPC réelle**