docs: le contrat de recharge et les deux pièges de télémétrie, plus le retour au moteur
CLAUDE.md portait un exemple de SetChargingInfo qui invitait à l'écriture partielle. Il porte maintenant la règle inverse, avec la preuve machine, et les deux pièges de lecture de GetLoadTelemetry : ne pas sommer allocatedW sans filtrer sur funding, et ne pas lire l'allocation d'une borne comme son état. Retour au moteur dans BRIEF_plugin_depuis_maquettes.md. Un point actionnable chez eux : la description trompeuse vit dans nymeaenergyjsonhandler.cpp:114 et part dans Introspect chez tout client. Elle ne se contente pas de ne pas prévenir, elle autorise explicitement l'erreur — le prochain client la croira. Une ligne à corriger, ou une écriture à rendre réellement partielle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
This commit is contained in:
parent
a92e9b2f62
commit
4b42b732dc
40
CLAUDE.md
40
CLAUDE.md
@ -52,6 +52,19 @@ nymeaService.call('NymeaEnergy.SetChargingInfo', {
|
||||
});
|
||||
```
|
||||
|
||||
> 🔴 **`SetChargingInfo` n'est PAS une écriture partielle**, malgré ce que dit son propre
|
||||
> `Introspect` (« Only given properties will be set, others will be untouched »).
|
||||
> `ChargingInfo` est reconstruit intégralement à chaque appel : un champ **absent du JSON
|
||||
> retombe sur le défaut C++**, et celui de `targetPercentage` vaut **80**. Muet — réponse
|
||||
> `EnergyErrorNoError`, et la `ChargingInfoChanged` qui suit porte 80 comme si
|
||||
> l'utilisateur l'avait choisi. Reproduit sur `.75` le 2026-08-27 (`+etm22`) : le champ
|
||||
> valait `0`, un appel portant le seul `chargingMode` l'a laissé à `80`.
|
||||
>
|
||||
> **Donc : lire (`GetChargingInfos`), patcher, réécrire l'objet complet** — sans
|
||||
> `chargingState`, qui est `r:` et ferait rejeter l'appel. Et **ne jamais afficher
|
||||
> `targetPercentage` comme une cible choisie par l'utilisateur** tant que l'écran ne l'a
|
||||
> pas lui-même écrite : 80 peut être l'empreinte d'une écriture qui ne parlait pas de cible.
|
||||
|
||||
`chargingState` est en **lecture seule** (`r:`) dans la charge utile :
|
||||
`ChargingStateIdle` · `ChargingStateSurplusCharging` · `ChargingStateSpotMarketCharging` ·
|
||||
`ChargingStateTimeRequirement`.
|
||||
@ -108,6 +121,7 @@ Min+PV + échéance → ChargingModeEcoMinWithTargetTime + targetPercentage +
|
||||
| `GetLoadConfig` | Lire les charges déclarées | Community |
|
||||
| `SetLoadConfig(loadConfigs[])` | Écrire — **remplace TOUT l'ensemble**, refus en bloc | Community |
|
||||
| `GetLoadTelemetry` | État runtime de l'arbitrage (budget, allocation, verrous, motifs) | Community |
|
||||
| — | Depuis `+etm22`, `loads[]` contient **aussi les bornes** (`mechanism.kind = "evcharger"`) et chaque entrée porte `o:funding` (`"surplus"` / `"grid"`) | — |
|
||||
| `ClearLoadFault(loadId)` | Lever le verrou de défaut — **l'ack ne prouve rien** | Community |
|
||||
|
||||
**Recharge EV**
|
||||
@ -233,6 +247,32 @@ batteryGreen boostRed pvGreen minPvBlue accentTeal
|
||||
|
||||
---
|
||||
|
||||
## Télémétrie d'arbitrage — deux pièges de lecture
|
||||
|
||||
**1. `funding` — ne jamais sommer `allocatedW` sans filtrer.** Depuis `+etm22` une borne
|
||||
peut figurer dans `loads[]` au titre du surplus **ou** du réseau (échéance, tarif
|
||||
dynamique). L'identité vérifiable est : *somme des `allocatedW` financés au surplus ==
|
||||
`budget.allocatedW`* ; une borne financée au réseau vit dans `budget.evReservedW`. Sommer
|
||||
sans filtrer donne un écart que rien ne permet d'interpréter. Omis en mode dégradé — sans
|
||||
plan, pas de financement. Cf. `LoadTelemetry.surplusAllocatedW`.
|
||||
|
||||
**2. ⚠️ Pour une BORNE, `allocatedW` et son motif sont une INTENTION, pas un ordre.**
|
||||
`adjustEvChargers()` re-décide le surplus après le waterfall et passe outre le refus de
|
||||
l'arbitre : relevé sur `.75`, l'arbitre publie « 0 W / `BELOW_MIN_POWER` » et le proxy
|
||||
commande `ON` à 6 A dans le même cycle. L'état réel se lit dans
|
||||
`mechanism.chargingEnabled` / `mechanism.currentA`, qui viennent du Thing. **Ne pas écrire
|
||||
« la borne ne charge pas parce que le budget est sous le plancher » à partir du seul
|
||||
motif.** La réserve **ne vaut pas** pour l'ECS et la PAC, où l'allocation publiée **est**
|
||||
la commande. Elle tombe avec le lot plugin **3g-2** ; côté app, elle est portée par
|
||||
`LoadTelemetryEntry.allocationIsCommand`.
|
||||
|
||||
**Motif `BATTERY_RESERVE`** — params `socPercent`, `reservePercent`, `withheldW`, tous
|
||||
entiers **déjà en pourcentage** (ne pas confondre `reservePercent` = 40 avec
|
||||
`batteryLevelConsideration` = 0,4). Le défaut d'usine de ce réglage est passé de 0,9 à
|
||||
**0,2**, mais une box déjà déployée garde la valeur qu'elle a persistée.
|
||||
|
||||
---
|
||||
|
||||
## Règles de modification
|
||||
- Tout nouvel écran → valider maquette avec Patrick avant de coder
|
||||
- Tout nouvel appel RPC → vérifier dans `INTERFACE.md` que la méthode existe
|
||||
|
||||
@ -1,5 +1,61 @@
|
||||
# Brief plugin — ce que les maquettes « Configurer » demandent au moteur
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-27 — retour sur le brief 3g-1 (`docs/BRIEF_depuis_plugin.md`)
|
||||
|
||||
Les quatre points sont traités côté app. Deux constats en retour, dont un actionnable chez
|
||||
vous.
|
||||
|
||||
### A. `SetChargingInfo` — la box PROMET l'écriture partielle, par écrit
|
||||
|
||||
`nymeaenergyjsonhandler.cpp:114` publie cette description, qui part dans
|
||||
`JSONRPC.Introspect` et donc chez tout client :
|
||||
|
||||
> *« Set the charging info for an EV charger. Only given properties will be set, others
|
||||
> will be untouched. »*
|
||||
|
||||
C'est l'inverse du comportement réel — un champ omis retombe sur son défaut C++. La phrase
|
||||
ne se contente pas de ne pas prévenir : **elle autorise explicitement l'erreur**. Un client
|
||||
qui la lit écrit un champ à la fois, en toute bonne foi, et repose 80 % de cible à chaque
|
||||
changement de mode.
|
||||
|
||||
Reproduit ici le 2026-08-27 sur `.75` (`+etm22`), borne `88160e45` :
|
||||
`targetPercentage` valait `0` ; un `SetChargingInfo` portant le seul `chargingMode` l'a
|
||||
laissé à **`80`**, réponse `EnergyErrorNoError`. Banc restauré.
|
||||
|
||||
**Demande :** corriger la description (une ligne), ou rendre l'écriture réellement
|
||||
partielle. La première suffit — l'app fait déjà lire-patcher-réécrire. Tant que la phrase
|
||||
est là, le prochain client la croira.
|
||||
|
||||
### B. `BATTERY_RESERVE` — la garde `withheldW > 0` fait bien son travail
|
||||
|
||||
En cherchant à observer le motif sur `.75`, la bascule du seuil à 0,9 a produit exactement
|
||||
le cas que votre garde couvre : `surplusW` à **−3 551 W**, donc rien à confisquer, donc
|
||||
**aucun `BATTERY_RESERVE` publié** — les charges sont sorties en `SURPLUS_INSUFFICIENT` et
|
||||
`BELOW_MIN_POWER`, ce qui est la vérité. Le motif n'a donc pas pu être vu en vrai d'ici :
|
||||
il faut du surplus RÉEL sous le seuil, et le banc importait au moment de l'essai. Seuil
|
||||
remis à 0,4.
|
||||
|
||||
Le rendu français côté app est aligné sur `socPercent` / `reservePercent` / `withheldW` et
|
||||
mène par les watts retenus, comme votre `decisionreason.cpp`.
|
||||
|
||||
### C. Ce que l'app dépend désormais de 3g-2
|
||||
|
||||
L'écran affiche l'état réel d'une borne (`mechanism.chargingEnabled` / `currentA`) **avant**
|
||||
l'allocation, et étiquette celle-ci « décision de l'arbitre ». C'est un contournement, pas
|
||||
une conception : il existe uniquement parce que les deux peuvent se contredire. Le jour où
|
||||
`adjustEvChargers()` ne commande plus le surplus, `allocationIsCommand` rend `true` partout
|
||||
et ces branches disparaissent. Prévenez-nous au déploiement.
|
||||
|
||||
### D. Méthode
|
||||
|
||||
`origin` est basculé en SSH (`gitea-lan:pakutz79/etm-powersync-app.git`). La forge était
|
||||
injoignable au moment du lot — `192.168.1.113` sans route, front public en 503 — donc les
|
||||
commits d'hier et celui-ci attendent en local. Ils partent dès qu'elle répond.
|
||||
|
||||
---
|
||||
|
||||
**Origine :** session du 2026-08-25, côté app. Écrit après le passage sur appareil et la
|
||||
conception des six écrans « Configurer » (`docs/mockups/`).
|
||||
**Cible :** `etm-powersync-energy-plugin-etm`, plugin déployé au moment du relevé :
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user