feat: session 09/07 — P1-P5 déployés (hystérésis robuste, anti-créneaux nuit, provocation PV, maintien SOC takeover, plafond EDF ON) + backups n8n/lovelace + HANDOFF à jour

This commit is contained in:
Tallagrand Thierry
2026-07-09 23:56:13 +02:00
parent 59f6ae7e9e
commit 9be0f61806
11 changed files with 925 additions and 178 deletions

52
AGENT.md Normal file
View File

@@ -0,0 +1,52 @@
EVCS Workflow Analysis
## Workflow Mechanism Analysis
### Energy Context Monitoring
- Tracks battery SOC, PV power, house load, grid power, and EVCS current
- Monitors vehicle connection status
- Continuously evaluates energy balance
### Decision Logic
- Primary decision: "stop_not_connected" when vehicle is not connected
- Secondary decisions based on energy context (night_hc, day_hc)
- Source authorization: "Bloqué" (Blocked) when conditions aren't met
### Control Flow
- Surplus controller active but only manages surplus when vehicle is connected
- Grid power used when solar is unavailable and house needs must be met
- Manual mode allows override of automated decisions
### State Transitions
- Vehicle connection status triggers different control paths
- Energy context changes trigger different strategies
- User interventions (manual mode) can override automated decisions
## Situational Framework
| Situation | Vehicle Status | Solar Power | House Load | Grid Power | EVCS Current | Decision Logic |
Control Action |
|---------|----------------|------------|-----------|------------|--------------|----------------|------------
----|
| No Vehicle, No Solar | Disconnected | 0 W | 696 W | ON | 0 A | stop_not_connected | Grid powers house |
| No Vehicle, Solar | Disconnected | 683 W | 696 W | OFF | 0 A | stop_not_connected | Solar powers house |
| Vehicle Connected, No Solar | Connected | 0 W | 696 W | ON | 0 A | auto_solar_plus | Grid powers house,
EVCS idle |
| Vehicle Connected, Solar | Connected | 683 W | 696 W | OFF | 0 A | auto_solar_plus | Solar powers house,
EVCS idle |
| Vehicle Connected, High Demand | Connected | 0 W | 1000 W | ON | 7 A | auto_solar_plus | Grid powers house,
EVCS charges |
| Manual Override | Any | Any | Any | Any | Any | Stop | Manual control |
## Key Insights
1. The system prioritizes grid power for house needs when solar is unavailable
2. The "stop_not_connected" logic prevents EV charging but doesn't manage grid power
3. The surplus controller only activates when vehicle is connected and there's surplus energy
4. Manual mode allows complete override of automated decisions
## Recommendations
1. Create a rule to disable grid power when no vehicle is connected and solar is unavailable
2. Implement a "low energy" mode that prioritizes battery charging over grid power
3. Add a "maintenance" mode that allows manual control of all systems
4. Create a monitoring dashboard to track energy flows in real-time

View File

@@ -1,207 +1,198 @@
# HANDOFF.md — EVCS Outside-Window Solar Charging Block # HANDOFF.md — EVCS / Grid Hysteresis — État au 09/07/2026
## 1. Contexte Projet ## 1. Contexte Projet
- **Installation** : Victron ESS sans injection réseau. Batterie 17,5 kWh. - **Installation** : Victron ESS **sans injection réseau**. Batterie 17,5 kWh utile.
- **Workflow actif** : `HA Snapshot EVCS Surplus Controller V2.6` (n8n) - **Workflows n8n actifs** :
- **Code source** : `/mnt/projects/EVCS/EVCS/decoupesurplus/` (architecture modulaire V2) - `HA Snapshot EVCS Surplus Controller V2.6` (id `ATswvGfG4KJUls0K`) — pilote la borne EVCS (30 s)
- **Fichiers critiques** : - `Victron - Battery / Grid Hysteresis Fix V1` (id `7iIlu7LLTW7TZA8o`) — pilote le switch EDF (1 min)
- `WINDOWS/WINDOW_SOURCEV2.js` — Détermine `profile` et `sourceMode` - **Accès** :
- `BATTERY/BATTERY_CLOUD_LOCKS.js` — Calcule `cloudDebt`, `cloudToleranceOk` - HA : `http://10.10.10.17:8123` — jeton dans `jeton_EVCS.txt` (⚠️ en clair, à révoquer si dépôt partagé)
- `snapshot.js` — Récupération des helpers HA - n8n : `https://n8n.airtria.pro` — clé API dans `~/.config/opencode/opencode.json` (mcp.n8n-mcp)
- `modified/EVCS_V2.5_PATCHED.json` — Export n8n de référence (avant upgrade V2.6) - **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 ## 2. Architecture Data-Flow
``` ```
Schedule Trigger (30s) EVCS V2.6 (30s) :
→ HA Snapshot (template) HA Snapshot (template) → INPUT_PARSE → WINDOWS_SOURCEV2
→ INPUT_PARSE → BATTERY_CLOUD_LOCKS → DECISION_ENGINEV2 → HA_SERVICE_CALLS_OUTPUT
→ WINDOWS_SOURCEV2 ← C'EST ICI QUE LE BLOCAGE SE PRODUIT → Build Actions → Execute HA Service
→ BATTERY_CLOUD_LOCKS
→ DECISION_ENGINEV2 Grid Hysteresis (1min) :
→ HA_SERVICE_CALLS_OUTPUT HA Snapshot → Compute Grid Hysteresis → Build Actions → Execute HA Service
→ Execute HA Service
``` ```
## 3. Problème Observé 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`.
### Symptôme ## 3. Patchs déployés le 09/07/2026 (session opencode)
L'après-midi, avec un fort ensoleillement et un SOC élevé (> 85 %), l'EVCS reste en :
- **Profil** : `outside_window`
- **Décision** : `stop_hard` ou `hold` / `blocked`
- **Source** : `Bloqué`
- **Action utilisateur requise** : Forcer manuellement `force_charge_now`
### Capture d'état typique ### P1 — Grid Hysteresis (déployé, vérifié)
``` 1. **Entité fantôme corrigée** : le snapshot lisait `input_select.victron_evcs_user_strategy`
Stratégie : auto_solar_plus (inexistante → toujours `unknown`). Corrigé en `input_select.victron_evcs_strategy`.
Profil : outside_window Conséquence avant fix : les overrides `force_grid_now`/`force_charge_now` et la coupure
Source : Bloqué « voiture pleine » ne fonctionnaient JAMAIS dans l'hystérésis.
SOC : 91 % 2. **Switch EDF `unavailable`** : nouvel état `switch_unavailable` = aucun ordre, aucun timestamp,
Cloud Debt: 1.66 kWh réévaluation complète au retour du switch. Avant : spam `turn_on` chaque minute + rallumage
PV Power : ~800 W (aligné sur charge maison, pas d'injection) aveugle à la reconnexion (incident du 08-09/07 : disjoncteur physique coupé par l'utilisateur,
Decision : stop_hard | battery_current_hard_stop 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é.
**Note contextuelle importante** : L'utilisateur a attendu ~45 min sans succès, puis a volontairement passé en `force_charge_now` pour charger sa moto électrique. Ce forçage a fait tirer sur les batteries à cause des nuages, augmentant encore le `cloud_debt`. ### 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`).
## 4. Root Cause Analysis ### 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.
### 4.1. Définition de outside_window ### P4 — Maintien SOC adapté au forecast pendant le takeover nuit (déployé 09/07 après-midi)
`outside_window` = tout horaire **hors** des 4 fenêtres configurées : - **Découverte clé** : `Victron - Auto Charge Manager V1.2` (id `cTeeHy4ePiVeELLh`) est le
- `night_hc` : 23:00 → 06:00 propriétaire des limites DVCC (`MaxChargeCurrent`/`MaxChargeVoltage` via MQTT,
- `morning_solar` : 07:30 → 11:00 feedback : `sensor.victron_max_charge_current_setting`). Son mode « nuit idle » laissait
- `midday_hc_or_solar` : 12:24 → 14:24 30 A de charge batterie dès que EDF était ON → batterie rechargée à fond pendant un takeover.
- `evening_high_soc` : 17:00 → 20:30 - **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).
Donc `outside_window` couvre : **11:00→12:24, 14:24→17:00, 20:30→23:00**. ### 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.
### 4.2. Logique du sourceMode en outside_window (WINDOW_SOURCEV2.js, L454-468) ### 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.
```javascript ### Maintenance du 09/07 soir (analyse des messages d'erreurs)
else if (profile === 'outside_window') { - **W-105 borne** (surcharge) : survenu en charge 20 A sur onduleur seul (disjoncteur EDF coupé).
if (strongSolarSignal) { Déjà couvert : gardes vebus_overload + P5 (plafond retombe à 20 A si AC-IN absent).
sourceMode = 'solar_only'; - **`saveDataErrorExecution: all`** activé sur les 8 workflows critiques (diagnostic des
} else if (isHighBatteryForPotential && potentialAllowed) { prochaines erreurs avec données des nodes ; Water Heater V2.3 avait erré 4× le 09/07 20:20-20:35,
sourceMode = 'solar_potential'; cause inconnue faute de données, rétabli seul).
} else { - **Garage Thermal** : la prise `switch.clim_hygro_socket_1` est débranchée volontairement par
sourceMode = 'blocked'; // ← TOMBE ICI 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.
Pour sortir de `blocked`, il faut **obligatoirement** l'un des deux : ## 4. Root Cause de l'incident « EDF ON à 48 % sans charge Jeep » (nuit 08→09/07)
#### A. strongSolarSignal (L343-352) Chaîne complète reconstituée via l'historique HA :
```javascript 1. Borne fantôme au matin du 08/07 (« véhicule en charge » sans véhicule) + `force_grid_now`
const strongSolarSignal = de la veille ⇒ EDF maintenu ON (la coupure voiture-pleine était du code mort, cf. P1.1/P1.4).
(parsed.pvPowerW > 0 && parsed.pvPowerW >= effectivePvStartMinW) || 2. L'utilisateur coupe le **disjoncteur physique** ⇒ switch `unavailable` pendant ~5 h.
parsed.pvPowerAvg10mW >= params.pvHoldMinW || 3. Le workflow reste coincé en `hold_on_min_runtime` (P1.3) en spammant `turn_on` (P1.2).
(departurePriorityActive && parsed.pvPowerAvg10mW >= params.pvHoldMinW); 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).
- `pvStartMinW` défaut = **1800 W** Tous les maillons sont corrigés par P1.
- `pvHoldMinW` défaut = **1200 W**
**Pourquoi c'est faux chez l'utilisateur** : ## 5. Sémantique utile (pour ne pas re-chercher)
Comme il n'injecte pas, quand SOC > 85 % et charge maison = 800 W, le MPPT régule les PV à ~800 W. Ni `pvPowerW`, ni `pvPowerAvg10mW` ne dépassent les seuils.
#### B. isHighBatteryForPotential (L335-339) - **Latch** (`input_boolean.victron_grid_hysteresis_latch`) : « c'est l'automate qui a allumé EDF
```javascript et il défend cet état ». Reprise en main manuelle : stratégie `stop` + hystérésis OFF + latch OFF.
const isHighBatteryForPotential = - **Fenêtres EVCS (helpers HA, corrigées/vérifiées le 09/07)** : `night_hc` **01:24→07:24** (HC réelles),
parsed.soc >= 85 && `morning_solar` 07:30→11:00, `midday_hc_or_solar` **12:24→14:24** (HC réelles),
forecastBufferKwh > 0 && `evening_high_soc` 17:00→20:30, sinon `outside_window`.
cloudDebtOkForPotential && // ← BLOQUÉ ICI L'Auto Charge Manager utilisait des fenêtres codées en dur légèrement fausses
batteryOkForPotential; (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.
**Pourquoi c'est faux chez l'utilisateur** : ## 6. Méthode de travail validée (à réutiliser)
```javascript
const cloudDebtOkForPotential = params.cloudDebtPrevKwh <= params.cloudDebtMaxKwh;
// cloudDebtPrevKwh = 1.66 kWh
// cloudDebtMaxKwh = 1.2 kWh (défaut)
// → cloudDebtOkForPotential = FALSE
```
**Donc `isHighBatteryForPotential` est FORCÉMENT FALSE**, même avec SOC 91 %. 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.
### 4.3. Pourquoi le cloud debt est à 1.66 kWh ## 7. Backlog / Point du 10/07 au réveil
Le `cloud_debt` s'accumule quand :
- Session EVCS active
- `sourceMode` n'est PAS `grid_hc_allowed` (donc solaire ou batterie)
- `batteryCurrentA < -neutralBand` (batterie qui décharge)
Sur une journée nuageuse avec intermittences, l'EVCS a démarré/stoppé plusieurs fois. Pendant les passages nuageux, la batterie a complété la puissance manquante → accumulation de dette. Cette dette ne se rembourse que quand `batteryCurrentA > 0` (batterie en charge). Or, si le système bloque l'EVCS, il n'y a pas de passage à `batteryCurrentA > 0` pendant la charge EVCS, donc la dette reste bloquée haute. 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).
### 4.4. Absence de probe en outside_window ## 8. Historique des sessions
Les probes (`morningProbeEligible`, `middayProbeEligible`) sont **strictement limitées** aux profiles `morning_solar` et `midday_hc_or_solar` (WINDOW_SOURCEV2.js L317-330). Il n'existe **aucun mécanisme de sonde** en `outside_window` pour tester si le PV peut supporter une charge à 6 A.
### 4.5. Réseau HC midi / nuit - **12/06/2026** : analyse blocage outside_window, déploiement afternoon_probe (V2.6),
Les toggles `allow_grid_hc_night` et `allow_grid_hc_midday` ne concernent QUE les fenêtres `night_hc` et `midday_hc_or_solar`. Ils sont **totalement ignorés** en `outside_window`. Ce n'est donc PAS une solution. 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,
### 4.6. Priorité départ P1 (hystérésis robuste + entité stratégie), P2 (anti-créneaux nuit), P3 (provocation PV).
`departurePriorityActive` s'active seulement si : - **09/07/2026 (journée/soir)** : incident Synology (NAS éteint → EDF ON toute la nuit, batterie 96 %),
- `departurePriorityEnabled = ON` firmware borne 2.08 (fantôme résolu), dashboard EDF Urgence + boost 28 A, P4 (maintien SOC
- `minutesUntilDeparture <= 240 min` au forecast pendant takeover, via Auto Charge Manager), P5 (plafond EDF ON configurable),
- Véhicule branché 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).
Effet : facilite `strongSolarSignal` en baissant la barre si `pvPowerAvg10mW >= pvHoldMinW`. Mais ne résout pas le blocage `cloudDebt`.
## 5. Résumé des Verrous
| Verrou | Condition | État actuel | Impact |
|--------|-----------|-------------|--------|
| Seuil PV instantané | `pvPowerW >= 1800 W` | ~800 W | `strongSolarSignal = false` |
| Seuil PV moyenne | `pvPowerAvg10mW >= 1200 W` | Souvent < 1200 W | `strongSolarSignal = false` |
| Cloud Debt | `cloudDebt <= 1.2 kWh` | 1.66 kWh | `isHighBatteryForPotential = false` |
| Probe absente | Seulement `morning` / `midday` | N/A en afternoon | Aucun test de démarrage |
| Réseau HC | Ignoré hors fenêtre | N/A | Pas de fallback grid |
## 6. Options de Correction Envisagées
### Option A — Relâcher `cloudDebtOkForPotential` en `outside_window`
Dans `WINDOW_SOURCEV2.js`, ignorer la condition `cloudDebtOkForPotential` pour le calcul de `isHighBatteryForPotential` quand `profile === 'outside_window'`.
**Avantage** : Simple, 1 ligne.
**Risque** : Peut entraîner une décharge excessive de la batterie si le PV baisse brutalement.
### Option B — Ajouter une `afternoon_probe` en `outside_window` (RECOMMANDÉ)
Créer une logique de sonde (probe) active en `outside_window` lorsque `auto_solar_plus` est sélectionné. Démarrage à 6 A pendant 3 min, puis évaluation.
**Avantage** : Fidèle à la philosophie du système (ne charge que si le PV confirme).
**Risque** : Nécessite modification de `BATTERY_CLOUD_LOCKS.js` + `DECISION_ENGINEV2.js`.
### Option C — Ajouter une fenêtre `afternoon_solar`
Étendre la logique des fenêtres pour inclure une plage 14:24→17:00 (ou configurable) avec des seuils PV adaptés.
**Avantage** : Résout définitivement le "trou" de l'après-midi.
**Risque** : Change la sémantique utilisateur des fenêtres.
### Option D — Abaisser `pvStartMinW` / `pvHoldMinW` dynamiquement
En `outside_window` + `auto_solar_plus`, baisser les seuils si `soc >= 90` et `forecastBufferKwh > 0`.
**Avantage** : Adaptatif.
**Risque** : Peut causer des démarrages/stops fréquents si le PV est instable.
## 7. Prochaines Étapes Recommandées
1. **Décider** quelle Option (A, B, C ou D) correspond au comportement souhaité.
2. **Vérifier** la valeur exacte de `cloud_debt_max_kwh` dans HA (`input_number.victron_cloud_debt_max_kwh` ou équivalent).
3. **Implémenter** le patch dans le fichier cible :
- Si Option A : `WINDOWS/WINDOW_SOURCEV2.js`
- Si Option B : `WINDOWS/WINDOW_SOURCEV2.js` + `BATTERY_CLOUD_LOCKS.js` + `DECISION_ENGINEV2.js`
- Si Option C : `WINDOWS/WINDOW_SOURCEV2.js` + `INPUT_PARSE.js`
- Si Option D : `WINDOWS/WINDOW_SOURCEV2.js`
4. **Tester** en mode "dry-run" (logique uniquement, pas d'appels HA).
5. **Redéployer** via export/import du workflow n8n.
## 8. Paramètres Clés à Vérifier dans Home Assistant
| Paramètre HA | Entité typique | Valeur défaut | Valeur actuelle ? |
|--------------|----------------|---------------|-------------------|
| `pv_start_min_w` | `input_number.victron_pv_start_min_w` | 1800 W | ? |
| `pv_hold_min_w` | `input_number.victron_pv_hold_min_w` | 1200 W | ? |
| `cloud_debt_max_kwh` | `input_number.victron_cloud_debt_max_kwh` | 1.2 kWh | ? |
| `battery_neutral_band_a` | `input_number.victron_battery_neutral_band_a` | 3 A | ? |
| `allow_grid_hc_midday` | `input_boolean.victron_allow_grid_hc_midday` | OFF | ON (actif) |
| `allow_grid_hc_night` | `input_boolean.victron_allow_grid_hc_night` | OFF | ON (actif) |
## 9. Bugs corrigés lors du déploiement V2.6
### Bug 1 — Clignotement EDF (`edf_general_ok_switch`)
- **Symptôme** : Le switch EDF clignotait ON/OFF toutes les 30s depuis la mise à jour manuelle du workflow V2.6
- **Cause** : Dans le nœud `Build EVCS Surplus Controller V2.0 Actions`, l'appel `edf_general_ok_on` avait la condition `true` (toujours actif) au lieu d'être conditionné au `sourceMode`
- **Fix** : Remplacé `true` par `context.sourceMode === 'grid_hc_allowed' || context.sourceMode === 'grid_forced'`
- **Note** : Il a fallu aussi ajouter `const context = root.context || {};` en haut du nœud `Build` car `context` n'était pas importé
- **Validation** : Désactivation de V2.6 → arrêt du clignotement. Réactivation Grid Hysteresis seul → stable. Réactivation V2.6 corrigé → stable.
## 10. Notes de Version
- Workflow mis à jour de V2.5 → **V2.6** par l'utilisateur via l'interface n8n.
- La feature afternoon_probe y est désormais active.
- Le fichier `modified/EVCS_V2.5_PATCHED.json` reste l'export de référence pour rollback.
--- ---
*Document créé le : 12/06/2026 | Mis à jour : 12/06/2026* *Document mis à jour le : 09/07/2026 (~23:55) — session opencode avec accès HA (REST+WS+NFS) + n8n (API+MCP)*
*Session terminée : afternoon_probe déployé + bug EDF corrigé + stabilité confirmée* *État live à la clôture : SOC 65 % ↘, Jeep branchée, fenêtre HC à 01:24, nuit de validation P2+P4 en cours*
*Prochaine action : Observer le comportement en afternoon et ajuster les seuils des helpers si nécessaire*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,696 @@
{
"views": [
{
"title": "EVCS Master",
"path": "default_view",
"icon": "mdi:car-electric",
"cards": [
{
"type": "vertical-stack",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "EVCS Master",
"subtitle": "Vue borne / session / stratégie / énergie"
},
{
"type": "conditional",
"conditions": [
{
"entity": "binary_sensor.victron_evcs_vehicle_connected",
"state": "off"
}
],
"card": {
"type": "markdown",
"content": "## ℹ️ Véhicule non détecté\n"
}
},
{
"type": "conditional",
"conditions": [
{
"entity": "binary_sensor.victron_evcs_vehicle_connected",
"state": "on"
},
{
"entity": "switch.victron_evcs_charging",
"state": "off"
}
],
"card": {
"type": "markdown",
"content": "## ⚠️ Véhicule connecté mais charge inactive\n"
}
},
{
"type": "conditional",
"conditions": [
{
"entity": "switch.victron_evcs_charging",
"state": "on"
}
],
"card": {
"type": "markdown",
"content": "## ✅ Charge EVCS active\n"
}
}
]
},
{
"type": "custom:layout-card",
"layout_type": "grid",
"layout": {
"grid-template-columns": "repeat(auto-fit, minmax(380px, 1fr))",
"grid-gap": "16px",
"margin": 0
},
"cards": [
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "État borne",
"subtitle": "Statut global"
},
{
"type": "custom:mushroom-template-card",
"entity": "sensor.victron_evcs_statut_fr",
"primary": "EVCS",
"secondary": "{{ states('sensor.victron_evcs_statut_fr') }} • Mode {{ states('sensor.victron_evcs_mode_fr') }} • {{ states('sensor.victron_evcs_current') | float(0) | round(1) }} A\n",
"multiline_secondary": true,
"icon": "mdi:ev-station",
"icon_color": "{% if is_state('switch.victron_evcs_charging', 'on') %}\n green\n{% elif is_state('binary_sensor.victron_evcs_vehicle_connected', 'on') %}\n blue\n{% else %}\n grey\n{% endif %}\n"
},
{
"type": "entities",
"show_header_toggle": false,
"entities": [
{
"entity": "sensor.victron_evcs_statut_fr",
"name": "Statut FR"
},
{
"entity": "sensor.victron_evcs_mode_fr",
"name": "Mode FR"
},
{
"entity": "sensor.victron_evcs_status_raw",
"name": "Statut brut"
},
{
"entity": "sensor.victron_evcs_mode_raw",
"name": "Mode brut"
},
{
"entity": "binary_sensor.victron_evcs_device_connected",
"name": "EVCS détecté"
},
{
"entity": "binary_sensor.victron_evcs_vehicle_connected",
"name": "Véhicule connecté"
},
{
"entity": "sensor.victron_evcs_charging_time",
"name": "Durée charge"
},
{
"entity": "input_boolean.victron_evcs_surplus_auto_enabled",
"name": "Contrôleur surplus actif"
}
]
}
]
},
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "Session",
"subtitle": "Courant / puissance / énergie"
},
{
"type": "custom:gauge-card-pro",
"entity": "sensor.victron_evcs_current",
"header": "Courant EVCS",
"min": 0,
"max": 32,
"needle": true,
"segments": [
{
"from": 0,
"color": "#43a047"
},
{
"from": 6,
"color": "#fdd835"
},
{
"from": 16,
"color": "#fb8c00"
},
{
"from": 24,
"color": "#e53935"
}
]
},
{
"type": "custom:mushroom-template-card",
"primary": "Session en cours",
"secondary": "{{ states('sensor.victron_evcs_ac_power') | float(0) | round(0) }} W • {{ states('sensor.victron_evcs_current') | float(0) | round(1) }} A • Consigne {{ states('sensor.victron_evcs_set_current') | float(0) | round(1) }} A • {{ states('sensor.victron_evcs_charging_time') }}\n",
"multiline_secondary": true,
"icon": "mdi:flash",
"icon_color": "amber"
},
{
"type": "entities",
"show_header_toggle": false,
"entities": [
{
"entity": "sensor.victron_evcs_ac_power",
"name": "Puissance EVCS"
},
{
"entity": "sensor.victron_evcs_l1_power",
"name": "Puissance L1"
},
{
"entity": "sensor.victron_evcs_current",
"name": "Courant réel"
},
{
"entity": "sensor.victron_evcs_set_current",
"name": "Consigne lue"
},
{
"entity": "number.victron_evcs_set_current_command",
"name": "Consigne envoyée"
},
{
"entity": "sensor.victron_evcs_charging_time",
"name": "Durée charge"
},
{
"entity": "sensor.victron_evcs_l1_energy_forward",
"name": "Énergie session"
}
]
}
]
},
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "Stratégie & décision",
"subtitle": "Mode utilisateur / logique n8n"
},
{
"type": "custom:mushroom-template-card",
"primary": "Mode utilisateur",
"secondary": "Stratégie : {{ states('input_select.victron_evcs_strategy') }} • Profil : {{ states('input_text.victron_evcs_operating_mode') }} • Décision : {{ states('input_text.victron_evcs_surplus_current_decision') }} • Source : {{ states('input_text.victron_evcs_source_mode') }}\n",
"multiline_secondary": true,
"icon": "mdi:strategy",
"icon_color": "{% if is_state('input_select.victron_evcs_strategy', 'force_grid_now') %}\n red\n{% elif is_state('input_select.victron_evcs_strategy', 'force_charge_now') %}\n orange\n{% elif is_state('input_select.victron_evcs_strategy', 'auto_solar_plus') %}\n green\n{% elif is_state('input_select.victron_evcs_strategy', 'auto_solar') %}\n teal\n{% else %}\n grey\n{% endif %}\n"
},
{
"type": "custom:mushroom-template-card",
"primary": "Besoin véhicule",
"secondary": "Véhicule : {{ states('input_select.victron_evcs_target_vehicle') }} • {{ states('input_number.victron_evcs_required_energy_kwh') | float(0) | round(1) }} kWh • Départ : {{ states('input_datetime.victron_evcs_departure_time') }}\n",
"multiline_secondary": true,
"icon": "mdi:calendar-clock",
"icon_color": "cyan"
},
{
"type": "entities",
"title": "Paramètres utilisateur",
"show_header_toggle": false,
"entities": [
{
"entity": "input_select.victron_evcs_target_vehicle",
"name": "Véhicule cible"
},
{
"entity": "input_number.victron_evcs_required_energy_kwh",
"name": "Énergie demandée"
},
{
"entity": "input_boolean.victron_evcs_departure_priority_enabled",
"name": "Priorité départ"
},
{
"entity": "input_datetime.victron_evcs_departure_time",
"name": "Heure départ"
},
{
"entity": "input_select.victron_evcs_strategy",
"name": "Stratégie EVCS"
},
{
"entity": "input_boolean.victron_evcs_allow_grid_hc_night",
"name": "Réseau HC nuit"
},
{
"entity": "input_boolean.victron_evcs_allow_grid_hc_midday",
"name": "Réseau HC midi"
},
{
"entity": "input_boolean.victron_evcs_surplus_auto_enabled",
"name": "Contrôleur surplus actif"
}
]
},
{
"type": "entities",
"title": "Décision contrôleur",
"show_header_toggle": false,
"entities": [
{
"entity": "input_text.victron_evcs_operating_mode",
"name": "Profil actif"
},
{
"entity": "input_text.victron_evcs_surplus_state",
"name": "État interne"
},
{
"entity": "input_text.victron_evcs_surplus_current_decision",
"name": "Décision logique"
},
{
"entity": "input_text.victron_evcs_source_mode",
"name": "Source autorisée"
},
{
"entity": "input_text.victron_evcs_surplus_reason",
"name": "Raison courte"
},
{
"entity": "input_text.victron_evcs_last_block_reason",
"name": "Dernier blocage"
},
{
"entity": "input_datetime.victron_evcs_blocked_until",
"name": "Bloqué jusqu'à"
}
]
}
]
},
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "Contexte énergie",
"subtitle": "Batterie / PV / maison / réseau"
},
{
"type": "custom:mushroom-template-card",
"primary": "Contexte instantané",
"secondary": "SOC {{ states('sensor.victron_battery_soc') | float(0) | round(1) }} % • PV {{ states('sensor.victron_pv_power_system') | float(0) | round(0) }} W • Maison {{ states('sensor.victron_consumption_power_l1') | float(0) | round(0) }} W • Grid {{ states('sensor.victron_grid_power_l1') | float(0) | round(0) }} W\n",
"multiline_secondary": true,
"icon": "mdi:home-lightning-bolt",
"icon_color": "{% set pv = states('sensor.victron_pv_power_system') | float(0) %} {% if pv < 300 %} grey {% elif pv < 1500 %} yellow {% else %} green {% endif %}\n"
},
{
"type": "entities",
"show_header_toggle": false,
"entities": [
{
"entity": "sensor.victron_battery_soc",
"name": "SOC batterie"
},
{
"entity": "sensor.victron_battery_current",
"name": "Courant batterie"
},
{
"entity": "sensor.victron_pv_power_system",
"name": "Puissance PV"
},
{
"entity": "sensor.victron_consumption_power_l1",
"name": "Charges maison"
},
{
"entity": "sensor.victron_grid_power_l1",
"name": "Puissance réseau"
},
{
"entity": "switch.edf_general_ok_switch",
"name": "Réseau EDF"
}
]
}
]
},
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "Actions rapides",
"subtitle": "Contrôle direct EVCS"
},
{
"type": "grid",
"columns": 2,
"square": false,
"cards": [
{
"type": "custom:button-card",
"entity": "switch.victron_evcs_charging",
"name": "Charge",
"icon": "mdi:ev-station",
"show_state": true,
"state_display": "[[[\n if (!entity) return 'Inconnu';\n if (entity.state === 'on') return 'ON';\n if (entity.state === 'off') return 'OFF';\n return entity.state;\n]]]\n",
"tap_action": {
"action": "toggle"
},
"hold_action": {
"action": "more-info"
},
"styles": {
"card": [
{
"border-radius": "18px"
},
{
"padding": "12px"
}
],
"name": [
{
"font-size": "15px"
},
{
"font-weight": 600
}
],
"state": [
{
"font-size": "12px"
}
]
},
"state": [
{
"value": "on",
"color": "#43a047"
},
{
"value": "off",
"color": "#757575"
}
]
},
{
"type": "custom:button-card",
"entity": "input_boolean.victron_evcs_surplus_auto_enabled",
"name": "Contrôleur surplus",
"icon": "mdi:robot",
"show_state": true,
"tap_action": {
"action": "toggle"
},
"hold_action": {
"action": "more-info"
},
"styles": {
"card": [
{
"border-radius": "18px"
},
{
"padding": "12px"
}
],
"name": [
{
"font-size": "15px"
},
{
"font-weight": 600
}
],
"state": [
{
"font-size": "12px"
}
]
},
"state": [
{
"value": "on",
"color": "#26a69a"
},
{
"value": "off",
"color": "#757575"
}
]
},
{
"type": "custom:button-card",
"entity": "binary_sensor.victron_evcs_vehicle_connected",
"name": "Véhicule",
"icon": "mdi:car-electric",
"show_state": true,
"tap_action": {
"action": "more-info"
},
"styles": {
"card": [
{
"border-radius": "18px"
},
{
"padding": "12px"
}
],
"name": [
{
"font-size": "15px"
},
{
"font-weight": 600
}
],
"state": [
{
"font-size": "12px"
}
]
},
"state": [
{
"value": "on",
"color": "#1e88e5"
},
{
"value": "off",
"color": "#757575"
}
]
},
{
"type": "custom:button-card",
"entity": "number.victron_evcs_set_current_command",
"name": "Courant demandé",
"icon": "mdi:current-ac",
"show_state": true,
"tap_action": {
"action": "more-info"
},
"hold_action": {
"action": "more-info"
},
"styles": {
"card": [
{
"border-radius": "18px"
},
{
"padding": "12px"
}
],
"name": [
{
"font-size": "15px"
},
{
"font-weight": 600
}
],
"state": [
{
"font-size": "12px"
}
]
}
}
]
},
{
"type": "entities",
"title": "Commandes manuelles",
"show_header_toggle": false,
"entities": [
{
"entity": "switch.victron_evcs_charging",
"name": "Start / Stop charge"
},
{
"entity": "number.victron_evcs_set_current_command",
"name": "Courant demandé"
},
{
"entity": "input_boolean.victron_evcs_charge_permission_now",
"name": "Permission charge"
},
{
"entity": "input_boolean.victron_evcs_control_latch",
"name": "Latch contrôle"
},
{
"entity": "switch.victron_evcs_auto_start",
"name": "Auto Start borne (diagnostic)"
}
]
}
]
},
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "Diagnostic",
"subtitle": "Identité / version / brut"
},
{
"type": "entities",
"show_header_toggle": false,
"entities": [
{
"entity": "sensor.victron_evcs_product_name",
"name": "Produit"
},
{
"entity": "sensor.victron_evcs_custom_name",
"name": "Nom borne"
},
{
"entity": "sensor.victron_evcs_serial",
"name": "Numéro de série"
},
{
"entity": "sensor.victron_evcs_firmware_version",
"name": "Firmware"
},
{
"entity": "sensor.victron_evcs_connected_raw",
"name": "Connected brut"
},
{
"entity": "sensor.victron_evcs_status_raw",
"name": "Statut brut"
},
{
"entity": "sensor.victron_evcs_mode_raw",
"name": "Mode brut"
},
{
"entity": "switch.victron_evcs_auto_start",
"name": "Auto Start borne"
},
{
"entity": "input_datetime.victron_evcs_last_start_at",
"name": "Dernier start"
},
{
"entity": "input_datetime.victron_evcs_last_stop_at",
"name": "Dernier stop"
},
{
"entity": "input_datetime.victron_evcs_last_current_change_at",
"name": "Dernier changement courant"
},
{
"entity": "input_datetime.victron_evcs_probe_started_at",
"name": "Début probe"
},
{
"entity": "input_datetime.victron_evcs_cloud_debt_started_at",
"name": "Début cloud debt"
},
{
"entity": "input_number.victron_evcs_cloud_debt_kwh",
"name": "Cloud debt"
},
{
"entity": "input_text.victron_evcs_control_last_signature",
"name": "Signature"
}
]
}
]
}
]
},
{
"type": "custom:vertical-stack-in-card",
"cards": [
{
"type": "custom:mushroom-title-card",
"title": "Historique EVCS",
"subtitle": "12 dernières heures"
},
{
"type": "custom:apexcharts-card",
"graph_span": "12h",
"header": {
"show": true,
"title": "Session EVCS",
"show_states": false
},
"series": [
{
"entity": "sensor.victron_evcs_current",
"name": "Courant EVCS",
"color": "#42a5f5"
},
{
"entity": "sensor.victron_evcs_set_current",
"name": "Consigne lue",
"color": "#ffca28"
},
{
"entity": "sensor.victron_evcs_ac_power",
"name": "Puissance EVCS",
"color": "#66bb6a"
},
{
"entity": "sensor.victron_pv_power_system",
"name": "PV",
"color": "#fdd835"
},
{
"entity": "sensor.victron_grid_power_l1",
"name": "Réseau",
"color": "#ef5350"
}
]
}
]
}
]
}
]
}