Trois corrections de Patrick sur la section batterie, plus le brief séparé.
1. LE BOOST CHANGE DE NATURE, et ce n'est pas un détail de libellé. Ce n'est pas « vider la
batterie dans la voiture » : c'est charger la voiture plus vite que l'abonnement ne le
permet. Un client en 6 kVA veut 7,5 ; le réseau donne 6, la batterie comble l'écart, le
disjoncteur ne saute pas.
Le réglage devient donc une PUISSANCE DE CHARGE VISÉE, pas une cible de SOC batterie, et
la première des trois fins devient « la voiture a atteint sa cible » — le plancher
batterie reste, mais comme garde-fou et non comme objectif. On ne cherche pas à vider la
batterie, on cherche à finir la charge plus tôt.
Ma réserve « le boost pourrait acheter en cachette » tombe, et elle tombe à l'envers : ce
mécanisme existe précisément pour NE PAS dépasser ce qu'on a le droit de soutirer.
Écrit dans la note : ce bloc appartient au délestage de LM-1006-1 vu par l'autre bout —
au lieu de réduire les charges pour tenir sous le plafond, on complète par la batterie.
Même objet, pas un mécanisme parallèle. Et c'est un argument de vente réel, donc il est
dit à l'écran : recharger plus vite sans passer de 6 à 9 kVA.
2. L'INTERRUPTEUR DE DÉCHARGE PROMETTAIT CE QUE LE MÉCANISME NE TIENT PAS. Il disait « la
maison continue d'être servie ; seule la voiture ne l'est pas ». enableDischarging est
binaire et l'onduleur ne voit qu'une consommation nette : coupé, le réfrigérateur passe au
réseau comme la voiture. Reformulé en « geler la batterie », avec le mécanisme expliqué.
La version modulée — plafonner dischargingRate sur la consommation hors borne, recalculé
à chaque cycle — part au brief comme question, pas à l'écran comme réglage.
C'est exactement la classe de défaut traquée depuis trois jours, et je l'avais écrite
moi-même.
3. LA RÉSERVE ADAPTATIVE DEVIENT UN PLANCHER DE SECOURS. Mon avertissement sur Battery Life
était juste mais incomplet : il disait ce que le bloc n'est pas sans dire ce qu'il est.
Combien d'autonomie garder pour une coupure est une préférence de RISQUE — elle dépend du
congélateur, du télétravail, de la fiabilité du réseau local. Héos ne pourra jamais la
décider à la place du client, ce qui la qualifie exactement selon le critère qui a fait
sortir les quatre autres blocs.
Deux détails de cohérence : la batterie est la seule charge sans compteur dédié possible —
elle publie currentPower elle-même — et c'est DIT plutôt qu'omis en silence comme dans les
cinq autres écrans. Et chargingRate trouve sa place : plafond de soutirage sur la charge
réseau, même motif que la recharge accélérée à l'envers.
Le brief moteur est à part, dans BRIEF_plugin_depuis_maquettes.md : les trois questions de
mécanisme (interdit actif, bail qui expire, controlHealthy), plus quatre que la recharge
accélérée soulève — la boucle à la minute contre un disjoncteur en secondes, la marge
dimensionnée contre le pire démarrage non piloté, le filet qui est la BORNE et non la
batterie, et la latence du Fronius à mesurer sur le banc. Avec la conséquence honnête écrite
noir sur blanc : en 6 kVA le gain réel est d'un à deux kilowatts, pas de 1,5 garanti.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
Quatrième passe, même logique que la précédente : ce qui demande un calcul revient à Héos,
pas à un formulaire. Démarrer assez tard pour arriver à 100 % juste avant la pointe suppose
de savoir ce que le soleil va faire — sans prévision ça ne marche pas, et une heure saisie à
la main serait un pari présenté comme un réglage.
Le gain reste écrit dans la note, parce qu'il justifie que le moteur s'en occupe : pic PV de
midi libéré pour l'ECS et la voiture, moins de temps passé à 100 % donc moins de
vieillissement calendaire, et aucun kWh sacrifié — la même énergie, stockée plus tard.
La frise du point de non-retour n'est PAS supprimée : elle remonte dans l'encart du motif
commun, où elle illustre ce que les trois cas partagent au lieu d'illustrer un réglage. C'est
même sa vraie place — une prévision n'engage personne, un point de non-retour si.
Et l'encart gagne une distinction qui manquait : quels paramètres sont calculés (charge
différée → Héos) et lesquels sont saisis (charge réseau, échéance véhicule). Le motif tient
dans les deux cas, ce qui renforce l'argument : le moteur porte la forme, Héos ou
l'utilisateur la remplit.
L'écran de configuration batterie ne garde donc que quatre blocs : le nom, le rang en lecture,
la décharge pendant recharge VE, la réserve adaptative, et la charge réseau manuelle. Quatre
passes, quatre retraits.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
Troisième passe de Patrick, et elle enlève encore. Le tableau « qui peut être servi par la
batterie » n'a pas lieu d'être : décider en général qui mérite d'être servi n'est pas un
réglage d'installateur, c'est un arbitrage — et il reviendra à Héos (l'optimiseur MILP) ou à
une décision par le prix, quand l'un ou l'autre existera. Offrir la question à la main
aujourd'hui figerait dans un écran ce qui doit se calculer.
Reste UN interrupteur : bloquer la décharge pendant la recharge du véhicule. C'est le seul
cas qu'aucun rang ne peut exprimer — la nuit il n'y a pas de surplus à répartir, donc aucune
position dans une file ne dit « ne vide pas la batterie dans la voiture ».
Le CSS des lignes de permission est retiré avec elles, plutôt que laissé mort dans la
feuille.
Trois passes, et l'écran n'a fait que rétrécir : un stepper de rang qui doublonnait la liste,
une colonne de permission qui doublonnait le rang, puis le tableau entier. Ce qui reste est
ce qu'aucun autre mécanisme ne sait dire.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
Deux corrections de Patrick, dont une qui rattrape une incohérence de ma part.
1. LE RANG N'A RIEN À FAIRE SUR CET ÉCRAN. J'y avais mis un stepper « Position −/+ ». Aucun
des six autres écrans de mécanisme n'en porte : le rang se règle par glisser-déposer dans
la liste des charges, et l'écran réel (load_mechanism_screen.dart) ne fait qu'« Inclure
dans l'arbitrage ». Deux réglages de rang auraient fini par diverger.
Il devient une ligne de lecture, qui rappelle le défaut — batterie en première position de
charge — et où il se règle vraiment.
2. « QUI A LE DROIT AVANT LA BATTERIE » N'EST PAS UN RÉGLAGE : c'est le rang. Mon tableau
avait deux colonnes, Solaire et Batterie ; la première disait exactement ce que la
position dit déjà. Retirée.
Ce qui reste est d'une autre nature, et c'est le seul cas qu'aucun rang ne peut exprimer :
la nuit, il n'y a pas de surplus à répartir, donc « ne vide pas la batterie dans la
voiture » n'est pas une position dans une file, c'est une permission de source. Un seul
interrupteur par charge : peut être servi PAR la batterie, ou non.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
La batterie devient une charge du waterfall, donc son bouton Configurer ouvre un écran de
mécanisme comme les cinq autres. Ajouté à la planche existante plutôt qu'à part : c'est
précisément la comparaison côte à côte qui avait révélé « six écrans, trois contrats » avant
qu'une ligne ne soit écrite.
RIEN DE CET ÉCRAN N'EXISTE. Ni BatteryAdapter, ni entrée LoadConfig pour le stockage, ni
prévision PV. Les seuls leviers réels de .75 sont les cinq actions de la classe SunSpec
Storage, et l'état réel affiché se limite à ce que le Thing publie — tout le reste porte un
badge « à créer », y compris là où ça alourdit la maquette.
Le rang par défaut est la batterie EN PREMIER, et ce n'est pas un choix de conception : c'est
la reproduction du comportement actuel. Une mise à jour ne change jamais l'arbitrage sans
geste ; la nouveauté est qu'il devient réglable.
Quatre blocs dans Configurer, dont deux qui ont demandé à être reformulés avant d'être
dessinés :
- Les blocages ne sont PAS une question de rang mais de SOURCE. « Ne pas vider la batterie
dans la voiture la nuit » ne s'exprime pas par un ordre de priorité — la nuit il n'y a pas
de surplus à répartir. La formulation juste est « quelles charges ont le droit d'être
servies par la batterie », réglée charge par charge. Le contrat porte déjà
Source { Solar, GridSource } ; il manque le troisième terme et le droit de le refuser.
- La réserve adaptative est montrée AVEC sa réserve : Victron a conçu Battery Life pour du
plomb, et sur du LFP le problème traité — la sulfatation — n'existe quasiment pas. Si le
mécanisme est repris, ce sera pour un motif à nommer, pas par mimétisme.
- La charge différée montre le POINT DE NON-RETOUR, pas la prévision. C'est lui qui garantit
qu'une prévision fausse ne coûte rien.
- La charge réseau pose le mécanisme seul. Ni prix, ni Tempo, ni marché de gros : le jour où
une source tarifaire existera, elle remplacera le doigt de l'utilisateur sans que le bloc
ne change.
Le boost vers le véhicule est HORS Configurer, sur la carte du tableau de bord : c'est le
seul cas où la batterie est une source et non une charge, et c'est un geste immédiat, pas une
intention durable. Trois fins possibles, toutes obligatoires — une action qui ne sait pas
s'arrêter n'a pas sa place sur un écran client.
Et le motif que la planche fait ressortir : charge différée, charge réseau et échéance
véhicule (LM-1009) ont la MÊME forme — une cible, une échéance, un point de non-retour. Trois
colonnes côte à côte le montrent ; le moteur peut l'implémenter une fois.
Trois questions écrites pour le moteur, pas résolues ici : bloquer demande un interdit ACTIF
et non une allocation à zéro, puisque la batterie agit d'elle-même · cet interdit doit
EXPIRER, sinon un HEMS qui tombe laisse la batterie inerte · et controlHealthy == false doit
sortir la batterie de l'arbitrage, comme available == false pour une charge en défaut.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
Le miroir INTERFACE_etmvariableload.md est passé en rév. 3 pendant la session (corps
identique au canonique du dépôt plugin, vérifié). Son §10 s'adresse nommément à l'agent
app — et il RATIFIE les maquettes dessinées aujourd'hui : relays[] = {thingId, powerW}
pour l'ECS relais, maxPowerW seul pour le continu, powerLevels absents de la config et
dérivés par le routeur.
Trois recalages :
- Le picker de relais filtre sur l'interface nymea « power » et présente les things par
NOM — nommé dans la maquette, il ne l'était pas.
- Le point 4 du brief plugin est tranché par la rév. 3 : la duplication de la
combinatoire est voulue (l'app dérive pour montrer, le routeur pour exécuter). La
demande devient donc un moyen de détecter la divergence — la télémétrie publie déjà
maxStageW, l'app peut y confronter le maximum de sa propre liste.
- En-tête du pont VM Service, que le commit précédent avait laissé dans sa forme brute.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
Trois maquettes, neuf écrans, après le passage sur appareil qui a montré que
/energy/setup décrit autre chose que l'installation.
- charges_pilotables_mockup.html : la liste à deux niveaux, avec le bandeau
d'arbitrage hérité de /energy/loads (qui disparaît) et la télémétrie sur la carte
de chaque charge.
- configurer_mecanismes_mockup.html : les six « Configurer » — routeur de relais,
modulable, SG-Ready, borne, zones HVAC, sélecteur d'appareils.
- raccordement_reseau_mockup.html : compteur réseau et protection de surcharge.
Décisions de conception que les images ne portent pas seules : les paliers
n'existent que chez le routeur de relais et y sont DÉDUITS des combinaisons ; le
mode fixed d'etmvariableload (legacy) n'est plus proposé mais reste lisible, parce
qu'ouvrir un écran ne doit pas convertir une config déployée ; SG1/SG2 sont une
convention d'app retrouvée par l'encodage standard, la box ne stocke qu'une liste
de relais par état ; le compteur par charge est dessiné inactif avec sa raison ; la
borne règle un mode par DÉFAUT, pas une commande.
CLAUDE.md corrigé contre JSONRPC.Introspect (plugin 1.15.2+etm15) :
- Energy.SetChargingMode n'existe pas — le « bug critique » décrivait un appel
impossible. Energy.* n'a que cinq méthodes.
- Le namespace EnergyPlugin.* n'existe pas : c'est NymeaEnergy.*, 20 méthodes et
14 notifications, toutes listées.
- ChargingInfo : chargingMode / targetPercentage / endDateTime (Uint), et non
mode / targetSoc / endTime. minCurrent n'existe nulle part.
- phasePowerLimit est en AMPÈRES par phase, et 0 coupe toute la recharge
intelligente.
- AirConditioning expose 8 méthodes, pas 3.
Toutes les méthodes citées ont été revérifiées une par une : aucune introuvable.
Règle ajoutée : contrôler ce fichier contre Introspect avant chaque lot — trois
fois aujourd'hui il a envoyé un agent dans le vide.
Et docs/BRIEF_plugin_depuis_maquettes.md : les six points que les maquettes
remontent vers le moteur, dont un seul blocage dur (pas de champ de courant
minimum pour le mode Min+PV).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H