3 Commits

Author SHA1 Message Date
Patrick Schurig
abcf80a5d3 feat(R3): counts — chaque watt dans un compteur, et GetChargingInfos filtre
counts est publié par niveau : { destination → watts }, Σ counts == targetW
toujours. Clés opaques et jamais recyclées, publié même à une seule destination
et même à zéro — un counts vide satisferait l'identité en perdant la destination,
sur le cas le plus fréquent.

L'identité ne passe plus par funding : elle somme des COMPTEURS, plus des
origines. EV_GRID_START y entre sans exception. funding et counts ne se dérivent
pas l'un de l'autre — l'un dit d'où viennent les watts, l'autre où ils sont
comptés.

LA GARANTIE EST UNE MACHINE. Sept sites construisent une LoadAction ; vérifier
qu'ils renseignent tous counts en RELISANT le code se referait à chaque site
ajouté. testCountsSumToTarget relit la charge utile — toutes les charges, trois
régimes dont l'import où tout vaut 0 — et constate le résultat.

ET LA CONTRE-ÉPREUVE A TROUVÉ UN TROU DANS LE TEST LUI-MÊME. Amputer le partage
d'EV_GRID_START le laissait PASSER : la borne, auto-provisionnée en queue par
LM-1209, ne voyait jamais de budget, et le régime n'était pas exercé. Corrigé —
rang 1, et EV_GRID_START en PREMIER régime, parce qu'une fois la borne en charge
son recrédit porte le budget bien au-dessus du plancher et le démarrage sous
tolérance ne peut plus se produire. Plus une assertion qui EXIGE que le régime
ait été exercé, sinon le test ne prouve rien du partage.

Deux fois dans ce lot, la contre-épreuve a corrigé le test plutôt que le code :
la première fois elle avait échoué au MONTAGE (NYMEA_PLUGINS_PATH omis) et ne
prouvait rien non plus. Un garde-fou qu'on n'a pas vu échouer POUR LA BONNE
RAISON ne vaut pas mieux qu'un garde-fou qu'on n'a pas vu échouer.

GetChargingInfos filtre sur evChargerId. Le paramètre était déclaré au schéma,
documenté « for all or a single EV charger », et Q_UNUSED : accepté, sans effet,
sans erreur. Des trois issues c'était la pire — un paramètre absent se voit, un
paramètre refusé se voit, un paramètre ignoré ne se voit pas. Identifiant inconnu
→ liste vide, jamais une erreur, et la liste vide est non ambiguë puisque toute
borne configurée a une entrée.

Balayage du namespace : sur 20 méthodes, 9 déclarent des paramètres et
GetChargingInfos était la seule à en ignorer un. Mon premier balayage avait
conclu « aucune méthode ne déclare de paramètre » — regex fausse ; refait en
croisant, pour chaque méthode, les params.insert de sa déclaration avec les
params.value de son corps.

Le garde-fou doxygen de ci-quality a par ailleurs attrapé ma propre insertion :
le bloc CountKey s'était glissé entre le commentaire de LoadAction et la
structure, qui perdait sa documentation.

simulation 34/34, charging 17/17, loadmodel 20/20, spotmarket 7/7, amd64 0/0,
doxygen 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
2026-08-30 10:14:19 +02:00
Patrick Schurig
58ec2d1110 [upstream-fix] SetChargingInfo promettait un partiel, le code remplace
La description publiée par l'introspection JSON-RPC annonçait « Only given
properties will be set, others will be untouched. » C'est faux : unpack()
construit un ChargingInfo NEUF depuis la charge utile, et setChargingInfo()
remplace le stocké en entier. Un champ writable omis retombe donc sur son
DÉFAUT — targetPercentage à 80, repeatDays vide, endDateTime absent,
assignedCarId nul — et l'appel réussit, EnergyErrorNoError. Rien ne signale
la perte.

Constaté sur le banc .75 le 2026-08-26 en restaurant la configuration : un
appel ne portant que chargingMode a porté targetPercentage de 0 à 80.

C'est la DESCRIPTION qui est corrigée, pas le comportement, et le choix
mérite d'être dit : ce .so est un drop-in de l'amont. Une sémantique de
fusion casserait le seul geste qui permet d'EFFACER un champ — retirer une
échéance, vider repeatDays — et changerait un contrat que les clients
existants suivent déjà. La description énumère désormais chaque défaut et
prescrit le geste correct : lire avec GetChargingInfos, modifier, renvoyer
l'objet complet.

Le test épingle le comportement annoncé, en trois temps : un objet complet,
un appel partiel qui aboutit et perd les champs omis, puis l'aller-retour
qui les préserve — y compris quand la charge utile relue porte
chargingState, propriété en lecture seule que la méthode doit ignorer sans
faillir. Il porte sur le COMPORTEMENT décrit, jamais sur la non-vacuité
d'une réponse.

Vérifié au passage : aucun autre Set* du namespace ne porte cette promesse
ni ce comportement. SetPhasePowerLimit, SetAcquisitionTolerance,
SetBatteryLevelConsideration et SetLockOnUnplug sont scalaires ;
SetSpotMarketConfiguration ne remet jamais un champ omis à son défaut ;
SetLoadConfig annonce déjà un remplacement d'ensemble.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj
2026-08-27 10:10:00 +02:00
b343650f9b initial commit 2026-01-11 11:09:23 +01:00