Le HANDOFF chargé à chaque session pesait 7566 lignes. Split vérifié par md5 : §1→§72 (09/07→17/09) déplacés SANS PERTE dans HANDOFF_ARCHIVE_2026.md (non chargé par CLAUDE.md, grep/lecture à la demande). HANDOFF.md ne garde vivantes que les sessions récentes §73→§77 + un index des sections archivées. Aucune info perdue, aucun changement d'@-import nécessaire (l'archive n'est pas référencée). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
504 lines
31 KiB
Markdown
504 lines
31 KiB
Markdown
# HANDOFF.md — EVCS / Grid Hysteresis — État au 09/07/2026
|
||
|
||
> ⚠️ Les sessions **§1 à §72** (09/07 → 17/09/2026) sont ARCHIVÉES SANS PERTE
|
||
> dans `HANDOFF_ARCHIVE_2026.md` (fichier NON chargé automatiquement —
|
||
> `grep`/lecture à la demande). Ce fichier ne garde vivantes que les sessions
|
||
> récentes (**§73→**). Split lossless vérifié par md5 le 24/09/2026.
|
||
|
||
## Index des sessions archivées (§1→§72 — détail dans HANDOFF_ARCHIVE_2026.md)
|
||
|
||
- 1. Contexte Projet
|
||
- 2. Architecture Data-Flow
|
||
- 3. Patchs déployés le 09/07/2026 (session opencode)
|
||
- 4. Root Cause de l'incident « EDF ON à 48 % sans charge Jeep » (nuit 08→09/07)
|
||
- 5. Sémantique utile (pour ne pas re-chercher)
|
||
- 6. Méthode de travail validée (à réutiliser)
|
||
- 7. Backlog / Point du 10/07 au réveil
|
||
- 8. Historique des sessions
|
||
- ⚙️ CONFIG LOCALE — Qwen3.6-35B sur DGX Spark (≠ gros modèle cloud)
|
||
- 🧩 SKILLS n8n — validées le 16/07/2026 (Qwen3.6 local, PAS gros modèle)
|
||
- 9. Session du 18/07/2026 (matin)
|
||
- 10. Session du 04/08/2026 (matin) — Fix grid HC 1h30 + libération takeover
|
||
- 11. Session du 07/08/2026 — P7 : Charge HC nuit automatique (VW hybride) + outillage push
|
||
- 12. Session du 07/08/2026 (soir) — R1 : Déduplication du moteur nuit
|
||
- 13. Session du 07/08/2026 (soir) — R3 : Suite de tests golden
|
||
- 14. Session du 07/08/2026 (soir) — P8 : Libération EDF quand le véhicule est plein
|
||
- 15. Session du 07/08/2026 (soir) — R2 : Git = source de vérité du code des nodes
|
||
- 16. Session du 07/08/2026 (nuit) — Agent IA planificateur en MODE OMBRE
|
||
- 17. Session du 08/08/2026 — Incident rebond EDF nuit 07-08/08 : fixes A/B/C/D
|
||
- 18. Session du 09/08/2026 — Incident nuit 08-09/08 : le rebond est revenu par un AUTRE chemin (Fix E)
|
||
- 19. Session du 09/08/2026 (suite) — Fix F : la mémoire "charge terminée" est prioritaire sur les gardes
|
||
- 20. Session du 09/08/2026 — PREMIER CÂBLAGE RÉEL de l'advisor : cible SOC matin
|
||
- 21. Session du 09/08/2026 — P9 : charge réseau avant départ + sélecteur véhicule VW/JEEP
|
||
- 22. Session du 09/08/2026 — P10 : mémoire "véhicule plein" en journée
|
||
- 23. Session du 12/08/2026 — P11 : reset du collant au changement de véhicule
|
||
- 26. Session du 27/08/2026 — L'oscillateur du takeover nuit, et deux acteurs non lus
|
||
- Diagnostic en attente de vérification (Qwen, 22/08)
|
||
- 24. Session du 22/08/2026 — Fix G : coupure EDF quand l'override EVCS disparaît mi-session (hystérésis)
|
||
- 25. Session du 24/08/2026 — Fix G retiré + diagnostic des deux oscillateurs
|
||
- 27. Session du 30/08/2026 — Deux propriétaires GX de la décision réseau, jamais lus
|
||
- 28. Session du 31/08/2026 — La substitution ne se reproduit plus (batterie preneuse). Cas « batterie pleine » encore ouvert
|
||
- 29. Session du 31/08/2026 (suite) — Fenêtre d'établissement du réseau
|
||
- 30. Session du 31/08/2026 (suite) — Séquence d'ouverture du contacteur
|
||
- 31. Session du 01/09/2026 (nuit) — La consigne réseau appartient désormais au contrôleur
|
||
- 32. Session du 01/09/2026 (nuit) — État réel de la résilience, faits vérifiés
|
||
- 33. Session du 01/09/2026 (nuit) — Chien de garde EVCS sur le second Synology
|
||
- 34. Session du 01/09/2026 (nuit) — Sauvegarde Home Assistant : elle n'existait pas, elle existe
|
||
- 35. Session du 01/09/2026 — La table des bandes SOC déclarait un temps que personne ne lisait
|
||
- 36. Session du 03/09/2026 — Un demarrage se decide sur l'excedent, pas sur la production
|
||
- 37. Session du 03/09/2026 (suite) — La provocation solaire demandee aux chargeurs eux-memes
|
||
- 38. Session du 03/09/2026 (suite) — Le plancher du soir cesse d'etre un niveau fixe
|
||
- 39. Session du 03/09/2026 (suite) — La cible du matin devient une energie
|
||
- 40. Session du 03/09/2026 (suite) — Session reseau UNIQUE de la nuit
|
||
- 41. Session du 03/09/2026 (suite) — La conversion energie ↔ SOC devient une courbe mesuree
|
||
- 42. Session du 04/09/2026 — Pourquoi la memoire s'arretait a la semaine
|
||
- 43. Nuit du 03-04/09 — La session unique tenait le contacteur, pas l'energie
|
||
- 44. Mise en service d'InfluxDB (04/09/2026 07:28) — la memoire est complete
|
||
- 45. Session du 04/09/2026 — Grafana : les deux bases se consultent
|
||
- 46. Session du 04/09/2026 — « Le systeme » : replique locale de la vue VRM
|
||
- 47. Session du 04/09/2026 (soir) — Connexion InfluxDB retiree du YAML, et un effet de bord du redemarrage
|
||
- 48. Session du 05/09/2026 — Le bridage de charge est une CELLULE, et il commence a 75 %
|
||
- 49. Session du 05/09/2026 — Le bridage a un coupable nomme : le module d'adresse 3
|
||
- 50. Session du 05/09/2026 (midi) — Lecture en direct de la chaine BMS depuis Linux
|
||
- 51. Session du 05/09/2026 (12h30) — Le parc est sain a cinq modules sur sept, et le hub n'est pas coupable
|
||
- 52. Session du 05/09/2026 (12h45) — BAT3 lue isolee : UNE cellule, et elle est en meilleur etat que deux modules restes en service
|
||
- 53. Session du 05/09/2026 (13h05) — Le declenchement saisi en direct, et l'origine des degats
|
||
- 54. Session du 05/09/2026 (18h30) — Le tampon d'evenements : la cellule 12 est detruite en ce moment, toutes les nuits
|
||
- 55. Session du 05/09/2026 (19h15) — La cellule 15 de BAT3 ne peut plus debiter, meme pleine
|
||
- 56. Session du 05/09/2026 (soir) — Dates d'achat, et ce qu'elles eliminent
|
||
- 57. Session du 06/09/2026 — Le relachement du contacteur se mesurait sur un instant
|
||
- 58. Session du 07/09/2026 — La borne annonce « charge terminee » apres 0,10 kWh
|
||
- 59. Session du 07/09/2026 — Fix H : un statut « charged » doit etre credible
|
||
- 60. Session du 07/09/2026 — Tableau « La journee », et un report de valeur qui fabriquait de l'energie
|
||
- 61. Session du 08/09/2026 — La prevision se juge sur la date qu'elle decrit
|
||
- 62. Session du 11/09/2026 — Le credit solaire sur la consigne reseau, valide en reel
|
||
- 63. Session du 13/09/2026 — Le conseiller ne lisait pas mal : on lui parlait mal
|
||
- 64. Session du 13/09/2026 — Le parc butait sur NOTRE plafond, pas sur le sien
|
||
- 65. Session du 13/09/2026 (soir) — Six sessions sur douze etaient mal etiquetees
|
||
- 66. Session du 13/09/2026 (soir) — Contrat d'etiquetage du concierge, arrete des deux cotes
|
||
- 67. Session du 14/09/2026 — L'auteur des transitions etait lisible depuis le debut
|
||
- 68. Nuit du 14 au 15/09/2026 — P12 : la voiture se charge sur le solaire d'hier
|
||
- 69. Session du 16/09/2026 — Premiere nuit P12, battement de coeur, guichet d'ordres, et la telemetrie BMS qui ment
|
||
- 70. Nuit du 16 au 17/09/2026 — Le chien de garde arretait la charge qu'il devait proteger
|
||
- 71. Session du 17/09/2026 — La prevision entre dans les tableaux Grafana
|
||
- 72. Session du 17/09/2026 (apres-midi) — Le reseau force a une echeance, et la surconsigne n'etait pas le defaut qu'on croyait
|
||
|
||
---
|
||
|
||
## 73. Nuit du 17 au 18/09/2026 — Dissocier la recharge batterie de la fonction EVCS
|
||
|
||
### Ce qui a declenche le chantier, et une correction de sens
|
||
Deux sessions ont passe l'apres-midi a signaler au proprietaire qu'un changement
|
||
de la consigne reseau reduisait la recharge de la batterie sous
|
||
`force_grid_now`. Sa reponse ferme la question : **« force_grid_now pour moi
|
||
c'est pour recharger la voiture, ça me va comme cela »**. Nous raisonnions tous
|
||
les deux comme si cette strategie etait une commande de recharge batterie —
|
||
parce que l'episode du 17/09 en avait eu l'effet. C'etait un effet de bord, pas
|
||
l'intention.
|
||
|
||
⚠️ A retenir : `force_grid_now` = « charge la voiture sur le reseau
|
||
maintenant ». Toute consequence sur la batterie solaire est incidente et ne doit
|
||
pas servir d'argument pour ou contre une evolution de cette strategie.
|
||
|
||
Et c'est lui qui en tire le chantier : **la recharge de la batterie solaire doit
|
||
devenir une fonction propre**, avec son tableau de bord et sa commande — un
|
||
bouton de charge manuelle en heures creuses pour les cas ou le bilan ne
|
||
l'autorise pas alors que la meteo ou la brievete du jour d'hiver la justifient.
|
||
|
||
### FAIT — le detail par cellule pendant la charge (Grafana)
|
||
Le detail cellule par cellule existait deja. Ce qui manquait etait le « pendant
|
||
la charge » : rien ne disait QUAND le module chargeait, et **un ecart entre
|
||
cellules ne veut rien dire hors de son courant** — le 05/09, meme module et meme
|
||
tension de parc, la cellule meneuse etait a 3555 mV sous 50 A et a 3496 mV sous
|
||
30 A.
|
||
|
||
Le releve console porte le courant depuis le 17/09 mais l'ingestion ecrivait
|
||
`null`. Elle lit desormais la colonne `courant_mA`, **reperee par son NOM** : les
|
||
US5000 et les US2000C n'emettent pas les memes colonnes, et une position ferait
|
||
lire la mauvaise grandeur des qu'on deplace le cable d'un pack a l'autre.
|
||
|
||
Deux panneaux : « Sante des modules — tous, d'un coup d'oeil » (le resume qu'on
|
||
lit en premier, la ou les autres panneaux demandent de choisir un module
|
||
d'avance) et « Le module chargeait-il ? ». Releve au deploiement, cinq modules
|
||
vivants, ecarts de **1 a 3 mV** — le parc restant est sain.
|
||
|
||
### CONCEPTION ARRETEE, NON ECRITE — la charge HC manuelle
|
||
Trois pistes, mesurees sur banc golden par la session voisine.
|
||
|
||
**Piste 2 — un chemin separe qui ne toucherait que la consigne et le contacteur :
|
||
MORTE.** Le controleur reecrit la consigne de repos a chaque cycle des que
|
||
`gridSessionActive` est faux : sonde a 5000 W poses de l'exterieur, ramenes a
|
||
700 W en trente secondes. Elle ne se fait pas ecraser par accident, elle
|
||
**s'auto-detruit** : pour qu'elle tienne il faudrait que le controleur la
|
||
connaisse, donc qu'elle ne soit plus separee.
|
||
|
||
**Piste 3 — forcer la cible du bilan : RETENUE, mais l'ENDROIT du forcage decide
|
||
de tout.**
|
||
|
||
| forcage | takeover | consigne | resultat |
|
||
|---|---|---:|---|
|
||
| **en aval** (un bouton ecrit `balance_required_soc`) | engage | **700 W** | batterie a ~100 W |
|
||
| **en amont** (dans `BALANCE.js`) | engage | **2333 W** | le manque reel finance |
|
||
|
||
⚠️ **Le cas en aval est le pire des trois parce qu'il A L'AIR DE MARCHER** :
|
||
contacteur ferme, session engagee, et une consigne qui ne finance rien. C'est le
|
||
defaut du 01/09 et du 04/09 a l'identique, et le seul des trois resultats
|
||
indiagnosticable depuis le tableau de bord. `required_soc` ENGAGE la session,
|
||
`grid_energy_needed_kwh` la FINANCE : les desolidariser donne une session muette.
|
||
|
||
**Point d'insertion, une ligne.** `BALANCE.js`, fonction `coverage()`, ligne 226 :
|
||
|
||
const requiredSocRawPct = socForEnergy(energyToCoverKwh, socFloorPct);
|
||
|
||
`requiredSocPct`, `socMarginPct`, `gridNeeded` et `gridEnergyNeededKwh` en
|
||
derivent tous dans les huit lignes suivantes. Un `Math.max(..., plancherManuel)`
|
||
a cet endroit fait donc bouger la cible ET le manque **ensemble par
|
||
construction** : le couplage implicite qui fait la fragilite de la piste 3
|
||
devient une consequence de la forme du code, et non une discipline a tenir.
|
||
|
||
### Trois effets a connaitre avant d'ecrire
|
||
1. **Les deux profils montent ensemble.** `coverage()` est appelee pour p50 ET
|
||
p80, donc le plancher monte aussi `required_soc_prudent`, qui est le plancher
|
||
de P12. Pendant une charge manuelle, la voiture cesserait donc de descendre
|
||
la batterie. Coherent — on ne remplit pas au reseau pendant que la voiture
|
||
vide — mais **une nuit ou il veut les deux, il n'aura que l'un**. C'est
|
||
structurel, aucune echeance n'y change rien.
|
||
2. ⚠️ **Le bilan ne connait pas les heures creuses.** `grep` sur les douze
|
||
fichiers de `src/energy-balance/` : zero occurrence de `hc_night` /
|
||
`hc_midday`. Sans garde de temps, une cible manuelle a 85 % fuit dans la
|
||
journee et mord sur `BATTERY_CLOUD_LOCKS.js:451`,
|
||
`eveningSocStartFloorPct = max(eveningSocStartMin, balanceRequiredSocPct)` —
|
||
la voiture ne demarrerait plus le soir. Ce n'est pas theorique, c'est une
|
||
ligne qui s'applique tous les soirs.
|
||
**Solution retenue : l'echeance, pas les bornes.** Meme mecanique que
|
||
l'expiration de `force_grid_now` (§72), deja eprouvee en production. Et
|
||
l'argument architectural : le bilan repond a « de combien la batterie a-t-elle
|
||
besoin », pas a « quand est-ce bon marche » ; lui apprendre les fenetres
|
||
tarifaires couplerait deux preoccupations aujourd'hui propres, et ajouterait
|
||
un endroit de plus a penser le jour ou une borne bouge.
|
||
3. **Rien d'autre n'est touche.** Le bloc « prochaine fenetre de deficit »
|
||
n'appelle pas `coverage()` (il utilise `socForEnergy` directement, l.287), et
|
||
un plancher a 85 reste loin sous l'ecretage a 100, donc aucune interaction
|
||
avec le signal de sous-dimensionnement de §26.
|
||
|
||
### La decision qui reste au proprietaire
|
||
Contraindre le bilan (piste 3) ou l'ignorer (piste 1, un `sourceMode` de plus
|
||
dans le controleur). **Ce n'est pas un choix de technique mais de sens** :
|
||
contraindre garde un seul arbitre, ignorer en cree un second — et ce projet a
|
||
deja paye cher les automates concurrents (charges programmees de l'ESS §26,
|
||
consigne a 4200 W §27, plancher SOC de l'ESS §27).
|
||
|
||
Reste aussi a construire, apres sa reconfiguration du LVHUB : le resume de
|
||
l'etat des cellules cote Home Assistant, et le bouton lui-meme.
|
||
|
||
---
|
||
*Document mis a jour le : 18/09/2026 (~03:15) — releve par cellule avec le courant, conception de la charge HC manuelle arretee et non ecrite*
|
||
|
||
## 74. Session du 20/09/2026 — Nuit LVHUB, correctifs swap rapide, et re-mesure du parc assaini
|
||
|
||
### La nuit LVHUB (18-19/09) : le detail batterie est mort depuis le 15/09, pas a cause des packs
|
||
Detail complet dans `docs/PROTOCOLE_LVHUB_CERBO.md`. En resume : le proprietaire
|
||
a voulu passer le pack US2000C en maitre a la place du pack US5000. **Six
|
||
leviers essayes, aucun ne ramene les trames etendues** du Cerbo (nombre de
|
||
modules, capacite, tensions de cellule, firmware) : rescan, redemarrage Cerbo,
|
||
MAJ firmware Cerbo, inversion du maitre, reset hub, sequence complete rejouee.
|
||
Les trames de BASE passent (SOC, limites 250/250 A, alarmes → le BMS protege) ;
|
||
seules les ETENDUES sont `null`, et ce **depuis le redemarrage HA du 15/09
|
||
05:09** (§69), donc AVANT toute manip de packs. Cause = la couche logicielle
|
||
Venus `can-bus-bms v0.71`, pas le materiel ni l'ordre des packs. **Ne plus
|
||
toucher au materiel batterie pour ce symptome — demontre inutile.** A instruire
|
||
a froid : firmware du LV-Hub, ou effet de la MAJ Venus, sur doc constructeur.
|
||
Config a garder : US5000 maitre → cable bleu → hub → Cerbo, cable gris entre
|
||
packs (seule combinaison qui donne les 3 bips). Le chainage inter-groupes ne
|
||
marche que dans le sens US5000-tete.
|
||
|
||
### Incident du swap rapide (19/09) et les correctifs — P11-bis, bouton reset, alerte
|
||
VW debranchee ~14:50, JEEP branchee, mais le swap n'a dure que **14 s** → la
|
||
borne a GELE son etat (charging_time reste a 1h25, jamais reinitialise). P11
|
||
(base uniquement sur charging_time ≤ 90s) restait aveugle → le collant
|
||
"vehicule plein" de la VW a bloque la JEEP, qui n'a pas charge de l'apres-midi.
|
||
Un debranchement FRANC de 30s a tout debloque.
|
||
|
||
Correctifs deployes (golden 113 → 116) :
|
||
- **P11-bis** : `sessionFresh` gagne un SECOND signal, l'age du dernier
|
||
(re)branchement (`as_timestamp` du last_changed de vehicle_connected),
|
||
independant du charging_time de la borne. Le swap de 14s avait bien fait
|
||
bouger ce last_changed. Meme fenetre 90s. Gabarit + INPUT_PARSE
|
||
(`evcsVehicleConnectedAgeSec`) + WINDOWS.
|
||
- **Bouton reset borne** (`input_boolean.evcs_reset_borne`, carte EVCS Master) :
|
||
echappatoire manuelle, force une session fraiche un cycle, momentane (le
|
||
controleur le rebascule off).
|
||
- **Detection anomalie "branchee mais ne charge pas"** (historiseur) : helper
|
||
`input_text.evcs_alerte_charge` (SPARK/OpenClaw envoie le WhatsApp), toggle
|
||
maitre `input_boolean.evcs_alerte_charge_enabled` (OFF, opt-in, seuils
|
||
500W/10min A CALIBRER). Critere robuste par la censure sans injection.
|
||
- Cote OpenClaw (SPARK) : le concierge ecrit desormais AUSSI le selecteur
|
||
`input_select.victron_evcs_vehicle` sur confirmation WhatsApp (plus besoin de
|
||
le regler a la main), + message spontane utilisable (une reponse VW/JEEP sans
|
||
question posee est appliquee) + menage gateway (la saturation qui avalait les
|
||
messages).
|
||
|
||
### ⚠️ RE-MESURE DE LA COURBE BATTERIE SUR LE PARC ASSAINI (le point majeur)
|
||
Le parc compte maintenant **13 equivalents US2000 sains** : 4 US5000 (pack 1,
|
||
neufs 03/2026) + 5 US2000C (pack 2, dont l'ancien module sain `…0323`). Les
|
||
modules abimes (cellules 12 et 15 mortes) sont sortis. Cellules du pack 2
|
||
mesurees a **3-6 mV d'ecart** (contre 168 mV sur l'ancien parc), SOH 99.
|
||
|
||
`node tools/mesure-courbe-batterie.mjs 8` sur ce parc donne une courbe
|
||
**PLATE a 0,30 kWh/point de 25 a 97 %, PLUS DE GENOU**. Le genou a 33 % (§41)
|
||
etait la signature de la cellule 12 morte : il a disparu avec elle. La reserve
|
||
utile a quasiment **double : 12,4 → ~23 kWh** (100 → 22 %).
|
||
|
||
Recalage deploye (rec 1) :
|
||
- `src/energy-balance/BALANCE.js` : `kneePct` 33 → 0, pentes 0,169/0,097 →
|
||
**0,1615 plat** (× echelleParc 13/7 = 0,30 au parc actuel).
|
||
- `src/nodes/INPUT_PARSE.js` : `batteryUsableCapacityKwh` 2,414 → **2,308
|
||
kWh/module** (0,30/13 × 100).
|
||
- helper `evcs_balance_usable_capacity_kwh` 15 → 23 (inerte mais coherent).
|
||
- Tests bilan 50/50 (10 attendus re-figes, verifies a la main), golden 116/116,
|
||
golden-acm 20/20. Mutation deux sens sur la courbe.
|
||
|
||
⚠️ **La courbe est un RESULTAT DE MESURE, a remesurer apres toute evolution du
|
||
parc.** Les constantes 0,1615 et 2,308 valent pour 13 modules ; le scaling
|
||
`modules/7` (helper `evcs_balance_modules_en_service` = 13) adapte au nombre.
|
||
|
||
### Rec 2 : top-off ACM 52,4 → 52,8 V
|
||
Le 52,4 V avait ete pose le 06/09 pour menager la cellule 12 faible du pack
|
||
ancien. Ce pack est sorti ; on releve le top-off pour atteindre une charge plus
|
||
pleine et **recalibrer la jauge SOC Pylontech** (qui derive tant qu'on n'atteint
|
||
jamais 100 %). 52,8 V = 3,52 V/cellule, sous les 53,2 V autorises par le BMS, et
|
||
la cellule haute du pack 2 est a 3356 mV. patch rejouable
|
||
`tools/patch-20260920-topoff-52v8.mjs`.
|
||
|
||
### ⚠️ ANGLE MORT A LEVER : cellules du pack 1 (US5000) hors surveillance
|
||
Le releve serie continu est sur le pack 2 (US2000C) ; les trames etendues du
|
||
Cerbo sont mortes (voir ci-dessus). Les cellules du **pack 1 (US5000) ne sont
|
||
donc PAS surveillees en continu**. Lues saines le 19/09 (`pwr` : Vlow 3330 /
|
||
Vhigh 3331, ~1-2 mV), et les US5000 sont neufs. Le proprietaire a commande un
|
||
**2e cable console** pour surveiller les deux packs. La re-mesure de courbe et
|
||
la capacite, elles, couvrent BIEN le parc entier (agregat SOC/puissance du
|
||
Cerbo). Seule la sante CELLULE du pack 1 manque — d'ou la prudence sur le
|
||
top-off, applique sur autorisation explicite du proprietaire.
|
||
|
||
### Production vs consommation (7 jours, 13-19/09)
|
||
Maison a ~40 kWh/jour, conso qui DEPASSE souvent la production (15/09 : 29 vs
|
||
39 ; 17/09 : 27 vs 42). Deja tendu en septembre ; l'hiver sera nettement
|
||
deficitaire. (PV mesuree = censuree sans injection, ressource reelle plus haute
|
||
les jours ou la batterie s'est remplie tot.)
|
||
|
||
---
|
||
*Document mis a jour le : 20/09/2026 — nuit LVHUB (trames etendues mortes cote Venus), P11-bis + bouton reset + alerte, courbe batterie remesuree (plate 0,30, ~23 kWh utiles), top-off 52,8 V*
|
||
|
||
## 75. Session du 24/09/2026 — Le releve des cellules migre du portable vers le Synology .17
|
||
|
||
### Le declencheur
|
||
Le releve serie des cellules Pylontech tournait sur le PC PORTABLE (boucle
|
||
`deploy/releve-cellules/lancer.sh`, @reboot crontab). Il est mort le **20/09 a
|
||
17:29** : le portable a redemarre a 18:13, son garde anti-doublon a vu un
|
||
`pgrep` transitoire et a refuse de relancer -> **4 jours de trou** dans la table
|
||
`cellule`, Grafana « No data » (§74 finissait juste avant ce constat). Le
|
||
portable n'a pas vocation a rester allume ; le proprietaire a propose d'utiliser
|
||
le **.17** (ARCHIVE3, DS920+), toujours allume, avec le cable FTDI sur un port
|
||
USB du NAS.
|
||
|
||
### Ce qui a rendu la migration possible (verifie, pas suppose)
|
||
- DSM embarque les pilotes `usbserial.ko` + `ftdi_sio.ko` (presents dans
|
||
`/lib/modules/`), simplement pas charges d'office. **Verrou decisif leve.**
|
||
- Le .17 monte deja le depot en NFS : `10.10.10.22:/volume2/projects` ->
|
||
`/volume1/homes/TMZF5455/projects`. Le conteneur ecrit donc les CSV dans le
|
||
meme `docs/mesures/batteries/` que lit le service d'historisation du **.22**.
|
||
**Un seul maillon bouge : le lecteur serie.** Toute la chaine aval est
|
||
inchangee.
|
||
- ⚠️ Le montage NFS est **`noexec`** : node LIT le .mjs (OK), mais un script ne
|
||
peut pas etre EXECUTE directement depuis ce montage.
|
||
|
||
### Ce qui a ete construit (`deploy/releve-cellules/`)
|
||
- **`compose.yaml`** : conteneur `node:20-bookworm-slim`, `restart:
|
||
unless-stopped`, device `/dev/ttyUSB0` mappe, depot monte en `/depot`. Tourne
|
||
en **`user: 1026:100`** (PAS root : le NFS peut ecraser root via root_squash ;
|
||
le dossier CSV est g+w pour `users`). TMZF5455 est dans le groupe `docker` ->
|
||
compose sans sudo. Remplace la boucle `lancer.sh`.
|
||
- **`charger-pilotes.sh`** : charge les modules (verifie via `lsmod`, ne fait PAS
|
||
confiance au code retour de `modprobe`) et `chmod 666 /dev/ttyUSB0` (le
|
||
conteneur n'est pas root). A lancer au boot en **root**.
|
||
- **`README.md`** : deploiement, bascule depuis le portable, verification.
|
||
|
||
### Garde anti-blocage ajoute a `pylontech-console.mjs` (--suivi)
|
||
Cause racine de la panne du 20/09 identifiee : **`lireJusqua` resout au timeout
|
||
au lieu de rejeter**, donc un port fige renvoie du vide en boucle sans jamais
|
||
crasher — `restart: unless-stopped` ne l'aurait pas rattrape. Un controle du
|
||
`mtime` du CSV force `process.exit(1)` apres 3x le pas sans ecriture, pour que
|
||
le superviseur (conteneur ou lancer.sh) rouvre le port. Protege les deux hotes.
|
||
|
||
### L'EPREUVE DU FEU : reboot + saut majeur DSM 7.3.2 -> 7.4.1 le meme coup
|
||
Le proprietaire a tout tente d'un coup. **Le premier boot a echoue**, revelant
|
||
deux pieges que l'update majeur cree — desormais corriges et **documentes dans
|
||
le README** :
|
||
|
||
1. **`noexec`** : la tache DSM appelait le CHEMIN NU du script -> « Permission
|
||
denied ». Corrige : commande **`sh <chemin>`** (lit au lieu d'executer).
|
||
2. **L'update majeur EFFACE le sudo passwordless** : DSM 7.4.1 a supprime le
|
||
NOPASSWD de TMZF5455, donc l'auto-elevation `sudo -n` du script est morte.
|
||
Parade durable : la **tache de boot tourne en root** (aucun sudo requis). Un
|
||
update majeur peut aussi DESACTIVER les taches declenchees — a reverifier.
|
||
|
||
Autres faits etablis : le noyau est **inchange** (4.4.302+ en 7.4.1, donc pas de
|
||
souci de vermagic) ; les `.ko` existent toujours ; le cable FTDI est bien
|
||
enumere (`0403:6001 FT232R USB UART`). `insmod` en root fonctionne sans erreur.
|
||
|
||
### Preuve du dernier maillon (reboot propre, ZERO intervention)
|
||
Second reboot, sans rien charger a la main :
|
||
```
|
||
uptime 14 min · /dev/ttyUSB0 cree a 14:14:11 en crw-rw-rw- (666)
|
||
conteneur Up · base .22 ingere les 5 modules (dernier point 14:24)
|
||
```
|
||
Le **666** est la preuve decisive : seul `charger-pilotes.sh` fait ce chmod, et
|
||
il n'a pas ete lance a la main -> la tache de boot (root, `sh`) s'est executee
|
||
seule. Chaine complete automatique validee.
|
||
|
||
### Config finale de la tache DSM (a ne pas casser)
|
||
Planificateur de taches DSM, tache declenchee **« Au demarrage »**,
|
||
**utilisateur = root**, commande :
|
||
```
|
||
sh /volume1/homes/TMZF5455/projects/EVCS/EVCS/deploy/releve-cellules/charger-pilotes.sh
|
||
```
|
||
Le `@reboot lancer.sh` du portable est **retire** de son crontab (remplace par
|
||
une note) : ne jamais le reactiver, il ferait un second lecteur sur la meme
|
||
chaine (relevés faux, silencieux).
|
||
|
||
### Limite inchangee
|
||
Le cable est sur le **pack 2** (5x US2000C) ; le **pack 1** (US5000) reste hors
|
||
surveillance cellule jusqu'au 2e cable en commande. La courbe de capacite et le
|
||
SOC couvrent tout le parc (agregat Cerbo).
|
||
|
||
---
|
||
*Document mis a jour le : 24/09/2026 — releve migre sur le Synology .17 (conteneur + tache boot root/sh), survie prouvee sur reboot + update DSM 7.4.1*
|
||
|
||
## 76. Session du 24/09/2026 — Le rapport de nuit extrapolait l'import sur les heures futures
|
||
|
||
### Le bug (signale par la session SPARK via les compteurs d'energie)
|
||
Rapport du 24/09 : « Au total 4,2 kilowattheures importes » pour la nuit, contre
|
||
**~1,6 kWh** vus par deux compteurs d'ENERGIE independants (le compteur EDF de
|
||
facturation `edf_general_ok_energie_totale` : 1,53 ; les compteurs AC-in Victron
|
||
`airtria_mpii_v1_energy_from_ac_in_1_to_*` : 1,60). Deux compteurs concordent :
|
||
c'est le 4,2 qui est faux.
|
||
|
||
### La cause — et ce n'etait PAS celle proposee d'abord
|
||
SPARK diagnostiquait une surestimation par la cadence d'echantillonnage
|
||
(`sum/60000` supposant 1 point/min alors qu'il y en aurait 2,7). **Faux, et
|
||
verifie en base** : le SQL ne sommait pas les points bruts, il construisait une
|
||
grille `generate_series` a la minute + derniere valeur connue, donc 1 point/min
|
||
par construction. Donnees d'ailleurs eparses : **130 points sur 961 minutes**.
|
||
Le 2,7 (= 4,2/1,53) etait une coincidence numerique, pas un mecanisme.
|
||
|
||
La vraie cause : **le rapport tourne a 06:47 mais sa fenetre va jusqu'a 10:00**.
|
||
Le `generate_series` remplit les ~3 h futures, et la derniere puissance connue
|
||
(le reseau qui importait encore a 06:47) est reportee sur 193 minutes fantomes.
|
||
Preuve : rejeu de l'ancienne methode tronquee a 06:47 -> **3,79 kWh**, cohcerent
|
||
avec le 4,2 ; la meme methode sur la fenetre COMPLETE (donnees presentes cet
|
||
apres-midi) redonne **1,60**. Elle n'etait donc fausse QUE parce qu'elle tournait
|
||
avant la cloture de sa propre fenetre.
|
||
|
||
⚠️ Lecon de methode : cette cause ne se trouve qu'en REJOUANT la requete sur les
|
||
donnees, pas en raisonnant sur un ratio. J'ai rejoue plutot que de croire le
|
||
diagnostic du ratio sur parole — c'est ce qui a evite de « corriger » la mauvaise
|
||
chose.
|
||
|
||
### Le correctif (deploye 24/09, `ec5f43e`)
|
||
`tools/rapport-nuit.mjs` : integration **ponderee par l'intervalle reel** entre
|
||
points (`lead(ts)`, cap 300 s pour qu'un trou n'extrapole rien ; aucun point
|
||
apres le dernier echantillon donc aucune extrapolation) **et fenetre plafonnee a
|
||
`now()`**. Applique aux QUATRE grandeurs integrees (importe, injecte, voiture,
|
||
batterie_chargee) qui partageaient la formule. Verifie : **1,60 kWh** sur fenetre
|
||
complete (= les compteurs), **1,19** en simulation 06:47 (import reel jusque-la).
|
||
|
||
⚠️ **Option ecartee** : lire le delta d'un compteur d'ENERGIE cumule (le plus
|
||
robuste en principe) est **inapplicable ici** — ces compteurs
|
||
(`edf_general_ok_energie_totale`, AC-in Victron) ne sont PAS dans la table
|
||
PostgreSQL `mesure` (seul `sensor.victron_grid_power_l1` y est) ; SPARK les lisait
|
||
direct sur HA (recorder SQLite). Si un jour on veut la methode compteur, il faut
|
||
d'abord ajouter ces entites a l'historisation.
|
||
|
||
⚠️ **Portee** : tous les rapports depuis le 08/09 surestimaient ces kWh (ils
|
||
tournent toujours avant 10 h). Le RESTE du rapport (SOC, heures de fermeture,
|
||
vehicule, injection) etait juste — ces faits ne sont pas integres.
|
||
|
||
### Decalage de l'heure d'envoi 06:47 -> 07:30 (proposition du proprietaire)
|
||
Le fix stoppe la SURestimation, mais a 06:47 le rapport reste INCOMPLET : il rate
|
||
l'import 06:47->07:24. Raisonnement du proprietaire : la fenetre HC ferme a 07:24,
|
||
et si l'ajustement de la cible du matin fait son travail il n'y a **pas** d'autre
|
||
fermeture EDF ensuite — donc un rapport apres 07:24 capture l'import COMPLET de la
|
||
nuit. `RAPPORT_HEURE/MINUTE` passe a **07:30** (`historise-evcs.mjs`).
|
||
|
||
Reserve consignee : les jours ou la cible du matin est trop basse, l'hysteresis
|
||
peut refermer EDF apres 07:24 (cas plein tarif §39) et un rapport a 07:30 le
|
||
raterait. C'est l'exception ; 07:30 est strictement meilleur que 06:47 (toujours
|
||
incomplet). La fenetre de calcul restant plafonnee a `now()`, l'heure d'envoi peut
|
||
etre avancee/reculee sans risque de fantome.
|
||
|
||
---
|
||
*Document mis a jour le : 24/09/2026 — rapport de nuit : integration ponderee + fenetre plafonnee (fin de l'extrapolation), envoi decale a 07:30 pour un total complet*
|
||
|
||
## 77. Session du 24/09/2026 — Banc de test PV pour l'agent vocal OpenClaw
|
||
|
||
### Contexte
|
||
Le proprietaire pilote OpenClaw en vocal depuis son iPhone et veut le faire
|
||
travailler la gestion PV. Coordination avec la session SPARK (qui tient
|
||
OpenClaw) : moi la source de verite et les attendus (j'ai PostgreSQL/InfluxDB et
|
||
l'historique HA), SPARK le harnais et la fiche de l'agent. Deux etages, comme
|
||
pour le concierge (§65/§66) : je ne touche pas a OpenClaw, SPARK ne fabrique pas
|
||
d'attendu.
|
||
|
||
### La source de verite d'abord (consignee MANUEL §0)
|
||
Production PV reelle du jour = SOMME des trois `*_yield_today` par chargeur
|
||
(airtria_mppt1 + mppt_450_200_hq2449yr9pt + v1), remis a zero chaque nuit. Verifie
|
||
le 24/09 (45,1 kWh) et recoupe par l'integrale de `victron_pv_power_system`
|
||
(44,0). Trois pieges ecartes : `energy_production_today` = PREVISION (Forecast.
|
||
Solar), `gx_device_pv_energy` = compteur PARTIEL et gelable, `*_d_hier` = la
|
||
veille.
|
||
|
||
### Le banc — 9 gammes, snapshots reels figes + attendus
|
||
Modele : `campagne-conseiller.mjs` (§63). Chaque gamme = fiche de l'agent +
|
||
snapshot capteurs fige + question, note sur >=3 passages. Snapshots batis sur
|
||
mes chiffres reels (jamais inventes), tires de la table `mesure` (puissances,
|
||
SOC, courant, import) et de l'historique HA (`yield_today`, modes MPPT,
|
||
prevision). Les 4 competences :
|
||
- **Lire la bonne source** : G1 production du jour (45,1 = somme yields, pas la
|
||
prevision) · G2 reste a produire (26,8 = `energy_production_today_remaining`,
|
||
le SEUL cas ou la prevision est juste).
|
||
- **Discriminer** : G3 bridage batterie pleine (SOC 97, +1 A, 7 MPPT
|
||
`voltage_current_limited`, import nul -> ecretage normal) · G4 vraie baisse
|
||
(PV quasi identique a G3 mais SOC 69, +25 A, 7 MPPT `mppt_active` -> ressource
|
||
limitee, rien de gache) · G5 substitution anormale (MPPT bridés MAIS import ~
|
||
conso -> le reseau remplace le solaire, SIGNALER).
|
||
- **Decider** : G6 charge voiture refusee sur excedent negatif (-1854 W,
|
||
batterie en decharge) · G7 reste-t-il du solaire a capter (un MPPT limited ->
|
||
oui, ressource refusee dispo).
|
||
- **Hygiene donnees** : G8 compteur GX gele (last_changed vieux -> ne pas citer)
|
||
· G9 `yield_today` unavailable (production partielle, ne pas sommer un trou).
|
||
|
||
### Ce que le banc a TROUVE (sa raison d'etre)
|
||
**G5 a echoue 0/3 au premier tour.** La regle de substitution etait pourtant
|
||
dans la fiche, mais l'agent concluait « bridage normal » sur batterie pleine +
|
||
MPPT bridés SANS verifier l'import reseau. SPARK a reordonne la fiche pour forcer
|
||
l'import en controle nº1 (« jamais conclure bridage sans avoir verifie que
|
||
l'import ~ 0 ») -> G5 = 3/3, G3/G4 sans regression. C'est le cas qui verrouille
|
||
la distinction bridage/substitution — celle qui a coute des mois d'observation
|
||
au proprietaire (§26-§31). Banc complet : **9/9 sur 3 passages**.
|
||
|
||
### Statut des snapshots (pour un futur re-figeage)
|
||
Reels a figer : G1, G2, G3, G4, G6, G7 (dates/valeurs dans les messages a SPARK
|
||
du 24/09). G5 = valeurs reelles du 27/08 (hors table `mesure`, anterieures au
|
||
04/09) mais comportement CORRIGE depuis le 31/08 -> cas d'apprentissage, ne se
|
||
reproduit plus. G8/G9 = synthetiques d'hygiene. Le banc extrait la fiche live
|
||
d'AGENTS.md d'OpenClaw a chaque run : toute evolution de la fiche est re-testee.
|
||
|
||
### Capteurs de discrimination (rappel, tous lus par l'agent)
|
||
Les 7 `*_operation_mode` (v1, airtria_mppt1, mppt_450_200 device + 4 trackers),
|
||
`victron_battery_soc`, `victron_battery_current` (signe = sens),
|
||
`victron_consumption_power_l1`, `victron_grid_power_l1`, `victron_pv_power_system`.
|
||
Regle-cle : bridage NORMAL ⇒ import nul ; substitution ANORMALE ⇒ import ~ conso.
|
||
|
||
---
|
||
*Document mis a jour le : 24/09/2026 — banc PV 9/9 pour OpenClaw (source de verite + discrimination bridage/vraie baisse/substitution), la source vit dans MANUEL §0*
|