Patrick Schurig 0f64609611 docs(relevé): 3g-1 sur machine — ce que le banc a dit, y compris ce qui fait mal
La transplantation ne commande rien tant que adjustEvChargers() n'est pas traité : l'arbitre
refuse, publie un motif juste, et le proxy commande ON à 6 A une ligne plus loin. La même
ligne part vers la Terra AC réelle et n'a rien fait UNIQUEMENT parce qu'elle est hors ligne.

Le plancher EV vaut la moitié du seuil de démarrage réel — acquisitionTolerance, qui est un
seuil de déclenchement, a été pris pour un plancher. 2828 W alloués, 4,1 A demandés, 6 A
exigés : allocation perdue.

La bascule 0,4 / 0,9 est faite dans les deux sens et la correction withheldW: 0 est vérifiée
sur machine. La moitié qui manque — la réserve retenant un surplus RÉEL — demande du soleil.

LM-1204-b n'est pas franchi : il exige deux bornes arbitrées, la Terra AC ne répond pas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
2026-08-26 18:39:10 +02:00

10 KiB
Raw Blame History

Relevé machine — 3g-1 sur le banc .75, 2026-08-26

Paquets : 1.15.2+etm20 → +etm21 → +etm22 (le dernier corrige ce que le passage machine a révélé). Batterie : Fronius Storage, SoC 50 %, Holding, capacity 10 kWh. Bornes : Terra AC réelle (192.168.1.132:502) + wallbox simulée.

Méthode. Ce qui suit distingue ce qui a été observé sur machine, ce qui vient d'un test unitaire, et ce qui n'est qu'une lecture de code. Les trois ne valent pas pareil, et mélanger les trois est exactement ce qui a envoyé un agent dans le vide le 2026-08-25.


1. La bascule batteryLevelConsideration — FAITE, dans les deux sens

Première exécution possible depuis que nymea-plugin-sunspec-etm01 1.15.2+etm11 déclare energystorage : la batterie est enfin vue (Having 1 batteries. Overall battery level is 50 %, journal 17:16:00).

seuil surplus motif publié
etm20, 17:12:00 0,9 négatif BATTERY_RESERVE, « 0 W de surplus retenus »
etm21, 17:17:00 et 17:18:00 0,9 négatif SURPLUS_INSUFFICIENT
etm21, 17:19:00 0,4 négatif SURPLUS_INSUFFICIENT

La correction withheldW: 0 est vérifiée sur machine. L'A/B est propre et les deux côtés sont au journal : dans des conditions identiques — même batterie, même seuil, même surplus négatif — etm20 disait « une règle vous bloque » et etm22 dit « il n'y a rien ». C'était la confusion que ce motif existe pour supprimer, retournée contre elle-même.

⚠️ La moitié qui manque, et il faut la dire : la réserve n'a jamais été observée en train de retenir un surplus réel (withheldW > 0). Il y aurait fallu un surplus positif et un SoC sous le seuil, et l'observation s'est faite en soirée. La courbe du jour montre que c'est atteignable : surplus maximum 8 733 W à 12:46Z. À refaire vers midi.

Défaut de rendu corrigé au passage : le journal affichait « batterie à 50 %% ». %% est une échappée printf ; QString::arg ne l'interprète pas.


2. LM-1204-b — NON franchi, et pour deux raisons distinctes

2.1 Ce qui a été démontré

Le discriminant fonctionne et il a été observé sur une borne réelle du contexte (cycle 17:34:00, +etm22) :

{09e520fd…}  0 W  funding=surplus   BELOW_MIN_POWER {budgetW: 1096, minPowerW: 2070}

journal : « Budget 1096 W sous le premier palier de Simulated wallbox (2070 W) — 0 W, le budget passe à la charge suivante ». C'est l'arbitre qui a refusé, et il le dit.

La cascade se réconcilie : chauffe-eau 3500 + pac-terrain 3000 = 6500 = budget.allocatedW. La borne indisponible sort en LOAD_UNAVAILABLE + faultCode: THING_MISSING.

2.2 Pourquoi le critère n'est pas franchi

Il faut DEUX bornes arbitrées. Il n'y en a qu'une. La Terra AC est injoignable — ThingErrorTimeout à chaque tentative de setup, connected: false — donc elle n'est pas une concurrente dans la cascade, elle est un défaut. Le scénario « 4 kW, deux planchers de 1,4 kW, une seule démarre » n'a pas d'objet tant que la seconde borne ne répond pas.

Et le test de rang est impossible tel qu'il est écrit — voir §3.2 : le rang d'une borne n'est pas configurable.


3. Ce que le passage machine a révélé, et qui n'était visible d'aucune autre façon

3.1 CORRIGÉ (+etm22) — les bornes étaient arbitrées sans franchir la frontière RPC

buildTelemetry() n'interrogeait que la table des charges de configuration et sautait l'EV avec un commentaire devenu faux. Le journal montrait les décisions des deux bornes ; GetLoadTelemetry n'en montrait aucune. Le changement de contrat annoncé par +etm21 n'était donc pas livré — et LM-1204-b était inéprouvable, puisque son discriminant est le motif publié.

Nouveau champ loads[].funding ("surplus" / "grid"), rendu nécessaire par l'entrée des bornes : une borne servie par la cascade est comptée dans budget.allocatedW, une borne servie par l'échéance ou le tarif dynamique soutire au réseau et vit dans budget.evReservedW.

3.2 NON CORRIGÉ — le rang d'une borne n'est pas configurable

EvAdapter::descriptor() fixe priority = 100 en dur, pour toutes les bornes. Deux bornes ont donc la même clé de tri, et std::sort n'est pas stable.

La cascade reste correcte — une seule démarre, l'autre reçoit son motif — mais laquelle n'est ni réglable ni garanti reproductible d'un cycle à l'autre. À ne pas présenter comme un réglage dans l'app avant qu'un lot dédié le traite.

3.3 NON CORRIGÉ, ET C'EST LE PLUS GRAVE — la double commande

Journal 17:34:00, deux lignes consécutives :

[Arbitre] "{09e520fd…}" → "Budget 1096 W sous le premier palier … — 0 W"
---------------- Adjusting chargers ----------------
Executing action Simulated wallbox to power: ON, Charging current: 6A, Issuer: Surplus

adjustEvChargers() re-décide le surplus après le waterfall et passe outre le refus de l'arbitre. 3g-1 a retiré la planification du surplus EV (la branche else de planSurplusCharging est devenue continue) mais a laissé tourner le chemin d'exécution du proxy, qui dérive sa propre consigne. La même ligne part vers la Terra AC réelle — elle n'a rien fait cette fois uniquement parce que la borne est hors ligne.

Conséquence à énoncer sans détour : tant que ceci n'est pas traité, la transplantation ne commande rien. L'arbitre décide, publie un motif juste, et le proxy commande autre chose une milliseconde plus tard. Tout ce qui précède dans ce relevé décrit une décision qui n'atteint pas le matériel.

Ce n'est pas corrigé ici volontairement : la correction porte sur le chemin d'exécution d'une borne réelle, et le choix — adjustEvChargers() ne doit plus agir que sur les bornes ayant reçu une action financée au réseau — est un arbitrage de lot (3g-2), pas une retouche. L'improviser un soir sur un banc qui porte une Terra AC serait exactement le genre de décision que le workflow interdit.

3.4 NON CORRIGÉ — le plancher EV est sous le seuil de démarrage réel

evFloorW() = max(borne, voiture) × acquisitionTolerance × 230 × phases. Avec 6 A, une tolérance de 0,5 et 3 phases : 2 070 W. Or démarrer exige réellement 6 A × 230 × 3, soit 4 140 W.

Observé au cycle 17:33:00 : le waterfall alloue 2 828 W à la wallbox (au-dessus du « plancher » de 2 070), puis l'adaptateur convertit — 2828 / (230 × 3) = 4,1 A, sous les 6 A minimum — et refuse d'enclencher : activé: false | courant: 0 A. 2 828 W ont été retirés du budget et donnés à personne.

acquisitionTolerance était, chez le proxy, un seuil de déclenchement — la part de réseau qu'on accepte de soutirer pour démarrer. Ce n'est pas la même grandeur qu'un plancher au sens du §12, qui doit être la puissance que la charge exige réellement. Les deux ont été confondus en écrivant evFloorW().


4. Autres constats, hors 3g

  • Energy.GetPowerBalanceLogs à SampleRate15Mins rend 50 échantillons TOUS À ZÉRO, alors que SampleRate1Min (681 échantillons) et SampleRate1Hour (12) sont peuplés sur la même fenêtre. C'est le cœur nymea, pas notre plugin — mais c'est la table que l'écran d'historique de l'app voudra. À vérifier avant de promettre les 90 jours.
  • SetChargingInfo substitue targetPercentage = 80 à un champ absent. Constaté en restaurant le banc : envoyer l'objet sans ce champ l'a mis à 80 alors qu'il valait 0. Un champ omis y acquiert donc une valeur — l'app doit toujours l'envoyer explicitement.
  • Les Things Fronius portent des octets nuls de bourrage dans leur nom ("Fronius" suivi de 25 \0 avant " Storage"). Cosmétique, mais cela traversera jusqu'à l'écran.
  • currentPowerStorage vaut 0 au bilan de puissance. Cohérent avec une batterie Holding / idle, donc non concluant : on ne peut pas encore distinguer « au repos » de « non câblé ». À reprendre quand la batterie charge ou décharge vraiment.

5. État où le banc a été rendu

valeur pourquoi
Paquet 1.15.2+etm22 contient la correction du §3.1
batteryLevelConsideration 0,4 — PAS remis à 0,9 voir ci-dessous
acquisitionTolerance 0,5 inchangé
phasePowerLimit 0 inchangé — désactive la protection de surcharge
LoadConfig identique à l'octet près au relevé d'avant vérifié par comparaison
ChargingInfos identiques à l'origine les deux bornes en ChargingModeNormal, sans voiture
Voitures simulées de test retirées LM1204-CAR-A/B-A-SUPPRIMER, ajoutées puis supprimées

Le seuil n'est délibérément PAS remis à 0,9. À 0,9 avec une batterie à 50 %, la règle gèle toute l'installation — c'est exactement ce que le journal de 17:12:00 montre, et c'est la raison pour laquelle le défaut d'usine est passé à 0,2 en +etm20. Y revenir rendrait le banc inutilisable pour la reprise de LM-1204-b, sans rien démontrer de plus. 0,9 n'était pas un réglage voulu du banc : c'était l'ancien défaut d'usine, hérité.

Pour reprendre l'essai (les deux voitures sont à recréer)

dart tools/rpc/probe.dart 192.168.1.75 Integrations.AddThing \
  '{"thingClassId":"4afcafb1-d341-4532-8062-738cae529cb5","name":"LM1204-CAR-A-A-SUPPRIMER"}'
dart tools/rpc/probe.dart 192.168.1.75 NymeaEnergy.SetChargingInfo \
  '{"chargingInfo":{"evChargerId":"<borne>","chargingMode":"ChargingModeEco","assignedCarId":"<voiture>"}}'

6. Ce qui manque au banc — la question posée, la réponse

La Terra AC ne répond pas (192.168.1.132:502, ThingErrorTimeout à chaque cycle). C'est le seul obstacle matériel, et il bloque à lui seul LM-1204-b, qui exige deux bornes arbitrées.

Et une wallbox simulée ne remplace pas la seconde : elle ne prouve ni le délai d'établissement, ni la négociation de courant, ni ce qu'une borne fait d'une consigne qu'elle n'accepte pas. Elle a un défaut de plus, visible ce soir : elle annonce desiredPhaseCount: 3 en permanence, ce qui a suffi à faire viser 3 phases et à porter le plancher publié à 2 070 W alors qu'aucun véhicule n'est branché. Sur ce point précis, le simulateur a fabriqué la condition du défaut §3.4 — utile pour le trouver, sans valeur pour le valider.