- WINDOWS: dayVehicleFullIdle (statut 3 ou VDF, héritages jour<->nuit), sourceMode blocked même face au solaire - DECISION: branche collante day_vehicle_full_idle au-dessus des gardes - échappatoires force_charge_now/force_grid_now (golden) - golden 33->36, validé live 10:04:30 (latch en 16 s sur la VW pleine) - HANDOFF §22 + MANUEL états collants
1069 lines
63 KiB
Markdown
1069 lines
63 KiB
Markdown
# HANDOFF.md — EVCS / Grid Hysteresis — État au 09/07/2026
|
||
|
||
## 1. Contexte Projet
|
||
|
||
- **Installation** : Victron ESS **sans injection réseau**. Batterie 17,5 kWh utile.
|
||
- **Workflows n8n actifs** :
|
||
- `HA Snapshot EVCS Surplus Controller V2.6` (id `ATswvGfG4KJUls0K`) — pilote la borne EVCS (30 s)
|
||
- `Victron - Battery / Grid Hysteresis Fix V1` (id `7iIlu7LLTW7TZA8o`) — pilote le switch EDF (1 min)
|
||
- **Accès** :
|
||
- HA : `http://10.10.10.17:8123` — jeton dans `jeton_EVCS.txt` (⚠️ en clair, à révoquer si dépôt partagé)
|
||
- n8n : `https://n8n.airtria.pro` — clé API dans `~/.config/opencode/opencode.json` (mcp.n8n-mcp)
|
||
- **MCP opencode** (option 1 déployée) : 5 serveurs distants dans `~/.config/opencode/opencode.json`
|
||
(`ha-get-state`, `ha-set-helper`, `grid-set-state`, `victron-set-charge-limits`, `pv-get-forecast`)
|
||
→ endpoints SSE `https://n8n.airtria.pro/mcp/<path>/sse`.
|
||
Les 4 outils paramétrés utilisent `$fromAI(...)` (corrigé le 09/07).
|
||
⚠️ **Endpoints MCP n8n sans authentification** — à sécuriser (Bearer sur les nodes MCP Server Trigger).
|
||
- **Fichiers de référence locaux** :
|
||
- `WINDOWS/WINDOW_SOURCEV2.js`, `BATTERY/BATTERY_CLOUD_LOCKS.js`, `HYSTERESIS/COMPUTE.js` — copies de lecture (le code qui fait foi est dans n8n)
|
||
- `modified/GRID_HYSTERESIS_backup_20260709-*.json` — rollback avant P1
|
||
- `modified/EVCS_V2.6_backup_20260709-*.json` — rollbacks avant P2 puis P3 (2 fichiers)
|
||
|
||
## 2. Architecture Data-Flow
|
||
|
||
```
|
||
EVCS V2.6 (30s) :
|
||
HA Snapshot (template) → INPUT_PARSE → WINDOWS_SOURCEV2
|
||
→ BATTERY_CLOUD_LOCKS → DECISION_ENGINEV2 → HA_SERVICE_CALLS_OUTPUT
|
||
→ Build Actions → Execute HA Service
|
||
|
||
Grid Hysteresis (1min) :
|
||
HA Snapshot → Compute Grid Hysteresis → Build Actions → Execute HA Service
|
||
```
|
||
|
||
Communication inter-workflows via helpers HA :
|
||
`input_text.victron_evcs_source_mode` (labels FR), `input_boolean.victron_evcs_night_grid_takeover_needed`,
|
||
`input_text.victron_evcs_surplus_state`, `input_select.victron_evcs_strategy`.
|
||
|
||
## 3. Patchs déployés le 09/07/2026 (session opencode)
|
||
|
||
### P1 — Grid Hysteresis (déployé, vérifié)
|
||
1. **Entité fantôme corrigée** : le snapshot lisait `input_select.victron_evcs_user_strategy`
|
||
(inexistante → toujours `unknown`). Corrigé en `input_select.victron_evcs_strategy`.
|
||
Conséquence avant fix : les overrides `force_grid_now`/`force_charge_now` et la coupure
|
||
« voiture pleine » ne fonctionnaient JAMAIS dans l'hystérésis.
|
||
2. **Switch EDF `unavailable`** : nouvel état `switch_unavailable` = aucun ordre, aucun timestamp,
|
||
réévaluation complète au retour du switch. Avant : spam `turn_on` chaque minute + rallumage
|
||
aveugle à la reconnexion (incident du 08-09/07 : disjoncteur physique coupé par l'utilisateur,
|
||
EDF rallumé à 00:41 au retour, batterie chargée sur réseau à SOC 48 %).
|
||
3. **`last_on_at`** : réécrit uniquement sur vraie transition (latch OFF→ON), plus à chaque retry.
|
||
Avant : `hold_on_min_runtime` pouvait durer éternellement.
|
||
4. **Bug latent corrigé** : le bloc `turn_off_force_grid_vehicle_full` (V2.5) était écrasé par la
|
||
chaîne principale (code mort). Flag `forcedCut` ajouté.
|
||
|
||
### P2 — EVCS V2.6 : continuité takeover nuit / anti-créneaux (déployé, vérifié en réel)
|
||
- Le snapshot relit `night_takeover_prev` (= helper takeover du cycle précédent).
|
||
- `nightTakeoverLatched` dans `WINDOWS_SOURCEV2` **et** `BATTERY_CLOUD_LOCKS` :
|
||
une fois le réseau HC engagé en session nuit, on y reste jusqu'à fin de session
|
||
(fenêtre/débranchement) ou SOC ≥ 90 (`NIGHT_TAKEOVER_RELEASE_SOC_PCT`).
|
||
- Supprime le cycle « château fort » : batterie→jeep→seuil 20 %→EDF ON→30 %→EDF OFF→batterie...
|
||
- Vérifié en production dans la nuit du 09/07 : takeover engagé à SOC 35 %, EDF resté ON,
|
||
Jeep en charge continue (`hold_min_run`).
|
||
|
||
### P3 — EVCS V2.6 : provocation PV batteries pleines (déployé, dry-run complet)
|
||
- Problème (ancien sujet de ce HANDOFF) : sans injection, batteries pleines ⇒ MPPT bridé à la
|
||
conso maison ⇒ aucun seuil PV observable ; en plus `forecastBufferKwh` (10,5 kWh véhicule
|
||
soustraits) toujours ≤ 0 l'après-midi, et `cloudDebt` en deadlock (batterie pleine = courant
|
||
nul = dette jamais remboursée). Sonde afternoon inopérante (`probe_enabled = off` + seuil PV 1000 W).
|
||
- Fix : `pvProvocationEligible = SOC ≥ 90 && batterie non déchargée` → `solar_potential`
|
||
(intégré à `isHighBatteryForPotential`, donc actif sur les 4 profils jour). Gardes aval intactes.
|
||
- **Attention réglage** : `soc_start_min` (HA) = 94 ⇒ plancher de démarrage hors fenêtre.
|
||
Le déclenchement pratique est donc SOC ≥ 94 %. Baisser ce helper à 90 pour aligner.
|
||
|
||
### P4 — Maintien SOC adapté au forecast pendant le takeover nuit (déployé 09/07 après-midi)
|
||
- **Découverte clé** : `Victron - Auto Charge Manager V1.2` (id `cTeeHy4ePiVeELLh`) est le
|
||
propriétaire des limites DVCC (`MaxChargeCurrent`/`MaxChargeVoltage` via MQTT,
|
||
feedback : `sensor.victron_max_charge_current_setting`). Son mode « nuit idle » laissait
|
||
30 A de charge batterie dès que EDF était ON → batterie rechargée à fond pendant un takeover.
|
||
- **ACM patché** : nouvelle branche prioritaire `evcs_takeover_hold` / `evcs_takeover_recover` :
|
||
si `night_grid_takeover_needed` ON + EDF ON → SOC < cible-1 : recharge 30 A ; sinon **maintien
|
||
2 A / 50.1 V** (maison + VE sur HC, batterie en pause). Cible lue depuis
|
||
`input_number.victron_evcs_night_target_soc` (écrite par le workflow EVCS : forecast fort→35 %,
|
||
moyen→55 %, faible→85 % — donc été ~35, hiver ~85 comme demandé).
|
||
- **EVCS patché** : avant minuit (20h→8h), la cible matin utilise
|
||
`max(today_remaining, tomorrow_kwh)` (sinon today_remaining≈0 à 23h → cible 85 % à tort en été).
|
||
Ajout `tomorrow_kwh` au snapshot + `parsed.tomorrowKwh`.
|
||
- Dry-runs : 8 scénarios ACM + 2 scénarios cible EVCS validés (voir session 09/07).
|
||
|
||
### Autres réalisations du 09/07 (après-midi)
|
||
- **Borne fantôme résolue** : firmware EVCS 2.05 → **2.08** (session repartie de zéro,
|
||
détection véhicule correcte). Les 19 kWh affichés étaient un cumul de session non réinitialisé.
|
||
- **Dashboard « EDF Urgence »** créé via API HA (sidebar) : scripts `script.edf_urgence_off`
|
||
(hystérésis OFF + latch OFF + stratégie stop + switch OFF — rien ne rallume),
|
||
`script.edf_force_on`, `script.edf_rendre_la_main` + cartes d'état et « pourquoi ».
|
||
- **Incident Synology** : NAS éteint dans la nuit (cause à chercher dans les logs DSM) →
|
||
n8n + HA morts → EDF resté ON toute la nuit, batterie chargée à 96 % sur réseau.
|
||
⚠️ SPOF structurel : tout le contrôle vit sur le Synology.
|
||
|
||
### P5 — Plafond courant EVCS configurable quand EDF est ON (déployé 09/07 soir)
|
||
- Le plafond 20 A venait de `cfg.evcsDedicatedMaxW` (4900−300 W / 230 V) codé en dur dans INPUT_PARSE.
|
||
- Nouveau helper **`input_number.evcs_plafond_courant_edf_on`** (6–32 A, défaut 20) : quand
|
||
**AC-IN est physiquement présent** (`acin1_available`, pas juste le switch), le plafond = helper ;
|
||
hors réseau, retour au plafond câblé ~20 A. Dry-run 5 scénarios OK (dont disjoncteur coupé).
|
||
- Subtilité : `victron_evcs_current_max_auto_a` (22) plafonne EN PLUS les modes auto ;
|
||
en force_grid/force_charge seul le nouveau helper compte.
|
||
- **Boutons dashboards** (EVCS Master = fichier YAML `/mnt/ARCH3/docker/HA/dashboards/evcs_master.yaml`
|
||
édité via NFS ; EDF Urgence = API) : carte « Plafonds courant EVCS » + boutons
|
||
**⚡ BOOST EDF 28A** (`script.evcs_boost_edf` : plafonds 28/28 + force_grid_now + consigne 28)
|
||
et **Retour normal** (`script.evcs_boost_off` : 20/22 + auto_solar_plus).
|
||
- Sémantique clarifiée : « Courant demandé » = « Consigne envoyée » = `number.victron_evcs_set_current_command`
|
||
(réécrit toutes les 30 s par le workflow) ; « Consigne lue » = readback borne. En force_charge_now,
|
||
PV ≥ 700 W ⇒ source « Solaire » ⇒ gardes nuages/batterie freinent : pour une grosse charge, force_grid_now.
|
||
|
||
### P6 — Override « abondance » du plancher SOC de démarrage (déployé 12/07 midi)
|
||
- **Symptôme** : 12/07 midi, PV 8,9 kW, batterie en charge +125 A, Jeep branchée → EVCS bloqué
|
||
`idle_wait_conditions`. Cause unique (dry-run) : plancher SOC fenêtre midi
|
||
(`input_number.victron_evcs_midday_soc_min` = 75) > SOC 66.
|
||
- **Fix (`BATTERY_CLOUD_LOCKS`)** : si `pvPowerAvg10m ≥ 4000 W` **et** `batteryCurrentA ≥ +20 A`
|
||
(abondance physique : la batterie monte même avec l'EVCS), plancher de démarrage abaissé à
|
||
**40 %** (`effectiveSocStartFloor`). Jours nuageux : planchers d'origine intacts.
|
||
Debug exporté : `solarAbundance`, `effectiveSocStartFloor` (safeguards).
|
||
- Vérifié en réel : déploiement 12:43 → `start_min_current` immédiat → charge 12 A.
|
||
- **Observation borne** : après des heures branchée en statut 6, la Jeep a mis ~2,5 min à
|
||
s'engager ; le workflow renvoie `turn_on` toutes les 30 s (retry intégré suffisant).
|
||
- Planchers de référence : matin 35, midi 75, soir ?, hors fenêtre 94 (`soc_start_min`).
|
||
|
||
### Maintenance du 09/07 soir (analyse des messages d'erreurs)
|
||
- **W-105 borne** (surcharge) : survenu en charge 20 A sur onduleur seul (disjoncteur EDF coupé).
|
||
Déjà couvert : gardes vebus_overload + P5 (plafond retombe à 20 A si AC-IN absent).
|
||
- **`saveDataErrorExecution: all`** activé sur les 8 workflows critiques (diagnostic des
|
||
prochaines erreurs avec données des nodes ; Water Heater V2.3 avait erré 4× le 09/07 20:20-20:35,
|
||
cause inconnue faute de données, rétabli seul).
|
||
- **Garage Thermal** : la prise `switch.clim_hygro_socket_1` est débranchée volontairement par
|
||
moments → l'appel au switch est maintenant conditionné à sa disponibilité (`execute: switchAvailable`).
|
||
Plus de warnings « missing entity » toutes les 10 min. Backup `modified/GARAGE_backup_*`.
|
||
- **Templates MQTT durcis** (33 occurrences, `configuration.yaml` + `packages/victron_vebus_mqtt.yaml`) :
|
||
garde `value_json is defined` → plus d'erreurs `value_json is undefined` aux reconnexions MQTT.
|
||
Backups `SAVE_CONFIG/*.bak.20260709-234800`, `check_config` valid, `mqtt.reload` fait.
|
||
⚠️ Après un `mqtt.reload`, les sensors restent `unknown` jusqu'à republication Venus :
|
||
publier `R/c0619ab34ac5/keepalive` (payload vide) force la republication complète.
|
||
|
||
## 4. Root Cause de l'incident « EDF ON à 48 % sans charge Jeep » (nuit 08→09/07)
|
||
|
||
Chaîne complète reconstituée via l'historique HA :
|
||
1. Borne fantôme au matin du 08/07 (« véhicule en charge » sans véhicule) + `force_grid_now`
|
||
de la veille ⇒ EDF maintenu ON (la coupure voiture-pleine était du code mort, cf. P1.1/P1.4).
|
||
2. L'utilisateur coupe le **disjoncteur physique** ⇒ switch `unavailable` pendant ~5 h.
|
||
3. Le workflow reste coincé en `hold_on_min_runtime` (P1.3) en spammant `turn_on` (P1.2).
|
||
4. 00:40:54 : le switch revient ⇒ 00:41:03 EDF ON sans cause actuelle ⇒ Multiplus charge la
|
||
batterie sur réseau (2980 W) ⇒ coupé à 01:06 (min_on 25 min, SOC ≥ 30).
|
||
Tous les maillons sont corrigés par P1.
|
||
|
||
## 5. Sémantique utile (pour ne pas re-chercher)
|
||
|
||
- **Latch** (`input_boolean.victron_grid_hysteresis_latch`) : « c'est l'automate qui a allumé EDF
|
||
et il défend cet état ». Reprise en main manuelle : stratégie `stop` + hystérésis OFF + latch OFF.
|
||
- **Fenêtres EVCS (helpers HA, corrigées/vérifiées le 09/07)** : `night_hc` **01:24→07:24** (HC réelles),
|
||
`morning_solar` 07:30→11:00, `midday_hc_or_solar` **12:24→14:24** (HC réelles),
|
||
`evening_high_soc` 17:00→20:30, sinon `outside_window`.
|
||
L'Auto Charge Manager utilisait des fenêtres codées en dur légèrement fausses
|
||
(01:30-07:15 / 12:25-14:25) — alignées sur 01:24-07:24 / 12:24-14:24 le 09/07.
|
||
- **Seuils hystérésis réseau (HA, valeurs réelles)** : ON ≤ 20 %, OFF ≥ 30 %, min_on 25 min.
|
||
- **Cibles nuit** : hard floor 22 % ; cible matin selon forecast : ≥18 kWh→35 %, ≥10→55 %, sinon 85 %.
|
||
`nightSessionActive = fenêtre nuit && véhicule branché` (⚠️ pas besoin de charge active).
|
||
- **Statut borne 6** = branchée en attente de démarrage (« Inconnu (6) » sur le dashboard).
|
||
- **Labels FR** dans `source_mode` : `Réseau forcé`, `Réseau HC autorisé`, `Assistance batterie nuit`,
|
||
`Bloqué`, `Solaire`, `Potentiel solaire` — l'hystérésis matche ces labels.
|
||
|
||
## 6. Méthode de travail validée (à réutiliser)
|
||
|
||
1. Backup : `GET /api/v1/workflows/<id>` → `modified/*_backup_<stamp>.json`.
|
||
2. Patch des `jsCode` par remplacement exact (script node), `node --check` sur chaque node.
|
||
3. **Dry-run local de la chaîne complète** : snapshot réel via `POST /api/template` HA,
|
||
exécution séquentielle des 5 nodes avec shim `$input`, horloge simulable en surchargeant
|
||
`global.Date` (voir session du 09/07).
|
||
4. `PUT /api/v1/workflows/<id>` (⚠️ filtrer `settings` : seuls
|
||
`executionOrder/timezone/save*/executionTimeout/errorWorkflow` sont acceptés).
|
||
5. Vérification live : helpers HA 40-70 s après.
|
||
|
||
## 7. Backlog / Point du 10/07 au réveil
|
||
|
||
1. **Vérifier la nuit de test P2+P4** (fenêtre HC 01:24→07:24, forecast fort → cible 35 %) :
|
||
- takeover engagé quand SOC ≤ 35 → EDF ON **continu** (pas de créneaux) ;
|
||
- `input_text.victron_charge_mode` = `evcs_takeover_recover` puis **`evcs_takeover_hold`** ;
|
||
- `sensor.victron_max_charge_current_setting` = **2** pendant le hold (batterie tenue ~35 %) ;
|
||
- 07:24 : fin de fenêtre → EDF coupé (`turn_off_recovered`), ACM repasse en profil jour.
|
||
Historique utile : `switch.edf_general_ok_switch`, `sensor.victron_battery_soc`,
|
||
`input_text.victron_charge_mode` entre 01:00 et 08:00.
|
||
2. **Cause de l'extinction du Synology** (DSM → Centre des journaux) + réflexion SPOF :
|
||
le fail-safe physique actuel est « EDF ON + charge batterie » quand le NAS meurt.
|
||
3. Baisser `input_number.victron_evcs_soc_start_min` 94 → 90 (aligner avec P3) si souhaité.
|
||
4. **Sécuriser** : auth Bearer sur les 5 MCP Server Triggers n8n + révoquer/déplacer le jeton HA.
|
||
5. **À investiguer** : « conditions de démarrage nuit batterie non réunies » quand la batterie
|
||
suffit pour la nuit (conditions de départ `battery_assist_night`).
|
||
6. Optionnel : brancher un keepalive/republish Venus automatique après `mqtt.reload`
|
||
(cf. note maintenance §3).
|
||
|
||
## 8. Historique des sessions
|
||
|
||
- **12/06/2026** : analyse blocage outside_window, déploiement afternoon_probe (V2.6),
|
||
fix clignotement EDF (`edf_general_ok_on` conditionné au sourceMode).
|
||
- **09/07/2026 (nuit)** : incident EDF nocturne analysé (root cause complète), MCP option 1 en place,
|
||
P1 (hystérésis robuste + entité stratégie), P2 (anti-créneaux nuit), P3 (provocation PV).
|
||
- **09/07/2026 (journée/soir)** : incident Synology (NAS éteint → EDF ON toute la nuit, batterie 96 %),
|
||
firmware borne 2.08 (fantôme résolu), dashboard EDF Urgence + boost 28 A, P4 (maintien SOC
|
||
au forecast pendant takeover, via Auto Charge Manager), P5 (plafond EDF ON configurable),
|
||
fenêtres HC corrigées (01:24-07:24 / 12:24-14:24), maintenance erreurs (save error executions,
|
||
prise garage conditionnelle, 33 templates MQTT durcis).
|
||
- **12/07/2026** : P6 (override abondance du plancher SOC midi — PV fort + batterie en charge
|
||
⇒ démarrage dès SOC 40) ; validation en réel à 12:43, charge Jeep engagée.
|
||
- **17/07/2026 (après-midi)** : **Session de débogage majeure** — résolution d'un enchaînement de bugs
|
||
bloquants empêchant la charge solaire de la Jeep :
|
||
|
||
**Bug 1 — Typo critique dans DECISION_ENGINEV2** :
|
||
- Ligne ~333 : `noDemandnightSessionNeedsClearScoreOff` (variable inexistante) au lieu de `noDemandScoreOff`
|
||
- Conséquence : `evcsVehicleNoDemandSemantic` restait `false` même quand la voiture était pleine
|
||
- Le filtre "véhicule plein/pause" ne fonctionnait jamais → blocage du système
|
||
|
||
**Bug 2 — Artefacts de corruption dans WINDOWS_SOURCEV2** :
|
||
- 3 occurrences de `tool_f6d3316d2001k4aAfEjbKGNUX2` (résidu de copier-coller ou debug)
|
||
- Provoquaient des erreurs `is not defined` à l'exécution
|
||
- Workflow en ERROR continu depuis 14:49
|
||
|
||
**Bug 3 — Session nuit jamais clearée** :
|
||
- `input_boolean.victron_evcs_night_session_active` restait `on` depuis 23:24 la veille
|
||
- Même après la fenêtre HC (01:24-07:24), le profil restait `night_hc` → `battery_assist_night`
|
||
- En plein jour avec grand soleil, le système pensait être en "assistance batterie nuit"
|
||
- Clearé manuellement à 14:45
|
||
|
||
**Bug 4 — EDF rebranché automatiquement en journée** :
|
||
- Condition `edf_general_ok_on` déclenchée dès `sourceMode` = `grid_forced` ou `grid_hc_allowed`
|
||
- Même en plein jour avec du soleil, EDF se rebranchait si le workflow passait en ces modes
|
||
- Correction : ajout de `context.profile === 'night_hc'` pour ne rebrancher que la nuit
|
||
|
||
**Configuration ajustée** :
|
||
- `battery_discharge_soft_a` : 0A (au lieu de -8A) — permet le démarrage sans décharge batterie
|
||
- `battery_neutral_band_a` : 0A (au lieu de -2A) — tolérance nulle
|
||
- `soc_start_min` : 80% (au lieu de 94%) — seuil de démarrage plus réaliste
|
||
- `night_target_soc` : 70% (au lieu de 85%) — laisse plus de marge pour le PV
|
||
|
||
**Mode sonde activé** :
|
||
- `probe_enabled` : ON — permet le démarrage à 6A pour "provoquer" la production PV
|
||
- Particulièrement utile en autoconsommation sans injection où le PV ne produit que si chargé
|
||
|
||
**Résultat** :
|
||
- Workflow réparé et fonctionnel
|
||
- Jeep en charge à 17A avec le soleil (16:58)
|
||
- EDF OFF (ne se rebranche que la nuit)
|
||
- Système 100% auto en `auto_solar_plus`
|
||
- DeliveryRatio à 99.9% — charge stable et efficace
|
||
|
||
**À noter** : Le courant de sonde (`probeCurrentA`) est codé en dur dans le workflow n8n (nœud SETUP/PARAMS),
|
||
pas dans un helper HA. Pour le changer de 6A à 10A, modifier directement dans l'interface n8n.
|
||
Mais le workflow augmente automatiquement le courant (17A atteints), donc ce n'est pas critique.
|
||
|
||
## ⚙️ CONFIG LOCALE — Qwen3.6-35B sur DGX Spark (≠ gros modèle cloud)
|
||
|
||
> ⚠️ Cette config est CALIBRÉE pour le modèle LOCAL Qwen3.6-35B-A3B (3B actifs,
|
||
> contexte limité). NE PAS appliquer telle quelle à un gros modèle cloud
|
||
> (FABLE/Claude), qui n'a besoin ni du bornage strict ni du découpage par nœud.
|
||
|
||
### Infra (validée le 16/07/2026)
|
||
- DGX Spark = 10.10.10.8, endpoint vLLM unique sur port 8000.
|
||
- UN SEUL modèle à la fois (contrainte NVRAM 1 Spark) → swap via scripts :
|
||
- `use-code` : docker compose down (VL) + sparkrun run qwen3.6-35b sur :8000
|
||
- `use-vl` : sparkrun stop --all + docker compose up (Qwen3-VL, vision)
|
||
- Recette : @official/qwen3.6-35b-a3b-fp8-mtp-vllm --tp 1 --port 8000
|
||
--gpu-mem 0.8 --served-model-name qwen3.6-35b
|
||
(MTP + flashinfer + tool-parser qwen3_coder déjà inclus dans la recette).
|
||
- Qwen3-VL (docker /opt/vllm) réservé à l'analyse de captures d'écran.
|
||
|
||
### opencode.json (poste Ubuntu, PAS la Spark)
|
||
- baseURL: http://10.10.10.8:8000/v1, apiKey "EMPTY".
|
||
- Modèle "qwen3.6-35b" : options { temperature: 0.15, reasoningEffort: "none" },
|
||
cost tout à 0 (sinon compteur fantôme FABLE), limit context 65536.
|
||
- permission.bash : "curl *": "deny", "wget *": "deny" (bloque hallucinations HTTP).
|
||
- Les 6 MCP (n8n-mcp, ha-get-state, ha-set-helper, grid-set-state,
|
||
victron-set-charge-limits, pv-get-forecast) : NE JAMAIS toucher ce bloc.
|
||
|
||
### RÈGLES DE TRAVAIL spécifiques au local (dans AGENTS.md)
|
||
- Contexte trop petit pour un workflow entier (~357K = ~102K tokens > 65K).
|
||
=> JAMAIS charger le workflow complet. UN nœud / UNE tâche à la fois.
|
||
- n8n-mcp_get_workflow renvoie le workflow entier => opencode l'écrit dans
|
||
~/.local/share/opencode/tool-output/tool_* (tronqué). Le fichier commence par
|
||
"Retrieved workflow: <nom>" AVANT le JSON => json.load ÉCHOUE.
|
||
Extraction validée : grep -n '"name":"NOEUD"' <fichier> puis sed -n 'X,Yp'.
|
||
(ripgrep/rg N'EST PAS installé sur la Spark, utiliser grep.)
|
||
- reasoningEffort none coupe le thinking (le tag /no_think en tête de chat NE
|
||
marche pas : opencode l'intercepte comme slash-command).
|
||
- Skill n8n installée dans .opencode/skills/ — surveiller les compactions.
|
||
|
||
### PÉRIMÈTRE (le plus important)
|
||
- LOCAL Qwen3.6 = tâches CIBLÉES : lire 1 nœud, repérer une faille logique,
|
||
patch simple, dry-run. Rapide, gratuit, fiable SI bien borné.
|
||
- CLOUD (FABLE/gros modèle) = refontes multi-nœuds, analyse d'un workflow
|
||
ENTIER, orchestration MCP longue. Ne pas forcer le local dessus (il boucle).
|
||
|
||
## 🧩 SKILLS n8n — validées le 16/07/2026 (Qwen3.6 local, PAS gros modèle)
|
||
|
||
> ⚠️ Config calibrée pour Qwen3.6-35B-A3B local (contexte 65K). Les skills
|
||
> aident au tool-calling n8n. Sur gros modèle cloud, elles sont facultatives.
|
||
|
||
### Installation
|
||
- Dépôt : github.com/viliamvolosv/n8n-opencode-skill
|
||
- Emplacement OBLIGATOIRE : .opencode/skills/<nom-skill>/SKILL.md
|
||
(dossier "skills" PLURIEL ; le repo clone dans un sous-dossier "skill"
|
||
SINGULIER → il FAUT remonter : mv n8n-opencode-skill/skill/* .)
|
||
- opencode doit être lancé DEPUIS le projet :
|
||
cd /mnt/projects/EVCS/EVCS && opencode
|
||
(sinon .opencode/skills/ n'est pas découvert → skills invisibles).
|
||
- Chargement PROGRESSIF : au repos les 8 skills ne coûtent que ~12K tokens
|
||
(18%). Le SKILL.md complet (~16K) ne se charge QUE si la skill est invoquée.
|
||
|
||
### Skills ACTIVES (5, validées par test réel sur DECISION_ENGINEV2)
|
||
- n8n-mcp-tools-expert → récup config nœud via MCP (fiabilise les 6 MCP)
|
||
- n8n-code-javascript → analyse/patch du jsCode (vars in/out, utilitaires)
|
||
- n8n-expression-syntax → vérif syntaxe {{ }}
|
||
- n8n-validation-expert → refs de nœuds, cohérence types, conditions
|
||
- n8n-node-configuration → conformité params/mode/position
|
||
|
||
### Skills DÉSACTIVÉES (déplacées dans .opencode/skills-off/)
|
||
- customize-opencode (méta, hors sujet EVCS)
|
||
- n8n-code-python (on fait du JS)
|
||
- n8n-workflow-patterns (exige le workflow ENTIER → risque contexte)
|
||
→ réactivables en les remettant dans skills/ , ou à réserver au cloud.
|
||
|
||
### Résultat du test de validation (5 skills, DECISION_ENGINEV2)
|
||
- Les 5 skills invoquées et nommées correctement, analyse d'ingénieur juste
|
||
(16 vars, 7 derived, 22 conditions, pipeline complet reconstitué).
|
||
- UNE SEULE compaction (pic 88% au parsing du workflow 357K, reprise propre).
|
||
- 2 AttributeError Python transitoires (structure connexions n8n) → auto-corrigés.
|
||
- Coût : $0.00.
|
||
|
||
### RÈGLE contexte associée (dans AGENTS.md)
|
||
- Analyse pipeline multi-nœuds : grep -n "name"/"jsCode" + sed sur la plage,
|
||
JAMAIS le workflow JSON entier en mémoire Python. Traite 1 nœud, résume, libère.
|
||
- Le fichier tool-output commence par "Retrieved workflow: <nom>" AVANT le JSON
|
||
→ parade validée : json.loads(text[text.index('{'):]).
|
||
|
||
### PÉRIMÈTRE (rappel)
|
||
- Local Qwen3.6 + skills = analyse ciblée 1 nœud, validation, patch simple. ✅
|
||
- Cloud (FABLE/gros modèle) = refonte multi-nœuds massive, workflow entier.
|
||
|
||
|
||
## 9. Session du 18/07/2026 (matin)
|
||
|
||
### Corrections documentation (MANUEL_COMPLET.md)
|
||
- **outside_window** : décrit comme "Mode probe (sonde) pour relancer le PV si besoin" (pas bloquant)
|
||
- **Cloud Debt & Probe** : nouvelle section 11.4 complète avec explication dette nuageuse, mode probe 10A, bypass en auto_solar_plus / force_charge_now
|
||
- **Stratégies** : clarifié `force_grid_now` = préservation batterie (SOC<22% → recover 35%/50%/75%) vs `force_charge_now` = secours manuel
|
||
- **Heure de départ** : nouvelle section 11.5 "Logique Heure de départ (Night EVCS)"
|
||
|
||
### Modification workflow
|
||
- **probeCurrentA** : augmenté de 6A à 10A dans le nœud INPUT_PARSE (fallback)
|
||
- Rationale : 6A trop faible pour "provoquer" la production PV, 10A plus efficace
|
||
|
||
### Versionnage Git
|
||
- Export du workflow dans `workflows/evcs-surplus-controller-v2.6.json`
|
||
- Commit & push effectués
|
||
|
||
## 10. Session du 04/08/2026 (matin) — Fix grid HC 1h30 + libération takeover
|
||
|
||
### Problèmes诊断qués
|
||
1. **Charge HC ne démarre pas à 1h30** : Le grid ne se mettait pas en route à l'ouverture de la fenêtre HC
|
||
2. **Grid ne rend pas la main après charge** : EDF restait ON après la fin de charge (SOC 71% < 90%)
|
||
|
||
### Root Causes identifiées
|
||
1. **`gridAllowedBySoc`** (BATTERY_CLOUD_LOCKS) : autorise le grid seulement si `soc <= 35%` ou `forceGridRequested`. En profile `night_hc` avec SOC > 35%, le grid était bloqué.
|
||
2. **`nightTakeoverLatched`** (WINDOWS_SOURCEV2) : reste latché tant que `soc < 90%` (hardcodé). Pas de condition "véhicule chargé" → le takeover ne se libérait jamais.
|
||
|
||
### Corrections appliquées
|
||
|
||
#### Fix 1: `gridAllowedBySoc` — Autoriser le grid en profile night_hc
|
||
**Node:** `BATTERY_CLOUD_LOCKS`
|
||
```javascript
|
||
// AVANT:
|
||
const gridAllowedBySoc = parsed.soc <= 35 || strategy.forceGridRequested;
|
||
|
||
// APRÈS:
|
||
const gridAllowedBySoc =
|
||
strategy.forceGridRequested ||
|
||
context.profile === 'night_hc' ||
|
||
parsed.soc <= 35;
|
||
```
|
||
|
||
#### Fix 2: `nightTakeoverLatched` — Libérer si charge terminée
|
||
**Node:** `WINDOWS_SOURCEV2`
|
||
```javascript
|
||
// AVANT:
|
||
const nightTakeoverLatched =
|
||
nightSessionActive &&
|
||
nightTakeoverPrev &&
|
||
parsed.soc < NIGHT_TAKEOVER_RELEASE_SOC_PCT;
|
||
|
||
// APRÈS:
|
||
const vehicleChargeCompleted =
|
||
!parsed.evcsChargingSwitchOn &&
|
||
parsed.soc >= 50;
|
||
const nightTakeoverLatched =
|
||
nightSessionActive &&
|
||
nightTakeoverPrev &&
|
||
(
|
||
parsed.soc < NIGHT_TAKEOVER_RELEASE_SOC_PCT ||
|
||
vehicleChargeCompleted
|
||
);
|
||
```
|
||
|
||
### Méthode de déploiement
|
||
- **MCP n8n-mcp_update_workflow** : Erreur 1003 (request/body must NOT have additional properties) — bug du MCP
|
||
- **Bypass** : Script `tools/n8n-rest-update-test.mjs` créé, utilise REST API n8n directement avec token
|
||
- PUT réussi avec body minimal : `{name, nodes, connections, settings, staticData}`
|
||
- Settings doit être minimal : `{"executionOrder":"v1","callerPolicy":"workflowsFromSameOwner"}`
|
||
|
||
### Backup
|
||
- `backup-workflow-ATswvGfG4KJUls0K-1785849621547.json`
|
||
|
||
### Vérification
|
||
- Les 2 patches sont dans le workflow en production
|
||
- À surveiller : prochaine nuit HC (01:24-07:24) pour valider le comportement
|
||
|
||
---
|
||
*Document mis à jour le : 04/08/2026 (~10:00) — Fix gridAllowedBySoc + nightTakeoverLatched via REST API*
|
||
*État live à la clôture : Workflow patché, à tester en conditions réelles*
|
||
|
||
## 11. Session du 07/08/2026 — P7 : Charge HC nuit automatique (VW hybride) + outillage push
|
||
|
||
### Problème (nuits du 05-07/08, observé sur l'historique HA)
|
||
- VW hybride branchée le soir + stratégie `auto_solar_plus` → **aucune charge en HC**.
|
||
01:24 : `idle_wait_conditions` / « Conditions de démarrage nuit batterie non réunies »
|
||
(SOC 53 %, target=trigger=35 %). L'utilisateur devait intervenir manuellement
|
||
(force_charge_now, ou BOOST EDF 28A à 05:50 le 07/08).
|
||
|
||
### Root causes
|
||
1. **`battery_assist_night` ne démarre jamais** : `batteryAssistNightStartOk` exige
|
||
`batteryCurrentA >= profileBatteryDischargeFloorA` (= `battery_discharge_soft_a` = 0 A).
|
||
La nuit, la maison tire sur la batterie (~ −14 A) → condition impossible.
|
||
→ le système attendait que le SOC dérive jusqu'au trigger 35 % (des heures) avant le grid.
|
||
(= backlog §7.5 « conditions de démarrage nuit batterie non réunies », résolu.)
|
||
2. **`allow_grid_hc_night` (toggle « Réseau HC nuit ») était décoratif** : parsé dans
|
||
INPUT_PARSE (`params.allowGridHcNight`) mais utilisé nulle part
|
||
(contrairement à `allowGridHcMidday`).
|
||
|
||
### Fix P7 (déployé 07/08 ~15:45, dry-run 7 scénarios validés)
|
||
Dans **WINDOWS_SOURCEV2** et **BATTERY_CLOUD_LOCKS** (logique nuit dupliquée) :
|
||
```javascript
|
||
const nightGridHcPreferred =
|
||
nightSessionActive && // fenêtre HC nuit + véhicule branché
|
||
params.allowGridHcNight; // toggle "Réseau HC nuit" ON
|
||
```
|
||
- `batteryAssistAllowed` : ajout `&& !nightGridHcPreferred`
|
||
- `nightGridTakeoverNeeded` : ajout `nightGridHcPreferred ||` dans le OR
|
||
|
||
**Effet** : dès l'ouverture de la fenêtre HC (01:24) avec véhicule branché,
|
||
`nightPreferredSourceMode` = `grid_hc_allowed` (ou `grid_forced` si SOC < cible matin)
|
||
→ `edf_general_ok_on` allume EDF → takeover helper ON → ACM `evcs_takeover_hold`
|
||
(batterie tenue 2 A) → `gridStartOk` → `start_grid_hc_min_current` puis step-up.
|
||
- Toggle « Réseau HC nuit » OFF → comportement historique (battery assist) intact.
|
||
- Jour / pas de véhicule / force_charge_now : aucun changement (dry-run vérifié).
|
||
- Garde-fous intacts : hard floor 22 %, recovery urgent, latch anti-créneaux P2, P4/P5.
|
||
|
||
### Outillage (résout le blocage minimax « impossible de pousser dans n8n »)
|
||
- **`tools/n8n-pull.mjs <id> <fichier>`** : GET workflow → JSON local (clé API lue
|
||
automatiquement dans `~/.config/opencode/opencode.json`).
|
||
- **`tools/n8n-push.mjs <fichier> [id]`** : PUT REST (body minimal validé 04/08) avec
|
||
**backup serveur auto pré-push** (`modified/prepush-*.json`) et **`node --check`
|
||
sur chaque jsCode** (abandon si erreur de syntaxe).
|
||
- **`tools/patch-20260807-night-hc-grid.mjs`** : le patch P7 (remplacements exacts, rejoue-able).
|
||
- **`tools/dryrun-20260807-night-hc.mjs`** : harnais dry-run WINDOWS→BCL avec snapshot simulé.
|
||
- Cycle git : commit backup AVANT (`1debfe3`) → patch → push → commit APRÈS.
|
||
Rollback : `git checkout 1debfe3 -- workflows/evcs-surplus-controller-v2.6.json && node tools/n8n-push.mjs workflows/evcs-surplus-controller-v2.6.json`.
|
||
|
||
### À surveiller (prochaine nuit HC 01:24→07:24, VW branchée)
|
||
1. 01:24-01:25 : `source_mode` → `Réseau HC autorisé`, EDF ON, takeover helper ON ;
|
||
2. `start_grid_hc_min_current` → montée en courant (plafond EDF ON = helper 20 A / boost 28 A) ;
|
||
3. ACM en `evcs_takeover_hold` (batterie ~2 A, SOC stable) ;
|
||
4. 07:24 : fin de fenêtre → EDF OFF, retour profil jour.
|
||
|
||
---
|
||
*Document mis à jour le : 07/08/2026 (~15:50) — P7 charge HC nuit auto + tools n8n-pull/n8n-push*
|
||
|
||
## 12. Session du 07/08/2026 (soir) — R1 : Déduplication du moteur nuit
|
||
|
||
### Contexte
|
||
Refactor n°1 du plan de maintenabilité (conseil validé avec l'utilisateur) :
|
||
le moteur nuit complet (~161 lignes : nightSessionActive, batteryAssistAllowed,
|
||
nightGridTakeoverNeeded, nightPreferredSourceMode, cibles/triggers, latch...)
|
||
était calculé DEUX FOIS, dans WINDOWS_SOURCEV2 ET BATTERY_CLOUD_LOCKS.
|
||
Chaque patch nuit devait être appliqué 2× (P2, fix 04/08, P7) → risque de
|
||
divergence silencieuse entre sourceMode (WINDOWS) et conditions (BCL).
|
||
|
||
### Fix R1 (déployé 07/08 ~19:03, A/B validé)
|
||
- **WINDOWS_SOURCEV2 = unique calculateur.** Reprend les 4 éléments que seul BCL
|
||
calculait : `nightSessionStartMs`, `nightSessionNeedsCapture` version stricte
|
||
(critère startMs inclus), `batteryRecoveryAllowedNow`, `batteryTargetReached`.
|
||
Export `nightEvcsPreview` étendu (+ `nightTakeoverLatched`, `nightGridHcPreferred`).
|
||
- **BATTERY_CLOUD_LOCKS consomme** `prev.nightEvcsPreview` (bloc de 161 lignes
|
||
remplacé par ~50 lignes de lecture avec fallbacks sûrs : preview absent
|
||
→ session inactive / 'blocked'). −1,7K chars.
|
||
- Le shape de l'export `nightEvcs` de BCL (consommé par DECISION_ENGINEV2 et
|
||
HA_SERVICE_CALLS_OUTPUT) est strictement inchangé.
|
||
- **RÈGLE DÉSORMAIS : toute évolution de la logique nuit se fait UNIQUEMENT
|
||
dans WINDOWS_SOURCEV2.** BCL ne contient plus aucune logique nuit.
|
||
|
||
### Validation A/B (tools/ab-20260807-r1.mjs)
|
||
- Pipeline complet WINDOWS→BCL exécuté avant (git HEAD) / après (patché) sur les
|
||
7 scénarios golden + 2 cas limites (capture session history vide, clear résiduel).
|
||
- Sorties BCL **strictement identiques** (nightEvcs, conditions, safeguards, context).
|
||
- Fallback preview absent : pas de crash, retombe en 'blocked'.
|
||
- Piège rencontré : Date.now() dans le harnais → faux diffs de quelques ms ;
|
||
corrigé en construisant le snapshot une fois puis clone JSON par exécution.
|
||
|
||
### Outillage ajouté
|
||
- `tools/patch-20260807-r1-dedup-night.mjs` : le refactor (rejouable).
|
||
- `tools/ab-20260807-r1.mjs` : harnais A/B git HEAD vs working tree — réutilisable
|
||
pour tout futur refactor (modèle pour R3, la suite de tests golden).
|
||
|
||
### Prochaines étapes du plan validé
|
||
- R3 : suite de tests golden (~15 scénarios, exécutée avant chaque push).
|
||
- R2 : git source de vérité (src/nodes/*.js + assemblage dans n8n-push.mjs).
|
||
- Puis agent IA planificateur en mode ombre (jamais dans la boucle 30 s).
|
||
|
||
---
|
||
*Document mis à jour le : 07/08/2026 (~19:10) — R1 déployé, comportement inchangé (A/B), production stable*
|
||
|
||
## 13. Session du 07/08/2026 (soir) — R3 : Suite de tests golden
|
||
|
||
### Ce qui existe désormais (`tests/golden/`)
|
||
- **`run.mjs`** : exécute le VRAI code des nodes en chaîne complète
|
||
(INPUT_PARSE → WINDOWS_SOURCEV2 → BATTERY_CLOUD_LOCKS → DECISION_ENGINEV2
|
||
→ HA_SERVICE_CALLS_OUTPUT) dans une sandbox vm avec **horloge simulée**
|
||
(Date épinglée par scénario) sur un **snapshot HA réel** modifié par overrides.
|
||
- **`fixture-snapshot.json`** : snapshot production capturé depuis l'exécution
|
||
n8n 975833 (173 champs, re-capturable via `tools/fetch-snapshot-fixture.mjs`).
|
||
- **`scenarios.mjs`** : 16 scénarios avec attendus FIGÉS après revue manuelle :
|
||
6 nuit HC (P7 ON/OFF, sous-cible, hard floor, sans véhicule, EDF ON→start),
|
||
4 solaire (matin fort, matin nuageux, abondance P6, provocation P3),
|
||
garde SOC midi HC, force_grid, force_charge nuit, stop, overload, débranché.
|
||
|
||
### Usage
|
||
- `node tests/golden/run.mjs` → 16/16 ✅ ou liste des diffs + exit 1
|
||
- `--print` : affiche les résumés obtenus (pour autoriser une évolution)
|
||
- `--json` : sortie machine (utilisée pour figer les attendus)
|
||
- `--only=<motif>` : filtre par id de scénario
|
||
|
||
### Intégration au déploiement (le point clé)
|
||
`tools/n8n-push.mjs` exécute la suite AVANT tout PUT du workflow EVCS :
|
||
**golden rouge = rien n'est poussé**. Échappatoire consciente : `--skip-golden`.
|
||
Évolution volontaire du comportement → mettre à jour les attendus dans
|
||
`scenarios.mjs` DANS LE MÊME COMMIT que le patch.
|
||
|
||
### Validation par mutation
|
||
P7 volontairement cassé (`nightGridHcPreferred = false`) → la suite détecte
|
||
2 échecs avec diffs précis (sourceMode, takeover, EDF) et bloque le push ;
|
||
restauration → 16/16. Le filet fonctionne.
|
||
|
||
### Résumé testé par scénario
|
||
profile, sourceMode, state, decision, desiredCharging, currentA,
|
||
takeoverNeeded, action EDF ON, service helper takeover.
|
||
|
||
### Notes de comportement figées (découvertes en écrivant les attendus)
|
||
- SOC 20 la nuit avec maison sur batterie (−14 A) → `battery_current_hard_stop`
|
||
(bande lt25 : hard −8 A) + demande réseau simultanée. Protection cohérente.
|
||
- `force_charge_now` la nuit (toggle HC ON) → `start_manual_local_min_current`
|
||
ET takeover helper ON (session nuit active) : EDF peut venir en renfort. Voulu.
|
||
- Garde SOC>35 midi : sourceMode `grid_hc_allowed` mais `idle_wait_conditions`
|
||
(le grid HC midi ne s'active que SOC ≤ 35) — comportement épinglé par le test.
|
||
|
||
### Prochaines étapes du plan
|
||
- R2 : git source de vérité (src/nodes/*.js + assemblage dans n8n-push).
|
||
- Agent IA planificateur en mode ombre (après R2).
|
||
- Enrichir la suite au fil des incidents : chaque bug corrigé = +1 scénario.
|
||
|
||
---
|
||
*Document mis à jour le : 07/08/2026 (~19:20) — R3 en place, 16/16, push bloquant*
|
||
|
||
## 14. Session du 07/08/2026 (soir) — P8 : Libération EDF quand le véhicule est plein
|
||
|
||
### Spécification utilisateur
|
||
La nuit : charge dès 01:24 (P7) ✓, puis **quand la charge est finie → EDF OFF,
|
||
sauf si la batterie solaire est sous le seuil** (alors EDF reste pour la recharger).
|
||
|
||
### Bugs découverts en traçant la spec
|
||
1. **Fix 04/08 logiquement inversé** : `(soc < 90 || vehicleChargeCompleted)` GARDAIT
|
||
le latch takeover après la charge au lieu de le libérer. Jamais corrigé en réalité.
|
||
2. **P7 ne relâchait pas** : `nightGridHcPreferred` restait vrai tant que véhicule
|
||
branché → l'EVCS redemandait EDF ON toutes les 30 s jusqu'à 07:24.
|
||
3. **Signal "voiture pleine" instable** : le VehicleDemandFilter retombe à false dès
|
||
que le relais s'ouvre (session désarmée) → impossible de s'appuyer dessus seul.
|
||
|
||
### Solution : mémoire AUTO-ENTRETENUE via l'état surplus (P8)
|
||
Chaîne (nouveau champ snapshot `prev_surplus_state` ← input_text.victron_evcs_surplus_state) :
|
||
```
|
||
charge → VDF détecte voiture pleine → state 'vehicle_full_or_paused'
|
||
cycle N+1 : WINDOWS voit ce state → vehicleChargeCompleted=true
|
||
→ DECISION émet 'night_session_done_idle'
|
||
cycles suivants : 'night_session_done_idle' s'auto-entretient
|
||
fin : débranchement (state vehicle_not_connected) ou fin de fenêtre 07:24
|
||
```
|
||
Effets quand vehicleChargeCompleted :
|
||
- latch libéré (fix inversion 04/08 : `soc < 90 && !completed`)
|
||
- préférence P7 relâchée ; sourceMode nuit → 'blocked' (zéro tentative de charge)
|
||
- SOC > cible matin : takeover helper OFF → hystérésis coupe EDF (turn_off_recovered)
|
||
- SOC ≤ cible : takeover helper ON → hystérésis maintient EDF (override) + ACM
|
||
recharge la batterie à la cible → puis relâche. ⚠️ Le nom 'night_session_done_idle'
|
||
ne doit matcher AUCUN pattern no-demand de l'hystérésis (full_or_paused /
|
||
not_requesting_power / charge_complete / no_demand) — c'est ce qui permet
|
||
à l'override batterie-basse de fonctionner. NE PAS RENOMMER sans vérifier.
|
||
|
||
### Nœuds modifiés (tools/patch-20260807-p8-release-full.mjs, rejouable)
|
||
- Snapshot template : +prev_surplus_state
|
||
- WINDOWS_SOURCEV2 : vehicleChargeCompleted collant, latch corrigé, P7 conditionné,
|
||
preferred 'blocked' si plein, export preview
|
||
- BATTERY_CLOUD_LOCKS : passthrough du flag dans nightEvcs
|
||
- DECISION_ENGINEV2 : branche 'night_session_done_idle' (avant sourceMode blocked)
|
||
|
||
### Validation
|
||
- 3 nouveaux scénarios golden (19 au total) : transition vehicle_full→done_idle
|
||
(takeover turn_off), auto-entretien, batterie basse (takeover turn_on maintenu).
|
||
- Les 16 scénarios existants inchangés (aucune régression).
|
||
- Push via pipeline bloquant (golden 19/19), production stable.
|
||
|
||
### Comportement attendu cette nuit (VW branchée, checklist complète)
|
||
1. 01:24-01:25 : grid_hc_allowed → EDF ON → charge 6 A puis montée (P7)
|
||
2. VW pleine : vehicle_full_or_paused → night_session_done_idle → takeover OFF
|
||
→ EDF coupé par l'hystérésis ≤ 1 min après (si min_on 25 écoulé et SOC ≥ 30)
|
||
3. Batterie sous cible matin (35 été) : EDF maintenu + recharge ACM jusqu'à cible
|
||
4. 07:24 : fin de fenêtre → retour profil jour
|
||
|
||
## 15. Session du 07/08/2026 (soir) — R2 : Git = source de vérité du code des nodes
|
||
|
||
### Nouveau layout
|
||
- **`src/nodes/`** = SOURCE DE VÉRITÉ : INPUT_PARSE.js, WINDOWS_SOURCEV2.js,
|
||
BATTERY_CLOUD_LOCKS.js, DECISION_ENGINEV2.js, HA_SERVICE_CALLS_OUTPUT.js,
|
||
BUILD_ACTIONS.js, HA_SNAPSHOT_TEMPLATE.json.tpl.
|
||
- `workflows/evcs-surplus-controller-v2.6.json` = artefact déployable, reconstruit
|
||
automatiquement depuis src/ à chaque push (src GAGNE toujours).
|
||
- `tools/lib-node-map.mjs` (cartographie), `tools/n8n-extract.mjs` (JSON→src,
|
||
uniquement pour resynchroniser après une édition UI n8n — à éviter),
|
||
`tools/n8n-build.mjs [--check]` (src→JSON / vérif synchro).
|
||
- `archive/` : toutes les copies historiques (decoupesurplus/, WINDOWS/, BATTERY/,
|
||
DECISION/, HYSTERESIS/, EVCSV2.2.json, surplus.js...) — copies mortes, dérivées.
|
||
- `.gitignore` : backup-workflow-*.json + modified/prepush-*.json (rollbacks locaux).
|
||
|
||
### WORKFLOW DE MODIFICATION DÉSORMAIS (règle inscrite dans AGENTS.md)
|
||
```
|
||
éditer src/nodes/<NODE>.js
|
||
node tools/n8n-push.mjs workflows/evcs-surplus-controller-v2.6.json
|
||
→ build src→JSON → node --check → GOLDEN 19/19 (bloquant) → backup serveur → PUT
|
||
git commit (src + json + scénarios si évolution voulue) && git push
|
||
```
|
||
JAMAIS d'édition directe du JSON ou de l'UI n8n (écrasée au prochain push).
|
||
|
||
### Validations effectuées
|
||
- Extract/build idempotents (extract → build → --check : zéro diff).
|
||
- Test mutation : édition src → build injecte 1 node → restauration git+extract OK.
|
||
- Pipeline complet de push exécuté en réel : build (no-op) → golden 19/19 → PUT OK.
|
||
- Nota : tailles `ls` ≠ longueur de code (octets UTF-8 vs caractères, accents FR).
|
||
|
||
### Plan de maintenabilité : état
|
||
- R1 ✅ déduplication moteur nuit (07/08)
|
||
- R3 ✅ suite golden 19 scénarios bloquante (07/08)
|
||
- R2 ✅ git source de vérité + build/push (07/08)
|
||
- Suivant : agent IA planificateur en MODE OMBRE (jamais dans la boucle 30 s) —
|
||
helpers advisor_*, validation bornée des sorties, kill-switch, cf. HANDOFF §13/§14.
|
||
|
||
## 16. Session du 07/08/2026 (nuit) — Agent IA planificateur en MODE OMBRE
|
||
|
||
### Ce qui tourne désormais
|
||
Workflow n8n **"EVCS Advisor Shadow V1"** (id `tdfInZns7cqo8fU2`, actif, 15 min) :
|
||
```
|
||
Schedule 15min → HA Snapshot compact (template Jinja {{ {...} | tojson }})
|
||
→ BUILD_PROMPT (kill-switch input_boolean.evcs_advisor_enabled, prompt FR ~4K chars)
|
||
→ IF_ENABLED → LLM_LOCAL_MODELS (GET 10.10.10.8:8000/v1/models, 5s)
|
||
→ LLM_LOCAL (modèle DYNAMIQUE = celui chargé sur la Spark, max_tokens 6000)
|
||
→ IF_LOCAL_OK → sinon LLM_CLOUD (OpenRouter anthropic/claude-haiku-4.5,
|
||
credential chiffré "OpenRouter Advisor" id eOM9xofDHT0KMoeY — clé JAMAIS dans git)
|
||
→ VALIDATE_PARSE (strip <think>, JSON strict, enums+bornes, invalide = REJET)
|
||
→ BUILD_HA_CALLS (helpers advisor_* + divergence + compteurs + logbook)
|
||
→ Execute HA Service
|
||
```
|
||
|
||
### GARANTIE MODE OMBRE (structurelle, testée)
|
||
BUILD_HA_CALLS n'écrit QUE vers `input_text/input_number.evcs_advisor_*` et
|
||
logbook. Le test `tests/advisor/run.mjs` ÉCHOUE si une écriture sort de ce
|
||
périmètre (16 tests : kill-switch, réponses valides/hors-bornes/non-JSON/
|
||
<think>/offline, périmètre ombre, compteurs accord/divergence).
|
||
Suite intégrée au push : `n8n-push.mjs workflows/evcs-advisor-shadow-v1.json`.
|
||
|
||
### Helpers HA (package NFS /mnt/ARCH3/docker/HA/packages/evcs_advisor_shadow.yaml)
|
||
`evcs_advisor_enabled` (kill-switch) · `advisor_strategy` · `advisor_night_target_soc`
|
||
(22-90) · `advisor_current_max_a` (6-28) · `advisor_ev_charge_tonight` · `advisor_reason`
|
||
· `advisor_status` (ok/invalid/llm_offline) · `advisor_divergence` · `advisor_last_run`
|
||
· `advisor_model` · `advisor_agree_count` / `advisor_diverge_count`.
|
||
Carte dashboard « 🤖 Conseiller IA (ombre) » ajoutée à evcs_master.yaml (backup dans SAVE/).
|
||
|
||
### Première recommandation réelle (07/08 22:40, via qwen3-vl local)
|
||
`auto_solar_plus` | cible 35% | charge VE cette nuit: oui | confiance 90% |
|
||
« Prévision solaire forte (40.7 kWh), cible SOC matin basse adaptée. Véhicule
|
||
chargé en HC avec réseau EDF. » → **accord avec le réel** (et avec P7/P8 !).
|
||
|
||
### Pièges rencontrés (pour mémoire)
|
||
1. Template /api/template DOIT être du Jinja : `{{ { ... } | tojson }}` —
|
||
sans wrapper, HA renvoie le texte brut non rendu.
|
||
2. Modèle local = qwen3-vl **Thinking** : max_tokens 2500 → JSON tronqué par le
|
||
raisonnement → rejeté proprement (`invalid: JSON non terminé`). Fix : 6000.
|
||
(La validation stricte a fait exactement son travail dès le 1er run réel.)
|
||
3. MCP activate_workflow → erreur 415 : activer via REST POST .../activate body {}.
|
||
4. n8n-push : le fallback d'id par défaut (EVCS) était DANGEREUX avec un 2e
|
||
workflow → supprimé, l'id vient du champ `id` du JSON ou de l'argument.
|
||
|
||
### Outillage multi-workflows (extension R2)
|
||
`lib-node-map.mjs` : WORKFLOWS[] (evcs + advisor) · build/extract/push acceptent
|
||
un fichier cible · `n8n-create.mjs` (POST création) · `gen-advisor-workflow.mjs`
|
||
(squelette advisor rejouable, credentials/ids inclus).
|
||
|
||
|
||
### Note LLM local (analyse du 07/08, décision : statu quo surveillé)
|
||
qwen3-vl-Thinking "fait le job" mais est sous-optimal pour l'advisor : variante
|
||
Thinking (raisonnement non désactivable, 40-60 s/run, risque marginal de JSON
|
||
tronqué — rejeté proprement par la validation). Le bon modèle local est
|
||
qwen3.6-35b quand il est chargé (use-code).
|
||
**Option prête si les compteurs se dégradent** (décision utilisateur : ne rien
|
||
changer pour l'instant) : routage intelligent = 1 IF après LLM_LOCAL_MODELS
|
||
(local seulement si l'id du modèle chargé n'est pas vl/thinking, sinon cloud
|
||
direct) + éventuellement cloud moins cher (gemini-2.5-flash-lite ~1€/mois vs
|
||
haiku-4.5 ~9€/mois plein temps). Implémentation ~30 min, tests advisor inchangés.
|
||
|
||
### Phase 3 — critères de bascule (dans 2-3 semaines d'observation)
|
||
1. Taux d'accord stratégie ≥ 80% (compteurs agree/diverge sur la carte) ;
|
||
2. Zéro sortie invalide NON rejetée (advisor_status) ;
|
||
3. Divergences analysées = soit l'agent a raison (améliorer les règles),
|
||
soit il a tort (améliorer le prompt) — les deux font progresser le système.
|
||
→ Alors premier câblage réel LIMITÉ : night_target_soc uniquement, derrière un
|
||
nouveau toggle `advisor_apply_night_target`, le reste inchangé.
|
||
|
||
|
||
## 17. Session du 08/08/2026 — Incident rebond EDF nuit 07-08/08 : fixes A/B/C/D
|
||
|
||
### Incident (captures HA analysées, faits dans docs/vision-notes.md)
|
||
P7 a chargé la VW dès 01:24 ✓ (session 1h32m). Ensuite :
|
||
1. Fin de charge détectée très tardivement (~04:15 au lieu de ~02:56) ;
|
||
2. Rebond EDF ON/OFF période ~10 min autour de SOC=35 (= cible matin) + flapping
|
||
ACM Charging/passthru 2-4 s (03:58→04:16) ;
|
||
3. Débranchement VW (04:17) → takeover bien relâché mais EDF maintenu
|
||
(hold_on_min_runtime 25 min) → arrêt manuel utilisateur 04:25 (stopped_user).
|
||
|
||
### Root causes
|
||
- **A — point dégénéré SOC==cible** (WINDOWS_SOURCEV2) : engage takeover
|
||
`soc <= cible`, release `soc > cible`, recovery ACM `soc < cible` (strict),
|
||
et l'ACM recharge PILE à la cible → aucune position stable, oscillation.
|
||
- **B — snapshot incomplet** : `evcs_night_session_start_soc/_at` jamais relus
|
||
(absents du template) → `nightSessionNeedsCapture` vrai à CHAQUE cycle →
|
||
"Night Session Start At" réécrit toutes les 30 s toute la nuit (visible logs),
|
||
budget décharge nuit faussé (start SOC glissant).
|
||
- **C — hystérésis grid** : pas de coupure anticipée au débranchement
|
||
(min_on 25 min s'applique même véhicule parti et SOC ≥ seuil OFF).
|
||
- **D — détection fin de charge fragile** : VDF armé uniquement session
|
||
pleinement active ; stop par garde (ex battery_current_hard_stop) → filtre
|
||
désarmé → 'vehicle_full_or_paused' inatteignable pendant longtemps.
|
||
|
||
### Fixes déployés (production 08/08 ~14:00, golden 23/23)
|
||
- **Fix A** (WINDOWS_SOURCEV2 + BCL passthrough + DECISION) : nouvel état
|
||
auto-entretenu **`night_recovery_done_idle`** émis quand
|
||
vehicleChargeCompleted && SOC ≥ cible. Takeover relâché DÉFINITIVEMENT ;
|
||
ré-engagement uniquement si SOC ≤ cible − 2 (bande NIGHT_RECOVERY_REARM_BAND_PCT).
|
||
⚠ Le nom ne matche aucun pattern no-demand de l'hystérésis (comme done_idle).
|
||
Structure takeover : completed ? !nightRecoveryDone : (trigger || urgent || latch).
|
||
- **Fix B** (HA_SNAPSHOT_TEMPLATE) : +`evcs_night_session_start_soc`,
|
||
+`evcs_night_session_start_at` (helpers input_text existants, vérifiés via MCP).
|
||
- **Fix C** (workflow hystérésis 7iIlu7LLTW7TZA8o, patch
|
||
tools/patch-20260808-c-unplug-cut.mjs rejouable) : nouvel état
|
||
**`turn_off_vehicle_unplugged`** = véhicule débranché (es=vehicle_not_connected)
|
||
+ latch ON + grid ON + pas d'override + SOC ≥ seuil OFF → coupure immédiate
|
||
(bypass min_on) + notification. Dry-run 4 cas validé (incident/SOC bas/
|
||
takeover ON/recovery_done) AVANT push. Le fichier local du workflow est
|
||
désormais suivi : workflows/victron-grid-hysteresis-v1.json.
|
||
- **Fix D** (WINDOWS_SOURCEV2) : `evcs_status_raw == 3` (Victron "charged")
|
||
vaut fin de charge immédiate, sans attendre le VDF.
|
||
|
||
### Golden : 19 → 23 scénarios
|
||
- 2 attendus mis à jour (transition/auto-entretien : SOC ≥ cible → état
|
||
`night_recovery_done_idle` désormais) ;
|
||
- 4 nouveaux : `night-recovery-atteinte-soc-cible` (LE cas du rebond),
|
||
`night-recovery-done-bande-collante` (34 % dans la bande),
|
||
`night-recovery-rearm-sous-bande` (32 % → ré-engagement),
|
||
`night-vw-statut-charged` (Fix D).
|
||
- Piège outillage : `tests/golden/run.mjs` exécute le JSON BUILDÉ
|
||
(workflows/*.json), pas src/ → toujours `node tools/n8n-build.mjs` avant un
|
||
run golden manuel (n8n-push le fait automatiquement).
|
||
|
||
### Documentation
|
||
- MANUEL_COMPLET.md §11.6 : GRAFCET complet du cycle nuit (G1 EVCS / G2
|
||
hystérésis) + états surplus fin de nuit + résumé incident.
|
||
- docs/vision-notes.md : OCR/faits des 9 captures de l'incident.
|
||
|
||
### À surveiller (prochaine nuit HC, VW branchée)
|
||
1. Fin de charge : passage à vehicle_full_or_paused OU night_recovery_done_idle
|
||
rapide (Fix D si la VW publie le statut 3) ;
|
||
2. SOC à la cible → UNE coupure EDF, puis plus aucun ON/OFF (sauf SOC ≤ cible−2) ;
|
||
3. "Night Session Start At" écrit UNE fois en début de session (Fix B) ;
|
||
4. Débranchement → EDF OFF ≤ 1 min (turn_off_vehicle_unplugged, Fix C).
|
||
|
||
---
|
||
*Document mis à jour le : 08/08/2026 (~14:15) — fixes A/B/C/D déployés, golden 23/23*
|
||
|
||
## 18. Session du 09/08/2026 — Incident nuit 08-09/08 : le rebond est revenu par un AUTRE chemin (Fix E)
|
||
|
||
### Incident (captures HA, VW branchée, HC, VW pleine vers ~03:00)
|
||
Fix A/D du 08/08 ont bien fonctionné CÔTÉ WINDOWS (status 3 → completed →
|
||
takeover Désactivé, source blocked). MAIS l'état EVCS observé était
|
||
`hold_min_run_source_lost` / `stop_source_lost` — jamais `night_recovery_done_idle`.
|
||
Cycles ~30 min de 03:00 à 05:40 : EDF ON (2531 W réseau, SOC 51 % > cible 35)
|
||
→ arrêt urgence utilisateur 05:43 (EDF URGENCE OFF + stratégie stop) → débranchement.
|
||
|
||
### Root cause (ordre des branches DECISION)
|
||
1. Status 3 arrive PENDANT la session armée (relais fermé, chg=1/perm=1/l=1).
|
||
2. WINDOWS → sourceMode 'blocked' (Fix D) ; mais dans DECISION la branche
|
||
session ACTIVE traite 'blocked' comme « source perdue » :
|
||
`hold_min_run_source_lost` (25 min de session zombie !) puis `stop_source_lost`.
|
||
La branche P8 (état collant) est dans la chaîne NON-active → jamais atteinte.
|
||
3. Au stop, le relais s'ouvre → le statut EVCS RETOMBE (3 → 1/6, même piège que
|
||
le VDF documenté en P8) → completed=false → re-P7 → takeover ON 1 cycle →
|
||
`edf_general_ok_on` rallume EDF + restart → status 3 → goto 2. Rebond ~30 min.
|
||
4. L'hystérésis adoptait l'EDF ON du cycle de flap puis `hold_on_min_runtime`
|
||
25 min (ov=0/eg=0 ensuite, mais latch pris) → EDF ON quasi permanent.
|
||
|
||
### Fix E (déployé 09/08 ~06:25, golden 25/25)
|
||
- **DECISION** : nouvelle PREMIÈRE branche du bloc session-active (avant VDF) :
|
||
`night_hc && vehicleChargeCompleted && !force*` → setOff immédiat avec l'état
|
||
collant (`night_recovery_done_idle` si SOC ≥ cible, sinon
|
||
`night_session_done_idle`), decision `stop_vehicle_full_or_paused`.
|
||
→ l'état collant est écrit AU PREMIER cycle où status=3, relais encore fermé ;
|
||
les retombées de statut ensuite sont sans effet (prev state dans la liste).
|
||
- **WINDOWS** : garde puissance sur Fix D (`status==3 && evcsAcPowerW <= 300 W`)
|
||
pour ne jamais latcher sur un glitch en pleine charge.
|
||
- **Hystérésis** (patch tools/patch-20260809-e2-recovery-done-cut.mjs) :
|
||
`turn_off_recovery_done` = es=night_recovery_done_idle + pas d'override +
|
||
SOC ≥ seuil OFF → coupure EDF immédiate (bypass min_on 25). Sûr car l'état
|
||
est collant (le takeover ne peut pas revenir, sauf SOC ≤ cible−2 → override).
|
||
Dry-run 5 cas validé avant push.
|
||
|
||
### Golden : 23 → 25
|
||
- `night-charged-en-session-active` : status 3 en session ARMÉE → arrêt propre
|
||
+ latch immédiat (LE chemin de l'incident) ;
|
||
- `night-flap-statut-apres-latch` : relais ouvert, statut retombé à 1,
|
||
prev=night_recovery_done_idle → collant, aucun re-P7/EDF.
|
||
|
||
### Comportement attendu au REBRANCHEMENT d'une VW pleine en HC
|
||
1. Session nuit → P7 → EDF ON → tentative de charge (inévitable : le système
|
||
ne peut pas savoir que la VW est pleine sans fermer le relais) ;
|
||
2. VW refuse (0 W, status 3) → ≤ 1 min : `night_recovery_done_idle` collant,
|
||
takeover OFF, session stoppée proprement ;
|
||
3. Hystérésis : `turn_off_recovery_done` → EDF OFF ≤ 1 min (plus de min_on 25) ;
|
||
4. Plus RIEN jusqu'au débranchement / fin de fenêtre (flaps de statut ignorés).
|
||
Total attendu : EDF ON ~2-3 min UNE seule fois, puis silence.
|
||
|
||
---
|
||
*Document mis à jour le : 09/08/2026 (~06:35) — Fix E déployé (EVCS + hystérésis), golden 25/25*
|
||
|
||
## 19. Session du 09/08/2026 (suite) — Fix F : la mémoire "charge terminée" est prioritaire sur les gardes
|
||
|
||
### Test de rebranchement utilisateur (~06:33, VW rebranchée en HC)
|
||
1. ✅ Fix E validé en réel : 06:51:00 `night_recovery_done_idle` latché,
|
||
06:51:03 `turn_off_recovery_done` → EDF coupé immédiatement (bypass min_on).
|
||
2. ❌ 06:53-06:56 : la VW a relancé une charge (status 2) qui, EDF OFF, tirait
|
||
sur la batterie (−47 A) → garde `mustStopHard` → état
|
||
`battery_current_hard_stop` ÉCRIT PAR-DESSUS le collant dans le helper →
|
||
completed perdu → re-P7 → EDF rallumé (hold_on_evcs_override, re-min_on 25).
|
||
→ Le collant ne survivait à AUCUN état émis par une branche plus prioritaire.
|
||
|
||
### Fix F (déployé 09/08 ~07:00, golden 26/26)
|
||
- DECISION : nouvelle branche dans la cascade PRINCIPALE, entre
|
||
`vehicle_not_connected` et `mustStopHard` :
|
||
`night_hc && vehicleChargeCompleted && !force*` → setOff avec l'état collant
|
||
(recovery_done/done_idle), `mustStop` si la borne est encore armée.
|
||
→ l'état collant est réécrit à CHAQUE cycle tant que VW branchée + fenêtre,
|
||
il survit aux micro-charges/gardes. Aucune perte de protection (le setOff
|
||
coupe chg/perm/latch comme un hard stop).
|
||
- La branche Fix E interne (session active) supprimée : remplacée par la
|
||
branche haute. La branche P8 historique (chaîne non-active) reste en filet.
|
||
- Golden 26 : `night-hard-stop-necrase-pas-done` (micro-charge −60 A sur
|
||
batterie avec prev=recovery_done → l'état reste recovery_done, stop ferme).
|
||
|
||
### Leçon d'architecture (pour le futur)
|
||
La "mémoire" portée par input_text.victron_evcs_surplus_state est fragile :
|
||
tout état émis l'écrase. Fix F la protège en réécrivant chaque cycle, mais la
|
||
solution PROPRE à terme est un helper dédié (input_boolean
|
||
victron_evcs_night_charge_done, package YAML + reload HA) si un nouveau cas
|
||
d'écrasement apparaît.
|
||
|
||
### Relecture de la nuit 08-09/08 (après coup)
|
||
La VW n'a pris que 5 kWh cette nuit, morcelés par les stops zombies (§18) :
|
||
elle n'était probablement PAS pleine. Les "micro-charges" du matin étaient de
|
||
vraies reprises. À 07:00 (post-Fix F) : charging_step_up_grid_hc = charge HC
|
||
nominale jusqu'à la fin de fenêtre 07:24. Le collant ne se posera que sur un
|
||
VRAI signal de fin (VDF no-demand ou status 3) — et tiendra désormais.
|
||
|
||
---
|
||
*Document mis à jour le : 09/08/2026 (~07:05) — Fix F déployé, golden 26/26*
|
||
|
||
## 20. Session du 09/08/2026 — PREMIER CÂBLAGE RÉEL de l'advisor : cible SOC matin
|
||
|
||
### Décision utilisateur
|
||
Météo Bretagne au beau fixe (47-58 kWh/j prévus) = conditions idéales pour
|
||
tester le câblage avec risque minimal. Périmètre STRICTEMENT limité au plan
|
||
§16 : `night_target_soc` uniquement, derrière un toggle, rollback 1 clic.
|
||
|
||
### Ce qui est câblé
|
||
`batteryMorningTargetPct` (WINDOWS_SOURCEV2) suit
|
||
`input_number.evcs_advisor_night_target_soc` SI ET SEULEMENT SI :
|
||
1. `input_boolean.evcs_advisor_apply_night_target` = ON (nouveau toggle,
|
||
initial OFF, package evcs_advisor_shadow.yaml + carte dashboard) ;
|
||
2. `input_text.evcs_advisor_status` = 'ok' (exact) ;
|
||
3. recommandation fraîche ≤ 120 min (parse `DD/MM HH:mm` de advisor_last_run,
|
||
année courante, garde passage d'année — parseAdvisorRunToMs INPUT_PARSE) ;
|
||
4. valeur clampée [max(22, hard floor), 90].
|
||
Sinon → règles forecast historiques (`batteryMorningTargetRulePct`), aucun
|
||
changement de comportement. Le toggle OFF = état d'origine.
|
||
|
||
### Chaîne technique
|
||
- Snapshot : +advisor_apply_night_target, +advisor_night_target_soc,
|
||
+advisor_status, +advisor_last_run (states() d'une entité absente →
|
||
'unknown' → toggle vu OFF → sûr AVANT le reload HA).
|
||
- INPUT_PARSE : params.advisorApplyNightTarget / advisorNightTargetSocPct /
|
||
advisorStatusOk / advisorLastRunMs.
|
||
- WINDOWS : override borné + export advisorNightTargetApplied +
|
||
batteryMorningTargetRulePct (preview) ; BCL passthrough ; DECISION : la
|
||
reason affiche `target=X% (ADVISOR)` quand appliqué → visible logbook/carte.
|
||
- Impact aval automatique : gridTakeoverTriggerPct, grid_forced vs hc_allowed,
|
||
recovery post-charge, bande anti-rebond — tous dérivés de la cible.
|
||
|
||
### Golden 26 → 29
|
||
- `night-advisor-target-applique` : SOC 30, advisor 22 ON/ok/frais →
|
||
grid_hc_allowed (au lieu de grid_forced par la règle 35) ;
|
||
- `night-advisor-target-stale` : > 120 min → fallback règles (grid_forced) ;
|
||
- `night-advisor-toggle-off` : toggle OFF → règles inchangées.
|
||
|
||
### Sécurités NON modifiées
|
||
Hard floor 22 %, hysteresis grid ON ≤ 20 %, recovery urgent départ, latch
|
||
anti-créneaux, collant fin de charge (Fix A/E2/F). L'advisor ne peut RIEN
|
||
piloter d'autre (VALIDATE_PARSE borne night_target_soc 22-90 côté advisor,
|
||
clamp identique côté contrôleur).
|
||
|
||
### Rollback (3 niveaux)
|
||
1. Toggle « APPLIQUER cible SOC matin » OFF → effet au cycle suivant (30 s) ;
|
||
2. Kill-switch advisor OFF → plus de runs → fraîcheur > 120 min → fallback
|
||
auto en ≤ 2 h même si le toggle reste ON ;
|
||
3. `git revert` du commit + `node tools/n8n-push.mjs workflows/evcs-surplus-controller-v2.6.json`.
|
||
|
||
### Surveillance (2-3 nuits)
|
||
- Carte Conseiller IA : « Cible SOC matin (agent) » vs « (réel) » doivent
|
||
converger quand le toggle est ON ;
|
||
- Logbook « Victron EVCS - Reason » la nuit : `target=X% (ADVISOR)` ;
|
||
- Le matin : SOC batterie au lever du soleil ≈ cible advisor, remontée solaire OK ;
|
||
- Si SOC matin < 22 % ou EDF nuit anormal → toggle OFF + capture + analyse.
|
||
|
||
### Action utilisateur requise après ce déploiement
|
||
Recharger HA pour créer le toggle : Outils développeur → YAML →
|
||
« ENTRÉES BOOLÉENNES (input_boolean) » Recharger (ou redémarrage HA), puis
|
||
activer le toggle sur la carte Conseiller IA (dashboard EVCS Master rechargé).
|
||
|
||
---
|
||
*Document mis à jour le : 09/08/2026 (~07:35) — câblage advisor night_target déployé (toggle OFF par défaut), golden 29/29*
|
||
|
||
## 21. Session du 09/08/2026 — P9 : charge réseau avant départ + sélecteur véhicule VW/JEEP
|
||
|
||
### Contexte clarifié par l'utilisateur (IMPORTANT pour comprendre les incidents)
|
||
DEUX véhicules alternent sur la borne :
|
||
- **VW** (hybride) : départ ~08:50, ne publie PAS franchement "charged" (statut
|
||
3 rare/fugace) → détection fin de charge = VDF surtout ;
|
||
- **JEEP** : départ ~06:30, publie "charged" franchement.
|
||
Relecture des incidents : nuit 08-09/08 = JEEP (rebonds §18) ; matin 09/08 =
|
||
VW déjà pleine (chargée la veille en solaire) rebranchée à la place de la JEEP.
|
||
L'heure de départ unique (08:50) était un réglage VW ; la JEEP part à 06:30
|
||
(dans la fenêtre HC → couverte par la mécanique nuit existante).
|
||
|
||
### Nouveaux helpers (package /mnt/ARCH3/docker/HA/packages/victron_evcs_vehicles.yaml)
|
||
- `input_select.victron_evcs_vehicle` (VW/JEEP, défaut VW) — à sélectionner au
|
||
branchement. NB : l'ancien `input_select.victron_evcs_target_vehicle`
|
||
(None/Jeep/Zero, créé UI) n'était câblé nulle part → retiré de la carte,
|
||
remplacé par le nouveau. À supprimer un jour via l'UI.
|
||
- `input_datetime.victron_evcs_departure_time_jeep` (06:30). L'existant
|
||
`victron_evcs_departure_time` (08:50) devient l'heure VW.
|
||
- `input_boolean.victron_evcs_allow_grid_departure_urgent` (toggle P9, OFF).
|
||
Carte "Paramètres utilisateur" (evcs_master.yaml) mise à jour.
|
||
⚠ RELOAD HA requis (input_boolean + input_datetime + input_select).
|
||
|
||
### P9 (golden 33/33, déployé 09/08 ~08:27)
|
||
- INPUT_PARSE : `departureTimeMin` suit le véhicule sélectionné (JEEP →
|
||
departure_time_jeep, fallback VW) ; impacte aussi departurePriorityActive
|
||
et batteryRecoveryUrgent nuit (donc la JEEP à 06:30 est mieux servie).
|
||
- WINDOWS : fenêtre `departureWindowActive` = toggle ON + véhicule branché +
|
||
HORS fenêtre HC + départ dans ≤ 120 min. Si charge non terminée →
|
||
`departureUrgentActive` → sourceMode `grid_hc_allowed` (prend le pas sur
|
||
blocked/solar_potential/hybrid_probe, JAMAIS sur solar_only ni stop/force*).
|
||
Fin de charge (VDF vehicle_full_or_paused relu, ou statut 3) →
|
||
`departureChargeDone` → sourceMode blocked + état collant.
|
||
- BCL : `gridAllowedBySoc` inclut departureUrgentActive (sinon SOC>35 bloquait).
|
||
- DECISION : branche collante `departure_done_idle` (même principe que Fix F,
|
||
au-dessus des gardes, mustStop si borne armée).
|
||
- HA_SERVICE_CALLS : helper takeover tenu ON pendant departureUrgentActive
|
||
(override hystérésis + maintien batterie ACM pendant la charge pré-départ).
|
||
- Hystérésis (patch tools/patch-20260809-p9-departure-cut.mjs) : coupure
|
||
immédiate E2 étendue à `departure_done_idle`.
|
||
|
||
### Golden 29 → 33
|
||
p9-depart-vw-grid (fenêtre active, SOC 45 > 35 → réseau quand même) ·
|
||
p9-depart-jeep-passe (sélecteur : départ JEEP passé → rien) ·
|
||
p9-done-idle-collant · p9-toggle-off (comportement historique).
|
||
|
||
### Mode d'emploi utilisateur
|
||
1. Recharger HA (YAML : input_boolean, input_datetime, input_select) ;
|
||
2. Sur la carte : sélectionner le véhicule au branchement (VW ou JEEP) ;
|
||
3. Activer « ⚡ Réseau avant départ (P9) » ;
|
||
4. La JEEP profite de sa vraie heure 06:30 dès cette nuit (recovery urgent
|
||
nuit) ; la VW aura sa fenêtre 06:50-08:50 hors HC si sous-chargée.
|
||
Rollback : toggle P9 OFF (30 s) ; sélecteur sans effet si P9 OFF (sauf heure
|
||
de départ pour la priorité/urgence nuit — voulu).
|
||
|
||
---
|
||
*Document mis à jour le : 09/08/2026 (~08:35) — P9 + sélecteur véhicule déployés, golden 33/33*
|
||
|
||
## 22. Session du 09/08/2026 — P10 : mémoire "véhicule plein" en journée
|
||
|
||
### Incident (captures utilisateur 09:49-09:57)
|
||
VW pleine rebranchée ~07:00. À 09:49, PV fort (5,5 kW) → le surplus solaire
|
||
lance une tentative (morning_solar, solar_only) : 6 A commandés, 0 W pris,
|
||
VRM affiche "Charged". Sans mémoire jour, le cycle se répète toutes les
|
||
~15 min TOUTE la journée (VDF stop → block → re-start). Zéro coût énergie
|
||
(solaire, EDF OFF) mais usure relais borne/VW + bruit. Les états collants
|
||
P8/P9 étaient scopés nuit / fenêtre départ.
|
||
|
||
### Fix P10 (déployé 09/08 10:04, golden 36/36, validé live à 10:04:30)
|
||
- WINDOWS : `dayVehicleFullIdle` collant = véhicule branché + hors fenêtre HC
|
||
+ (prev ∈ {day_vehicle_full_idle, vehicle_full_or_paused,
|
||
night_recovery_done_idle, night_session_done_idle, departure_done_idle}
|
||
OU statut 3 ≤ 300 W). sourceMode → 'blocked' (y compris face à solar_only :
|
||
charger un véhicule plein ne sert à rien).
|
||
- HÉRITAGES bidirectionnels : le jour hérite des états done de la nuit
|
||
(07:24 → aucune re-tentative matinale) ; la nuit hérite du jour
|
||
(vehicleChargeCompleted inclut day_vehicle_full_idle → pas de tentative
|
||
01:24 si resté branché plein).
|
||
- DECISION : branche collante 'day_vehicle_full_idle' au-dessus des gardes
|
||
(principe Fix F), stop propre si session armée.
|
||
- Libération : DÉBRANCHEMENT (vehicle_not_connected) — ou échappatoires
|
||
force_charge_now / force_grid_now (testé golden).
|
||
- Hystérésis : nom sans pattern no-demand ✓, aucun changement nécessaire
|
||
(EDF pas impliqué dans ce chemin).
|
||
|
||
### Golden 33 → 36
|
||
day-vw-pleine-statut3 (session armée 0 W statut 3 → collant) ·
|
||
day-full-heritage-nuit (prev=night_recovery_done_idle → pas de solaire) ·
|
||
day-full-echappatoire-force-charge (force_charge_now relance).
|
||
|
||
### Validation live
|
||
10:04:14 PUT → 10:04:30 : `day_vehicle_full_idle` latché sur la VW pleine
|
||
en cours de tentative ; statut retombé à 6 (relais ouvert) sans relance.
|
||
|
||
### Cycle de vie complet d'un véhicule branché (résumé)
|
||
```
|
||
branché → tentatives légitimes (nuit P7 / jour solaire / P9 départ)
|
||
→ plein détecté (statut 3 ou VDF)
|
||
→ collant : night_*_done_idle / departure_done_idle / day_vehicle_full_idle
|
||
(héritages entre périodes, réécrit chaque cycle au-dessus des gardes)
|
||
→ DÉBRANCHEMENT = seule remise à zéro (+ force_* en échappatoire)
|
||
```
|
||
|
||
---
|
||
*Document mis à jour le : 09/08/2026 (~10:10) — P10 déployé et validé live, golden 36/36*
|