_SocProgress affichait « 62 % » et « Aujourd'hui à 07:30 » en dur, sur un écran client. Laissé de côté au lot précédent comme préexistant ; ça ne tient plus, une promesse fausse ne se périme pas. Le correctif attendu — brancher la vraie valeur — n'existe pas. Vérifié sur .75 : sur les 58 classes de la box, aucune classe portant l'interface evcharger ne déclare d'état de charge, aucune ne porte l'interface car, et les deux bornes du banc publient pluggedIn, charging, maxChargingCurrent, phaseCount, sessionEnergy — jamais un pourcentage. Le seul batteryLevel de l'installation est celui de la batterie de la MAISON (Fronius Storage, 50 %, rendu visible par le correctif energystorage du plugin SunSpec). C'est une autre grandeur : l'afficher en face d'une cible de recharge remplacerait un chiffre faux par un chiffre faux et crédible. Retiré, avec la barre de progression — une jauge sans grandeur mesurée est un dessin — et la carte dit maintenant pourquoi l'avancement n'est pas affichable. Reste ce que l'app SAIT : la cible qu'elle vient elle-même d'écrire. Le brief 3g-2 est rapatrié depuis le dépôt plugin (la forge étant à terre, il n'avait pas pu voyager) et CLAUDE.md est recalé dessus. À noter, la description trompeuse de SetChargingInfo signalée hier est corrigée sur la box : elle énumère désormais tous les défauts et conclut « send it back complete ». Le prompt 3g-2 déposé à la racine est commité tel quel, il était déjà indexé. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
141 lines
7.9 KiB
Markdown
141 lines
7.9 KiB
Markdown
# Brief agent app — ce que 3g-2 change pour l'écran
|
||
|
||
**Émis par** : l'agent plugin, après 3g-2 · **Date** : 2026-08-27
|
||
**Banc** : `.75`, plugin `1.15.2+etm24`, nymea `1.15.2+202606191336~trixie1`
|
||
**Contexte** : `docs/RELEVE_3g2.md` (ce dépôt) porte les traces machine, cycle par cycle.
|
||
**Contrat** : `INTERFACE.md` fait autorité — ce brief ne fait qu'en signaler les mouvements.
|
||
|
||
> Quatre points. Le deuxième **retire une mise en garde** que l'app portait, et il est le plus
|
||
> intéressant pour vous : du code conditionnel peut disparaître.
|
||
|
||
---
|
||
|
||
## 1. `loads[] ⊆ GetLoadConfig` est RÉTABLI
|
||
|
||
3g-1 avait rompu l'invariant sans le signaler : les bornes entraient dans `loads[]` de
|
||
`GetLoadTelemetry` **sans figurer dans aucune `GetLoadConfig`**. C'était exactement la charge
|
||
fantôme que le lot B-bis avait supprimée, réapparue par l'autre bout — et vous vous appuyez sur
|
||
cet invariant pour résoudre libellé, domaine et rang de chaque charge publiée.
|
||
|
||
**Depuis `+etm23`, toute borne détectée reçoit d'office une entrée `LoadConfig`.** Sur le banc,
|
||
`GetLoadConfig` est passé de **2 à 4 entrées** au premier cycle après la mise à jour :
|
||
|
||
```
|
||
chauffe-eau adapter=relay-router prio=1 domain=ecs
|
||
PAC banc adapter=sg-ready prio=2 domain=heating
|
||
Simulated wallbox adapter=evcharger prio=1 domain=ev ← nouvelle
|
||
Terra AC Charger (TCP) adapter=evcharger prio=1 domain=ev ← nouvelle
|
||
```
|
||
|
||
Forme d'une entrée `evcharger` : `id` (le ThingId de la borne), `adapter: "evcharger"`,
|
||
`mode: "dynamic"`, `domain: "ev"`, plus `label` / `priority` / `enabled`. **Aucune charge
|
||
utile** — pas de `relays`, pas de `sgReady`, pas de `powerLevels`/`maxPowerW`/`minPowerW`, pas
|
||
de `minOnS`/`minOffS`. Les limites d'une borne viennent du Thing et changent avec le véhicule
|
||
branché ; les déclarer en configuration en ferait une seconde source de vérité.
|
||
|
||
**Rien à faire de votre côté pour que ça marche** : l'aller-retour `GetLoadConfig` →
|
||
`SetLoadConfig` verbatim est **neutre**, vérifié sur machine — l'objet relu est identique et
|
||
aucun adaptateur n'est reconstruit.
|
||
|
||
**Le sens de l'inclusion reste le même** : `GetLoadConfig` peut contenir des charges absentes de
|
||
`loads[]`. Pour une borne, ça veut dire *hors arbitrage* — pas de voiture assignée, mode manuel,
|
||
ou **aucun véhicule branché** (ce dernier cas depuis `+etm24`). Une borne configurée absente de
|
||
`loads[]` se lit « pas arbitrée en ce moment », jamais « perdue ».
|
||
|
||
---
|
||
|
||
## 2. ⚠️ La mise en garde sur `allocatedW` des bornes TOMBE
|
||
|
||
Vous portiez : *« pour une borne, `allocatedW` est la décision de l'arbitre et non l'état de la
|
||
borne »*. C'était juste, et ça ne l'est plus.
|
||
|
||
**Le défaut derrière cette mise en garde** : jusqu'à `+etm22`, l'arbitre décidait et publiait,
|
||
puis `adjustEvChargers()` — le chemin d'exécution hérité de l'amont — re-décidait derrière lui
|
||
et commandait autre chose. La décision publiée n'atteignait pas le matériel.
|
||
|
||
**`+etm23` ferme ça.** Une charge, un commandeur : les actions EV du waterfall sont dispatchées
|
||
vers l'adaptateur, et les bornes ainsi commandées sortent du chemin proxy. Mesuré sur machine :
|
||
9 commandes émises à la borne sur la fenêtre d'observation, **9 par l'arbitre, 0 par le proxy**,
|
||
et 6 cycles sur 6 portent la ligne « commandée par le waterfall, pas de re-décision ici ».
|
||
|
||
**Conséquence pour vous** : `allocationIsCommand` doit rendre `true` **partout**, et les
|
||
branches conditionnelles qui en dépendaient peuvent disparaître.
|
||
|
||
**Une réserve, et elle est petite mais réelle** : l'allocation reste ce que l'arbitre a
|
||
**commandé**, pas ce que la borne **tire**. Un plafond matériel, un véhicule qui refuse ou un
|
||
câble débranché feront toujours diverger les deux. Ce que vous pouvez enfin affirmer, c'est que
|
||
*personne d'autre ne commande*. Pour l'écart commande/réalité, la charge utile `mechanism` de la
|
||
borne porte `chargingEnabled`, `currentA`, `phaseCount` et `pluggedIn` — c'est là qu'il se lit,
|
||
pas dans `allocatedW`.
|
||
|
||
---
|
||
|
||
## 3. Nouvelle clé ARB : `EV_GRID_START`
|
||
|
||
```
|
||
EV_GRID_START { budgetW, floorW, gridW } funding: "grid"
|
||
```
|
||
|
||
**Une borne qui DÉMARRE en soutirant au réseau.** Le surplus ne paie pas son plancher, mais le
|
||
réglage `acquisitionTolerance` (défaut 0,5) autorise l'appoint : à 0,5, une borne qui exige
|
||
4 140 W démarre dès 2 070 W de surplus et tire le reste du réseau.
|
||
|
||
`gridW = floorW − budgetW` est **ce qui est acheté**. C'est le chiffre qui fait de cette ligne
|
||
une décision, et pas un effet de bord : il mérite d'être montré, pas rangé dans un repli.
|
||
|
||
**Comptabilité — c'est le seul cas où l'`allocatedW` d'une charge se partage** entre les deux
|
||
compteurs du budget : `budgetW` vient du surplus et entre dans `budget.allocatedW`, `gridW`
|
||
vient du réseau et entre dans `budget.evReservedW`. `budgetW + gridW == allocatedW`, toujours.
|
||
Si vous réconciliez la somme des allocations avec le budget, c'est la ligne à traiter à part.
|
||
|
||
**Borné par construction** : le démarrage exige du surplus réel et consomme tout le budget
|
||
restant, donc **au plus une borne par cycle** peut soutirer.
|
||
|
||
> **Pas encore vu sur machine.** Guetté six cycles au banc, jamais fabriqué : le budget de la
|
||
> borne est passé de 385 W à 2 350 W sans s'arrêter dans la fenêtre de tolérance. Le motif est
|
||
> éprouvé par la simulation. Prévoyez la clé, mais ne construisez pas d'écran autour d'une
|
||
> chose que le terrain n'a pas encore montrée.
|
||
|
||
Seconde clé du même lot, plus rare : `PHASE_LIMIT { limitW, requiredW }` — c'est la **limite de
|
||
phase** qui interdit, pas le budget. Deux causes, deux gestes : l'une se règle en délestant la
|
||
maison ou en revoyant l'abonnement, l'autre en attendant le soleil.
|
||
|
||
---
|
||
|
||
## 4. Le rang d'une borne est configurable, et il se classe avec les autres
|
||
|
||
`LoadConfig.priority`, comme toute autre charge. **Il n'y a pas de second système de priorité**
|
||
et il n'y en aura pas : un classement propre aux bornes ne saurait pas exprimer
|
||
« VE1 > ECS > VE2 », et deux classements qui ne peuvent pas s'interclasser sont un défaut, pas
|
||
deux fonctionnalités. `ChargingInfo` ne porte donc **aucun** champ de rang.
|
||
|
||
**Le glisser-déposer marche donc sur une seule liste**, bornes comprises, et se pose par
|
||
`SetLoadConfig` comme le reste. Vérifié sur machine, y compris l'inversion de rang entre deux
|
||
charges.
|
||
|
||
Le tri est **total** : à rang égal, l'identifiant départage. Laquelle est servie est donc
|
||
reproductible d'un cycle à l'autre, même avant tout réglage.
|
||
|
||
> **Réserve sur le rang par défaut, et elle vous concerne.** À la création automatique, la borne
|
||
> reçoit « le plus petit rang existant moins un, borné à 1 ». Quand une charge occupe déjà le
|
||
> rang 1 — c'est le cas du banc — l'intention « en tête » **dégénère en égalité** : les deux
|
||
> bornes et le chauffe-eau sont tous à 1. Sans conséquence sur la reproductibilité, mais
|
||
> **l'ordre affiché à l'installateur avant qu'il n'ait rien réglé n'est pas un choix, c'est un
|
||
> artefact**. Ne le présentez pas comme un réglage tant que le point n'est pas tranché côté
|
||
> moteur (`docs/RELEVE_3g2.md` §3.2). Les vrais rangs se posent à la mise en service, par vous.
|
||
|
||
---
|
||
|
||
## Ce qui n'a PAS changé, et qu'il ne faut pas déduire
|
||
|
||
- **L'échéance de départ et le tarif dynamique restent au proxy**, financés au réseau
|
||
(`funding: "grid"`, motifs `EV_DEADLINE` / `EV_SPOT_MARKET`). Leur puissance vit dans
|
||
`budget.evReservedW`, pas dans `budget.allocatedW`.
|
||
- **`chargingState` de `ChargingInfo`** garde son sens et reste publié par l'arbitre pour les
|
||
bornes qu'il commande.
|
||
- **`SetChargingInfo` n'est toujours pas partiel** — l'objet remplace le stocké, et un champ
|
||
writable omis retombe sur son défaut. La description publiée le dit désormais, et
|
||
`INTERFACE.md` en donne la table. Lire → modifier → renvoyer complet.
|
||
- **`repeatDays` va de 1 (lundi) à 7 (dimanche)**, pas de 0 à 6. La documentation disait le
|
||
contraire ; le code a toujours refusé le 0.
|