Nuit : la recovery batterie est une passe unique, pas un oscillateur
La bande de 2 points du Fix A engageait le takeover a SOC 33 et le relachait a 35 : 0,34 kWh, soit une demi-heure de conso nocturne plate. Trois cycles EDF la nuit du 26-27/08, six la nuit du 20-21/08, 2,5 kWh importes pour rien. Une seule passe de recovery par session de nuit. Le plancher dur reste un terme independant, l hysteresis garde sa bande 20/30, et le vrai critere de re-engagement (un budget d energie) appartient au bilan, pas a une constante de plus ici. Golden 40 -> 42, preuve par mutation. Documente aussi deux acteurs non lus : les charges programmees de l ESS (45 % la nuit, contre notre cible 35 %) et le regime majoritairement hors reseau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
88
HANDOFF.md
88
HANDOFF.md
@@ -1109,6 +1109,94 @@ p11-changement-vehicule-nuit (prev=night_recovery_done_idle + charging_time 15s
|
||||
2. CHANGEMENT DE VÉHICULE / nouvelle connexion (charging_time ≤ 90 s, P11) ;
|
||||
3. force_charge_now / force_grid_now (échappatoires manuelles).
|
||||
|
||||
|
||||
## 26. Session du 27/08/2026 — L'oscillateur du takeover nuit, et deux acteurs non lus
|
||||
|
||||
### L'oscillateur (corrigé, golden 40 → 42, déployé 08:32)
|
||||
Nuit du 26-27/08, Jeep pleine restée branchée, aucun besoin de charge : trois
|
||||
sessions EDF (04:24→05:16, 05:31→06:50, 07:05→07:30), **2,5 kWh importés** un
|
||||
matin annoncé à 43 kWh de solaire. La nuit du 20-21/08 en comptait six. Le
|
||||
helper `night_grid_takeover_needed` précède chaque bascule du contacteur de 3 s :
|
||||
la cause est entièrement dans le moteur de nuit EVCS, pas dans l'hystérésis
|
||||
(`grid_soc_on`/`grid_soc_off` valent toujours 20/30, jamais approchés).
|
||||
|
||||
Cause, dans `WINDOWS_SOURCEV2.js` :
|
||||
```
|
||||
gridTakeoverTriggerPct = max(plancher 22, cible 35) = 35 → engage si SOC <= 35
|
||||
nightRecoveryDone : SOC >= 35 → relâche
|
||||
NIGHT_RECOVERY_REARM_BAND_PCT = 2 → réengage à 33
|
||||
```
|
||||
Engagement et relâchement sur **le même nombre**, la bande de 2 points du Fix A
|
||||
ne faisant que retarder le réarmement. Or 2 points de SOC ≈ 0,34 kWh ≈ 31 min à
|
||||
la charge nocturne (~650 W, parfaitement plate) : ce n'est pas une hystérésis,
|
||||
c'est la période d'une horloge. C'est le propriétaire qui a mis le doigt dessus
|
||||
en observant que le phénomène était « super stable car pas de variation de
|
||||
consommation » — la platitude de la charge est ce qui rend le cycle régulier.
|
||||
|
||||
**Correctif** : la recovery batterie post-charge est UNE SEULE passe par session
|
||||
de nuit (`nightRecoveryDone = ... || nightRecoveryDonePrev`, sans bande). C'était
|
||||
déjà l'intention affichée du Fix A (« relâché DÉFINITIVEMENT »). Aucun seuil de
|
||||
ré-engagement n'est ajouté : `batteryBelowHardFloor` reste un terme indépendant
|
||||
de `nightGridTakeoverNeeded`, et l'hystérésis garde sa bande 20/30. Le vrai
|
||||
critère de ré-engagement est un budget d'énergie restant à couvrir — il
|
||||
appartient au bilan énergétique, pas à une constante de plus ici.
|
||||
|
||||
Golden : `night-recovery-rearm-sous-bande` (qui épinglait l'oscillateur) devient
|
||||
`night-recovery-done-pas-de-reengagement` ; +`night-oscillateur-nuit-2627`
|
||||
(le relevé exact de la nuit) ; +`night-recovery-done-plancher-dur` (le filet
|
||||
de sécurité doit rester : SOC 21 → ré-engagement). Preuve par mutation : bande
|
||||
restaurée → échec du nouveau scénario ; retirée → 42/42.
|
||||
|
||||
### D'où venait le 35 %, et pourquoi ce n'est pas une réserve
|
||||
`batteryMorningTargetPct` vient des règles de prévision (≥ 18 kWh → 35 %,
|
||||
≥ 10 → 55 %, sinon 85 %). C'est une **optimisation économique** — « demain il
|
||||
fera beau, la batterie peut descendre » — et non une réserve de sécurité, ce que
|
||||
le propriétaire croyait. Point de conception qui en découle : **une réserve
|
||||
contre la perte du réseau ne peut pas être constituée avec le réseau** ; elle se
|
||||
charge sur le solaire et se décide le soir. Voir §27 (à écrire) pour la réserve
|
||||
d'orage.
|
||||
|
||||
### Acteur non lu n°1 — les charges programmées de l'ESS (IMPORTANT)
|
||||
Le GX a **trois fenêtres de charge réseau programmées, actives tous les jours**,
|
||||
que rien dans nos workflows ne lit :
|
||||
|
||||
| # | Départ | Durée | Cible SOC | Actif |
|
||||
|---|---|---|---|---|
|
||||
| 0 | 01:35 | 365 min (→ 07:40) | **45 %** | on |
|
||||
| 1 | 07:30 | 298 min (→ 12:28) | **45 %** | on |
|
||||
| 2 | 12:24 | 120 min (→ 14:24) | **55 %** | on |
|
||||
| 3 | 14:24 | 120 min | 85 % | off |
|
||||
| 4 | 23:08 | 60 min | 50 % | off |
|
||||
|
||||
Entités : `switch.gx_device_ess_batterylife_schedule_charge_N_enabled`,
|
||||
`time..._start`, `number..._duration`, `number..._soc`.
|
||||
`sensor.gx_device_ess_batterylife_state` = `self_consumption` hors réseau.
|
||||
Le GX veut donc 45 % la nuit quand EDF est présent, quand notre cible dit 35 % :
|
||||
**deux automates avec deux consignes différentes sur la même batterie**. À
|
||||
arbitrer (les désactiver et garder un seul propriétaire, ou les aligner).
|
||||
|
||||
### Acteur non lu n°2 — l'export d'excédent PV
|
||||
`switch.gx_device_dc_coupled_pv_feed_in_excess` = off ·
|
||||
`switch.gx_device_ac_coupled_pv_feed_in_excess` = off · `ess_max_feed_in_power`
|
||||
= −1. Cohérent avec « sans injection », à confirmer comme voulu.
|
||||
|
||||
### Ce qui n'est PAS démontré : le bridage du MPPT par la présence d'EDF
|
||||
Tentative de preuve sur la seule fenêtre EDF-ON diurne conservée en base
|
||||
(21/08, 07:24→08:07) : PV moyenne 91 W (max 218) contre **907 W** (max 1464)
|
||||
sur les 43 min suivantes, EDF coupé, à consommation identique (668 vs 637 W).
|
||||
Écart spectaculaire — **mais l'enveloppe solaire passe elle-même de 0,17 kWh à
|
||||
07 h à 1,03 kWh à 08 h**, soit un facteur 6 qui explique l'essentiel. La mesure
|
||||
est donc confondue par le lever du soleil et **ne prouve rien**. Ne pas décider
|
||||
d'une mise à jour firmware sur cette base. Test propre à faire : fermer EDF
|
||||
10 min en milieu de journée, batterie non pleine, et comparer.
|
||||
|
||||
### Historique HA plus court que configuré
|
||||
`configuration.yaml` déclare `purge_keep_days: 90`, mais l'historique brut
|
||||
s'arrête au **20/08** (~7 jours) : testé vide au 01/06, 29/07, 10/08, 17/08 ;
|
||||
1 point au 20/08, 7 au 24/08. Base recréée, ou purge qui ne respecte pas la
|
||||
consigne — à investiguer. Les statistiques long terme (horaires, conservées
|
||||
indéfiniment) sont un stock distinct et probablement intactes.
|
||||
|
||||
## Diagnostic en attente de vérification (Qwen, 22/08)
|
||||
Voici mon analyse et plan de correction :
|
||||
Diagnostic
|
||||
|
||||
@@ -93,6 +93,31 @@ batterie n'est publié côté HA — à corriger.
|
||||
Deux véhicules alternent : **VW** (hybride, départ ~08:50) et **JEEP**
|
||||
(départ ~06:30) — cf. §11.5.
|
||||
|
||||
### Régime d'exploitation — majoritairement HORS RÉSEAU
|
||||
Précision donnée par le propriétaire le 27/08/2026, à ne pas redécouvrir :
|
||||
l'installation fonctionne **la plupart du temps AC-IN ouvert**
|
||||
(`switch.edf_general_ok_switch` fermé seulement lors des sessions réseau
|
||||
décidées par les workflows). Conséquence assumée : **l'alarme perte-réseau du
|
||||
MultiPlus (`switch.airtria_mpii_v1_grid_lost_alarm_setting`) est désactivée
|
||||
volontairement** — avec cette topologie elle se déclencherait en permanence. Ne
|
||||
pas proposer de la réactiver.
|
||||
|
||||
Pour détecter une **vraie coupure subie** (orage), le signal correct n'est donc
|
||||
pas l'alarme mais la contradiction commande/mesure : contacteur EDF commandé
|
||||
fermé alors que `sensor.victron_ac_in1_available` reste à 0 ou que
|
||||
`sensor.airtria_mpii_v1_input_voltage_l1` reste nulle. Entièrement local.
|
||||
|
||||
Les coupures d'orage sont **fréquentes** sur ce site, et emportent souvent la
|
||||
connexion Internet avec elles — d'où l'exigence d'outils consultables hors
|
||||
ligne (la console VRM est hébergée dans le cloud Victron).
|
||||
|
||||
### Charges programmées de l'ESS (propriétaire concurrent — 27/08/2026)
|
||||
Le GX porte ses propres fenêtres de charge réseau, **trois actives tous les
|
||||
jours**, qu'aucun workflow ne lit : 01:35 → 45 %, 07:30 → 45 %, 12:24 → 55 %
|
||||
(plus deux désactivées). Elles entrent en concurrence directe avec la cible du
|
||||
matin calculée par l'EVCS (35 % en été). Entités
|
||||
`switch.gx_device_ess_batterylife_schedule_charge_N_enabled` et associées.
|
||||
|
||||
### Réseau
|
||||
Victron **ESS sans injection** : la production est bridée en permanence à ce que
|
||||
le système peut absorber (consommation + charge batterie). Voir
|
||||
|
||||
@@ -394,17 +394,24 @@ const vehicleChargeCompleted =
|
||||
// ⚠ 'night_recovery_done_idle' ne doit matcher AUCUN pattern no-demand de
|
||||
// l'hystérésis (full_or_paused / not_requesting_power / charge_complete /
|
||||
// no_demand) — même contrainte que 'night_session_done_idle'.
|
||||
const NIGHT_RECOVERY_REARM_BAND_PCT = 2;
|
||||
// 27/08/2026 : la bande de 2 points fabriquait un oscillateur. Relevé de la
|
||||
// nuit du 26-27/08 (cible 35) : engagement à SOC 33, relâchement à 35, trois
|
||||
// cycles EDF de 52, 79 et 25 min, 2,5 kWh importés pour rien un matin de grand
|
||||
// soleil ; la nuit du 20-21/08 en comptait six. Deux points de SOC valent
|
||||
// ~0,34 kWh, soit une demi-heure de conso nocturne : ce n'est pas une
|
||||
// hystérésis, c'est la période d'une horloge — d'autant plus régulière que la
|
||||
// charge de nuit est parfaitement plate.
|
||||
// La recovery batterie post-charge est donc UNE SEULE passe par session de
|
||||
// nuit, ce qui était déjà l'intention affichée du Fix A ("relâché
|
||||
// DÉFINITIVEMENT"). Aucun seuil de ré-engagement ici : le plancher dur reste
|
||||
// un terme indépendant de nightGridTakeoverNeeded (voir plus bas), et
|
||||
// l'hystérésis réseau garde sa propre bande 20/30. Le vrai critère de
|
||||
// ré-engagement est un budget d'énergie restant à couvrir, pas un delta de
|
||||
// SOC : il appartient au bilan énergétique, pas à une constante de plus ici.
|
||||
const nightRecoveryDonePrev = prevSurplusState === 'night_recovery_done_idle';
|
||||
const nightRecoveryDone =
|
||||
vehicleChargeCompleted &&
|
||||
(
|
||||
parsed.soc >= batteryMorningTargetPct ||
|
||||
(
|
||||
nightRecoveryDonePrev &&
|
||||
parsed.soc > batteryMorningTargetPct - NIGHT_RECOVERY_REARM_BAND_PCT
|
||||
)
|
||||
);
|
||||
(parsed.soc >= batteryMorningTargetPct || nightRecoveryDonePrev);
|
||||
|
||||
// =====================================================
|
||||
// P9 (09/08/2026) : CHARGE RÉSEAU AVANT DÉPART, HORS FENÊTRE HC.
|
||||
|
||||
@@ -582,8 +582,8 @@ export const scenarios = [
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'night-recovery-rearm-sous-bande',
|
||||
label: 'Nuit HC 05:30, recovery done mais SOC 32 ≤ cible−2 → ré-engagement takeover (recharge batterie)',
|
||||
id: 'night-recovery-done-pas-de-reengagement',
|
||||
label: 'Nuit HC 05:30, recovery done et SOC 32 sous la cible → AUCUN ré-engagement (la recovery est une passe unique, 27/08)',
|
||||
time: '2026-08-08T05:30:00+02:00',
|
||||
overrides: {
|
||||
...VEHICLE_NIGHT, soc_percent: '32', allow_grid_hc_night: 'on',
|
||||
@@ -595,15 +595,65 @@ export const scenarios = [
|
||||
expect: {
|
||||
profile: 'night_hc',
|
||||
sourceMode: 'blocked',
|
||||
state: 'night_session_done_idle',
|
||||
// Collant désormais : la recovery ayant eu lieu, l'état reste
|
||||
// 'night_recovery_done_idle' même sous la cible — c'est lui qui autorise
|
||||
// la coupure EDF immédiate côté hystérésis (E2) dès SOC >= grid_soc_off.
|
||||
state: 'night_recovery_done_idle',
|
||||
decision: 'hold',
|
||||
desiredCharging: false,
|
||||
currentA: 6,
|
||||
takeoverNeeded: true,
|
||||
takeoverNeeded: false,
|
||||
edfOnAction: false,
|
||||
takeoverHelperService: 'turn_off',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// L'oscillateur mesuré : nuit du 26-27/08, Jeep pleine restée branchée,
|
||||
// cible 35. L'ancienne bande de 2 points ré-engageait le takeover pile à
|
||||
// SOC 33 -> EDF ON 04:24/05:31/07:05, OFF 05:16/06:50/07:30, 2,5 kWh
|
||||
// importés une nuit où le lendemain était annoncé à 43 kWh.
|
||||
id: 'night-oscillateur-nuit-2627',
|
||||
label: 'Nuit HC 05:31, recovery done et SOC 33 (cible 35 − 2) → le takeover NE se ré-engage pas : plus d\'oscillateur',
|
||||
time: '2026-08-27T05:31:00+02:00',
|
||||
overrides: {
|
||||
...VEHICLE_NIGHT, soc_percent: '33', allow_grid_hc_night: 'on',
|
||||
grid_state: 'off', victron_acin1_available: '0',
|
||||
battery_current_a: '-11', night_takeover_prev: 'off',
|
||||
evcs_charging_switch: 'off',
|
||||
prev_surplus_state: 'night_recovery_done_idle',
|
||||
},
|
||||
expect: {
|
||||
profile: 'night_hc',
|
||||
sourceMode: 'blocked',
|
||||
state: 'night_recovery_done_idle',
|
||||
takeoverNeeded: false,
|
||||
edfOnAction: false,
|
||||
takeoverHelperService: 'turn_off',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Le filet de sécurité doit rester intact : sous le plancher dur, le
|
||||
// takeover se ré-engage même si la recovery a déjà eu lieu, parce que
|
||||
// batteryBelowHardFloor est un terme INDÉPENDANT de nightGridTakeoverNeeded.
|
||||
id: 'night-recovery-done-plancher-dur',
|
||||
label: 'Nuit HC 05:31, recovery done mais SOC 21 sous le plancher dur 22 → ré-engagement du takeover (sécurité)',
|
||||
time: '2026-08-27T05:31:00+02:00',
|
||||
overrides: {
|
||||
...VEHICLE_NIGHT, soc_percent: '21', allow_grid_hc_night: 'on',
|
||||
grid_state: 'off', victron_acin1_available: '0',
|
||||
battery_current_a: '-11', night_takeover_prev: 'off',
|
||||
evcs_charging_switch: 'off',
|
||||
prev_surplus_state: 'night_recovery_done_idle',
|
||||
},
|
||||
expect: {
|
||||
profile: 'night_hc',
|
||||
takeoverNeeded: true,
|
||||
takeoverHelperService: 'turn_on',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: 'night-vw-statut-charged',
|
||||
label: 'Nuit HC 03:00, statut EVCS brut 3 (charged), sans état VDF préalable → fin de charge immédiate (Fix D)',
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user