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) ;
|
2. CHANGEMENT DE VÉHICULE / nouvelle connexion (charging_time ≤ 90 s, P11) ;
|
||||||
3. force_charge_now / force_grid_now (échappatoires manuelles).
|
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)
|
## Diagnostic en attente de vérification (Qwen, 22/08)
|
||||||
Voici mon analyse et plan de correction :
|
Voici mon analyse et plan de correction :
|
||||||
Diagnostic
|
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**
|
Deux véhicules alternent : **VW** (hybride, départ ~08:50) et **JEEP**
|
||||||
(départ ~06:30) — cf. §11.5.
|
(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
|
### Réseau
|
||||||
Victron **ESS sans injection** : la production est bridée en permanence à ce que
|
Victron **ESS sans injection** : la production est bridée en permanence à ce que
|
||||||
le système peut absorber (consommation + charge batterie). Voir
|
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
|
// ⚠ 'night_recovery_done_idle' ne doit matcher AUCUN pattern no-demand de
|
||||||
// l'hystérésis (full_or_paused / not_requesting_power / charge_complete /
|
// l'hystérésis (full_or_paused / not_requesting_power / charge_complete /
|
||||||
// no_demand) — même contrainte que 'night_session_done_idle'.
|
// 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 nightRecoveryDonePrev = prevSurplusState === 'night_recovery_done_idle';
|
||||||
const nightRecoveryDone =
|
const nightRecoveryDone =
|
||||||
vehicleChargeCompleted &&
|
vehicleChargeCompleted &&
|
||||||
(
|
(parsed.soc >= batteryMorningTargetPct || nightRecoveryDonePrev);
|
||||||
parsed.soc >= batteryMorningTargetPct ||
|
|
||||||
(
|
|
||||||
nightRecoveryDonePrev &&
|
|
||||||
parsed.soc > batteryMorningTargetPct - NIGHT_RECOVERY_REARM_BAND_PCT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// P9 (09/08/2026) : CHARGE RÉSEAU AVANT DÉPART, HORS FENÊTRE HC.
|
// 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',
|
id: 'night-recovery-done-pas-de-reengagement',
|
||||||
label: 'Nuit HC 05:30, recovery done mais SOC 32 ≤ cible−2 → ré-engagement takeover (recharge batterie)',
|
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',
|
time: '2026-08-08T05:30:00+02:00',
|
||||||
overrides: {
|
overrides: {
|
||||||
...VEHICLE_NIGHT, soc_percent: '32', allow_grid_hc_night: 'on',
|
...VEHICLE_NIGHT, soc_percent: '32', allow_grid_hc_night: 'on',
|
||||||
@@ -595,15 +595,65 @@ export const scenarios = [
|
|||||||
expect: {
|
expect: {
|
||||||
profile: 'night_hc',
|
profile: 'night_hc',
|
||||||
sourceMode: 'blocked',
|
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',
|
decision: 'hold',
|
||||||
desiredCharging: false,
|
desiredCharging: false,
|
||||||
currentA: 6,
|
currentA: 6,
|
||||||
takeoverNeeded: true,
|
takeoverNeeded: false,
|
||||||
edfOnAction: 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',
|
takeoverHelperService: 'turn_on',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'night-vw-statut-charged',
|
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)',
|
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