RECAP.md porte les neuf commits du jour et les trois défauts que seul l'appareil a montrés :
deux cibles tactiles hors norme (26 px, 18 px), et une correction naïve laide que le test
validait — c'est la capture qui a tranché, pas l'assertion.
Ce qui manquait dans le suivi, et qui est écrit noir sur blanc :
AUCUN BILAN JOURNALIER N'EXISTE DANS L'APP. Les quatre tuiles du tableau de bord mêlent deux
natures de grandeur, et aucune n'est journalière — Autoconsommation et Autonomie sont des
pourcentages instantanés, Vers réseau et Depuis réseau des kWh cumulés depuis l'origine. Les
deux dernières portaient « aujourd'hui » : totalReturn n'est pas remis à zéro chaque nuit,
d'où le « · cumulé ». Le bilan du jour reste à écrire, et le TODO porte les trois pièges de
sa mise en œuvre — bornes à minuit local, clés de log SANS le préfixe currentPower, et les
cumulés corrompus de .75.
Et la question ouverte au moteur, telle qu'elle s'est posée : « ECS d'abord, batterie
ensuite » n'est pas exprimable aujourd'hui, et batteryLevelConsideration fait l'inverse — il
annule le budget SOUS le seuil, donc la batterie passe avant. La cause est structurelle : la
batterie n'est dans aucun GetLoadConfig et le plugin ne la commande nulle part. Le levier
existe pourtant, la classe SunSpec Storage de .75 déclare enableCharging, chargingRate et
enableDischarging.
Deux entrées du TODO étaient devenues fausses : kCodesReserveBatterie est fait, et « Zones —
à ne pas entreprendre » est revu puisque le lot est cadré.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
Le fil conducteur des six commits du jour tient en une phrase : l'app ne recalcule pas
ce que la box publie, et n'anticipe pas ce qu'elle n'a pas encore publié.
Le point à retenir est le seuil de réserve batterie : 0,9 en usine, batterie du banc à
50 %. Quand la règle montera dans le budget, `.75` ne pilotera plus rien par grand
soleil, sans erreur ni panne. Le RECAP le dit, et le TODO garde les trois crochets
laissés ouverts — dont le `Set` volontairement vide qui attend le code du moteur.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff
Ce qui a été livré (6 commits), ce qui a été vérifié SUR L'APPAREIL contre .75, l'erreur de
la soirée — les écrans construits sans ouvrir docs/mockups/, tout à refaire — et les cinq
points repris demain matin, dont le glissement et le sélecteur de mécanisme que j'avais rendu
non cliquable à tort.
Renvoie au BRIEF_agent_plugin.md du dépôt plugin pour les sept constats côté moteur.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff
flutter run n'imprime pas les traces dart:developer log() — elles partent au VM
Service, ni sur stdout ni dans logcat. Ce pont est ce qui a rendu le passage sur
appareil du 2026-08-25 exploitable ; il ne vivait que dans un scratchpad de session,
alors que le RECAP le donnait « à récupérer au prochain passage ».
Mode d'emploi en tête, avec le piège déjà payé une fois : le VM Service livre
l'historique bufférisé d'un coup au streamListen, donc horodater à la réception
écrase toute la chronologie sous une seule seconde. On lit event.timestamp.
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
Premier passage de l'app sur matériel (Redmi Note 9S). MIUI refuse INJECT_EVENTS :
Patrick a piloté, l'agent a observé. Captures et trace dans screenshots/ (ignoré).
Acquis à l'écran :
- §7-9 : trois Hello, tous « demandée fr_FR → retenue fr_FR », la valeur relue venant
de m_clientLocales côté box. Mais la locale de l'APPAREIL (en-GB) ne part pas :
_requestedLocale est figé à fr_FR et rien ne câble l'un sur l'autre.
- §7-5 : l'écran suit l'arbitre sans un geste — budget, allocation, motif recomposé,
et le recrédit anti-clignotement visible en clair.
- Écriture de domaine faite au doigt, confirmée par la box et le fichier persisté.
- SetLoadConfig 19:53:46.007 → LoadConfigChanged .288 → ack .303 : la confirmation
a battu l'acquittement de 15 ms. Conclure sur l'ack, c'est courir après sa propre
notification.
§7-6 : l'état « données anciennes » est INATTEIGNABLE par perte de lien — la garde de
routage évacue vers /installations une seconde après la coupure, bien avant le seuil de
180 s. Le compteur de fraîcheur, lui, a été vu vivre. Seul sinceCycleAdvanced peut donc
réellement déclencher le bandeau : l'arbitre figé socket vivante.
Huit défauts constatés, aucun corrigé (constat d'abord) : pas de reconnexion
automatique, /energy/setup qui décrit une autre installation que la vraie, cumuls
aberrants venus de la box, thème ignoré, débordements, titre dupliqué, instantané
effacé hors connexion, bruit de transport.
Décisions de Patrick : l'ordre se règle dans « Charges pilotables » au glisser-déposer,
/energy/loads disparaît, deux niveaux conservés, chip « À déclarer » → bouton
Configurer ouvrant le mécanisme et ses Things.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
Patrick a relancé le service à 18:23:45. LoadConfigStore recharge 2 configs depuis
/var/lib/nymea/energy-load-configuration.json et la relecture RPC est identique clé
par clé à celle d'avant l'arrêt : chauffe-eau→ecs, pac-terrain→hvac.
Le redémarrage montre aussi la reprise à froid vue du dehors (ECS-411-b) : les
adaptateurs journalisent ce qu'ils ont LU sur les contacts, avec la mention
« correspondance exacte » — l'état de départ est constaté, pas supposé.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
Trois sondes permanentes, et le relevé de ce qu'elles ont montré sur .75.
- telemetry_watch.dart (lecture seule) : cadence du battement de cœur et avancée
du cycle. C'est la seule mesure qui distingue une installation stable d'un
moteur arrêté — les deux produisent le même flux de trames.
- domain_assign.dart (écrit, --yes) : classe des charges en important le code
d'écriture de l'app. Une sonde qui sérialiserait à sa façon ne prouverait rien
sur l'app.
- fault_probe.dart (écrit, --yes) : provoque une charge en défaut, éprouve
ClearLoadFault, restaure. La config d'origine part sur le disque AVANT la
première écriture et la restauration rejoue ce fichier verbatim — filtrer la
sonde hors d'une relecture serait une reconstruction, c'est-à-dire le chemin
qui a failli écraser la config du banc via persist(). La config relue est
ensuite comparée clé par clé, pas seulement « la sonde a disparu ».
Ce que le banc a appris, et qui ne se déduisait pas du code :
- ClearLoadFault lève RÉELLEMENT le verrou (journal : « défaut LEVÉ ») et le
cycle suivant reverrouille, la cause n'ayant pas disparu. La télémétrie ne
repasse jamais à available:true. Une app qui aurait cru l'acquittement aurait
menti deux secondes plus tard.
- Le défaut publié est WRITE_FAILED, pas THING_MISSING : l'écriture est tentée,
échoue, l'échelle s'épuise, et m_faulted masque le second code.
- SetLoadConfig refuse en bloc et le motif n'est PAS dans la réponse RPC — il est
au journal. L'app ne peut que rapporter un refus, pas l'expliquer.
- ECS-412 confirmé au journal : une écriture de domaine ne reconstruit aucun
adaptateur (« 2 inchangée(s), 0 retirée(s) »). Le §7-1bis n'est plus une
lecture de code.
.75 est laissé classé (chauffe-eau→ecs, pac-terrain→hvac) : métadonnée que
l'arbitre ne lit pas, et le multi-domaines y devient exerçable. Retour arrière :
domain_assign.dart --clear --yes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
Le bloqueur consigné en fin de session précédente est levé : GetLoadTelemetry,
LoadTelemetryChanged et o:domain sont sur .75 (20 méthodes / 14 notifications).
Mais consigne explicite de Patrick : ne PAS enchaîner sur la moitié télémétrie
tant que l'agent plugin n'a pas fini son travail sur pac-terrain. L'écart
GetLoadTelemetry (2 charges) / GetLoadConfig (1 charge) est ce travail en
cours, pas une anomalie à remonter — coder contre cet état serait coder contre
une cible mouvante.
Consigne aussi que l'arbitre tourne bien (timestamp +1 min/cycle), ce qui fixe
le plancher du seuil de fraîcheur §7-6 à plus de 60 s.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Point d'arrêt avant redémarrage machine. Consigne l'état du lot B app, le
bloqueur (lot plugin non déployé sur .75), le partage vérifié/non-vérifié,
et la question ouverte sur la fusion /energy/setup ↔ /energy/loads.
Note l'accès ssh au banc désormais documenté dans CLAUDE.md : il lève la
limite qui rendait le critère 7-1bis (non-reconstruction des adaptateurs)
invérifiable faute de journal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>