8 Commits

Author SHA1 Message Date
Patrick Schurig
74b7a84913 docs(maquette): la recharge accélérée n'est pas un vidage — et le brief moteur qui va avec
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
2026-08-27 21:46:03 +02:00
Patrick Schurig
b298c366d8 docs(maquette): la charge différée sort de l'écran — sans prévision, elle ne se règle pas
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
2026-08-27 21:25:41 +02:00
Patrick Schurig
77f29b3f3b docs(maquette): plus de permissions par charge — un seul interrupteur de décharge
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
2026-08-27 21:18:51 +02:00
Patrick Schurig
47495e48f6 docs(maquette): le rang sort de l'écran batterie, et les permissions ne gardent que la décharge
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
2026-08-27 21:12:13 +02:00
Patrick Schurig
aa22a52187 docs(maquette): « Configurer · Batterie » — septième écran, et un brief pour le moteur
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
2026-08-27 20:54:57 +02:00
Patrick Schurig
eac1dde4ee docs: recalage sur la rév. 3 du contrat charge pilotée
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
2026-08-25 21:45:53 +02:00
Patrick Schurig
054cbfab3c docs: maquettes de la refonte « Charges pilotables » + CLAUDE.md recalé sur la box
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
2026-08-25 21:30:32 +02:00
Patrick Schurig
0ebbde49da chore(docs): range specs/contrats/refs dans docs/, track l'autorité
- specs -> docs/ (DASHBOARD_SPEC compagnon du contrat)
- ref introspection nymea-jsonRPC -> docs/reference/
- mockups HTML -> docs/mockups/ ; briefs consommés -> docs/archive/
- track AGENTS.md + INTERFACE_etmvariableload.md (étaient untracked)
- INTERFACE_*: marqué miroir, canonique = repo plugin
2026-06-28 12:49:57 +02:00