P12 — la voiture se charge sur le solaire d hier, pas sur le reseau

Idee du proprietaire, apres la nuit du 14 au 15/09 : 8 kWh achetes au reseau, la
batterie encore a 69 % a 5 h du matin, et une prevision de 38,7 kWh le lendemain
dont 10,5 auraient ete ecretes faute de place. Payer du reseau la nuit pour
ecreter le solaire le lendemain, c est perdre deux fois.

Le mode `battery_assist_night` existait pour exactement ca, et etait MORT depuis
toujours : il exige `batteryCurrentA >= plancher de decharge` (0 A), impossible
la nuit puisque la maison tire sur la batterie. On ne cree donc pas un chemin,
on rend viable celui qui existe — en remplacant un test de COURANT impossible
par un test d ENERGIE.

Le plancher n est PAS une constante nouvelle : c est `required_soc_prudent`, que
le bilan publie deja en profil p80. La voiture descend la batterie jusqu au
niveau qui lui permet encore de tenir jusqu au croisement PV, et pas plus bas.
La bascule vers le reseau est automatique quand le surplus est epuise — la
soupape decrite par le proprietaire, sans aucune regle horaire.

⚠️ Verrou anti-oscillateur, et il n est pas decoratif. Le plancher descend
pendant la nuit et le SOC aussi : voiture en charge le SOC perd 7,1 pt/h contre
3,7 pour le plancher, mais voiture ARRETEE il n en perd que 2,6. Le surplus
reapparaitrait tout seul et la charge repartirait. Une seule passe par session
de nuit, comme la recovery batterie depuis le 27/08.

Les gardes de courant restent : commonSafety, l arret dur, et la garde souple
avec sa tolerance temporelle — c est elle qui arretera la charge quand la bande
de SOC deviendra trop basse pour le courant tire. Ce qu on retire est uniquement
le plancher de PROFIL, qui interdisait de demarrer des que la batterie n etait
pas a l equilibre.

Cinq scenarios, six mutations qui mordent chacune sur un jeu different. Deux
d entre elles ont trouve de vraies faiblesses : les attendus ne verifiaient que
le choix du mode et pas que la voiture charge, et la garde de fraicheur du bilan
n etait couverte par aucun scenario.

Rien n est deploye et le toggle est a off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
Tallagrand Thierry
2026-09-15 05:05:17 +02:00
parent 66daac2933
commit 26bdabf979
5 changed files with 222 additions and 11 deletions

View File

@@ -820,14 +820,44 @@ const gridHoldOk =
parsed.gridAvailable && parsed.gridAvailable &&
parsed.batteryCurrentA >= gridBatteryHardDynamicA; parsed.batteryCurrentA >= gridBatteryHardDynamicA;
// P12 (15/09/2026) — le mode `battery_assist_night` etait MORT depuis toujours.
// Il exige `batteryCurrentA >= plancher de decharge du profil` (0 A), condition
// impossible la nuit puisque la maison tire sur la batterie (§11, ~ -14 A). Il
// n'a donc jamais demarre une seule fois.
//
// Quand P12 est actif, on remplace ce test de COURANT par un test d'ENERGIE :
// rester au-dessus de ce qu'il faut pour tenir jusqu'au croisement PV. Le
// plancher vient du bilan (`required_soc_prudent`, profil p80), il est calcule
// dans WINDOWS_SOURCEV2 et transmis ici — ce node CONSOMME, il ne recalcule pas.
//
// ⚠️ LES GARDES DE COURANT NE SONT PAS SUPPRIMEES POUR AUTANT :
// - `commonSafetyStartOk` / `commonSafetyHoldOk` restent evalues normalement ;
// - le MAINTIEN garde `batteryDischargeHardDynamicA`, l'arret dur, qui n'est
// jamais contourne ;
// - la garde SOUPLE et sa tolerance temporelle (§35) continuent de s'appliquer
// par `batterySoftDischargeHoldOk` en aval, et c'est elle qui arretera la
// charge quand la bande de SOC deviendra trop basse pour le courant tire.
// Ce qu'on retire, c'est uniquement le plancher de PROFIL, qui interdisait de
// demarrer quoi que ce soit des que la batterie n'etait pas a l'equilibre.
const nightBatterySurplusAvailable = !!nightPrev.nightBatterySurplusAvailable;
const nightBatterySurplusFloorPct = Number.isFinite(nightPrev.nightBatterySurplusFloorPct)
? nightPrev.nightBatterySurplusFloorPct
: Infinity;
const batteryAssistNightStartOk = const batteryAssistNightStartOk =
context.sourceMode === 'battery_assist_night' && context.sourceMode === 'battery_assist_night' &&
commonSafetyStartOk && commonSafetyStartOk &&
nightSessionActive && nightSessionActive &&
batteryAssistAllowed && batteryAssistAllowed &&
parsed.soc > batteryHardFloorPct && parsed.soc > batteryHardFloorPct &&
parsed.soc > gridTakeoverTriggerPct && (
parsed.batteryCurrentA >= profileBatteryDischargeFloorA; nightBatterySurplusAvailable
? parsed.soc > nightBatterySurplusFloorPct
: (
parsed.soc > gridTakeoverTriggerPct &&
parsed.batteryCurrentA >= profileBatteryDischargeFloorA
)
);
const batteryAssistNightHoldOk = const batteryAssistNightHoldOk =
context.sourceMode === 'battery_assist_night' && context.sourceMode === 'battery_assist_night' &&
@@ -835,8 +865,12 @@ const batteryAssistNightHoldOk =
nightSessionActive && nightSessionActive &&
batteryAssistAllowed && batteryAssistAllowed &&
parsed.soc > batteryHardFloorPct && parsed.soc > batteryHardFloorPct &&
parsed.soc > gridTakeoverTriggerPct && parsed.batteryCurrentA >= batteryDischargeHardDynamicA &&
parsed.batteryCurrentA >= batteryDischargeHardDynamicA; (
nightBatterySurplusAvailable
? parsed.soc > nightBatterySurplusFloorPct
: parsed.soc > gridTakeoverTriggerPct
);
// ===================================================== // =====================================================
// PROBE LOGIC — STRICTEMENT LIMITÉE AU MODE PROBE // PROBE LOGIC — STRICTEMENT LIMITÉE AU MODE PROBE
@@ -1082,6 +1116,9 @@ return [{
batteryMorningTargetPct, batteryMorningTargetPct,
balanceMorningTargetApplied, balanceMorningTargetApplied,
nightGridSessionCommitted, nightGridSessionCommitted,
// P12 — passe-plat du verrou anti-oscillateur vers la sortie.
nightBatterySurplusExhausted: !!nightPrev.nightBatterySurplusExhausted,
nightBatterySurplusAvailable: !!nightPrev.nightBatterySurplusAvailable,
maxBatteryDischargePoints, maxBatteryDischargePoints,
gridTakeoverTriggerPct, gridTakeoverTriggerPct,
@@ -1156,6 +1193,8 @@ return [{
gridHoldOk, gridHoldOk,
batteryAssistNightStartOk, batteryAssistNightStartOk,
nightBatterySurplusAvailable,
nightBatterySurplusFloorPct,
batteryAssistNightHoldOk, batteryAssistNightHoldOk,
probeStartOk, probeStartOk,

View File

@@ -1163,6 +1163,26 @@ return [{
} }
}, },
// P12 — LE VERROU ANTI-OSCILLATEUR.
//
// Le plancher de surplus (`required_soc_prudent`) DESCEND pendant la nuit,
// et le SOC aussi. Mesure du 15/09 : voiture en charge, le SOC descend de
// 7,1 pt/h et le plancher de 3,7 ; voiture ARRETEE, le SOC ne descend plus
// que de 2,6 pt/h quand le plancher continue a 3,7. Le surplus
// REAPPARAITRAIT donc tout seul et la charge repartirait — cinquieme
// oscillateur du projet, par construction.
//
// Une seule passe de surplus par session de nuit, comme la recovery
// batterie depuis le 27/08. Ecrit uniquement sur changement reel.
helper_night_battery_done: {
execute: !!night.nightBatterySurplusExhausted !== !!params.nightBatteryDonePrev,
domain: 'input_boolean',
service: night.nightBatterySurplusExhausted ? 'turn_on' : 'turn_off',
data: {
entity_id: 'input_boolean.victron_evcs_night_battery_done'
}
},
helper_night_grid_takeover_needed: { helper_night_grid_takeover_needed: {
execute: true, execute: true,
domain: 'input_boolean', domain: 'input_boolean',

View File

@@ -604,8 +604,10 @@ const nightBatterySurplusAvailable =
balanceMorningTargetApplied && balanceMorningTargetApplied &&
Number.isFinite(params.balanceRequiredSocPrudentPct) && Number.isFinite(params.balanceRequiredSocPrudentPct) &&
!vehicleChargeCompleted && !vehicleChargeCompleted &&
!nightBatterySurplusExhausted && // Le plancher est teste UNE SEULE FOIS, dans `nightBatterySurplusExhausted`.
parsed.soc > nightBatterySurplusFloorPct; // Le retester ici serait redondant — et une redondance ne se teste pas : la
// mutation du 15/09 l'a montre en ne cassant aucun scenario.
!nightBatterySurplusExhausted;
// La batterie d'abord, le reseau quand le surplus est epuise. C'est la soupape // La batterie d'abord, le reseau quand le surplus est epuise. C'est la soupape
// decrite par le proprietaire — « quitte a fermer EDF entre 6h et 7h24 pour // decrite par le proprietaire — « quitte a fermer EDF entre 6h et 7h24 pour

View File

@@ -2462,4 +2462,154 @@ export const scenarios = [
decision: 'stop_hard', decision: 'stop_hard',
}, },
}, },
// =====================================================
// P12 — CHARGE NUIT SUR LA BATTERIE (15/09/2026)
// =====================================================
// Idee du proprietaire : plutot qu'acheter du reseau la nuit pour charger la
// voiture, lui donner le solaire DEJA STOCKE, et laisser ainsi de la place
// dans la batterie pour la production du lendemain. Releve de sa nuit du 14
// au 15/09 : 8 kWh achetes au reseau, batterie encore a 69 % a 5 h du matin,
// et une prevision de 38,7 kWh le lendemain dont 10,5 auraient ete ecretes.
//
// Le mode `battery_assist_night` existait pour exactement ca et etait MORT
// depuis toujours : il exigeait `batteryCurrentA >= 0`, impossible la nuit
// puisque la maison tire sur la batterie. P12 remplace ce test de COURANT par
// un test d'ENERGIE — rester au-dessus de `required_soc_prudent`, que le
// bilan publie deja. Aucune constante nouvelle.
{
id: 'p12-charge-nuit-sur-batterie',
label: 'Nuit HC, P12 ON, SOC 69 bien au-dessus du plancher 38,7 -> la voiture puise dans la batterie',
time: '2026-09-15T02:30:00+02:00',
overrides: {
...VEHICLE_NIGHT, soc_percent: '69',
allow_grid_hc_night: 'on',
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
grid_state: 'off', victron_acin1_available: '0',
battery_current_a: '-16',
evcs_charging_switch: 'off',
balance_status: 'ok', balance_last_run: '15/09 02:25',
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
balance_energy_to_cover_kwh: '3.18',
},
expect: {
profile: 'night_hc',
sourceMode: 'battery_assist_night',
// ⚠️ Verifier que la voiture CHARGE REELLEMENT, pas seulement que le mode
// est choisi. Sans ces trois lignes, neutraliser la condition de demarrage
// dans BATTERY_CLOUD_LOCKS ne cassait aucun scenario — constate par
// mutation le 15/09.
state: 'start_battery_assist_night_min_current',
decision: 'start',
desiredCharging: true,
currentA: 6,
takeoverNeeded: false,
edfOnAction: false,
},
},
// CONTREFACTUEL — surplus epuise : la bascule vers le reseau se fait TOUTE
// SEULE. C'est la soupape decrite par le proprietaire, sans regle horaire.
{
id: 'p12-surplus-epuise-le-reseau-prend-le-relais',
label: 'Nuit HC, P12 ON mais SOC 37 sous le plancher 38,7 -> P7 reprend, EDF se ferme',
time: '2026-09-15T02:30:00+02:00',
overrides: {
...VEHICLE_NIGHT, soc_percent: '37',
allow_grid_hc_night: 'on',
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
grid_state: 'off', victron_acin1_available: '0',
battery_current_a: '-16',
evcs_charging_switch: 'off',
balance_status: 'ok', balance_last_run: '15/09 02:25',
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
balance_energy_to_cover_kwh: '3.18',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_hc_allowed',
takeoverNeeded: true,
edfOnAction: true,
},
},
// ⚠️ LE SCENARIO ANTI-OSCILLATEUR, le plus important des quatre.
// Voiture arretee, le plancher descend PLUS VITE que le SOC (-3,7 pt/h
// contre -2,6) : le surplus reapparaitrait tout seul. Ici le SOC est
// repasse AU-DESSUS du plancher, mais le verrou est pose — pas de reprise.
{
id: 'p12-verrou-pas-de-reprise-du-surplus',
label: 'Nuit HC, P12 ON, SOC 45 repasse au-dessus du plancher MAIS verrou pose -> pas de reprise',
time: '2026-09-15T04:00:00+02:00',
overrides: {
...VEHICLE_NIGHT, soc_percent: '45',
allow_grid_hc_night: 'on',
night_battery_charge_enabled: 'on', night_battery_done_prev: 'on',
grid_state: 'off', victron_acin1_available: '0',
battery_current_a: '-16',
evcs_charging_switch: 'off',
balance_status: 'ok', balance_last_run: '15/09 03:55',
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
balance_energy_to_cover_kwh: '3.18',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_hc_allowed',
takeoverNeeded: true,
},
},
// CONTREFACTUEL du toggle : a off, exactement le comportement d'avant.
{
id: 'p12-toggle-off-comportement-historique',
label: 'Nuit HC, memes conditions mais toggle P12 OFF -> P7 comme avant',
time: '2026-09-15T02:30:00+02:00',
overrides: {
...VEHICLE_NIGHT, soc_percent: '69',
allow_grid_hc_night: 'on',
night_battery_charge_enabled: 'off', night_battery_done_prev: 'off',
grid_state: 'off', victron_acin1_available: '0',
battery_current_a: '-16',
evcs_charging_switch: 'off',
balance_status: 'ok', balance_last_run: '15/09 02:25',
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
balance_energy_to_cover_kwh: '3.18',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_hc_allowed',
takeoverNeeded: true,
edfOnAction: true,
},
},
// ⚠️ GARDE DE FRAICHEUR. Le plancher vient du bilan ; s'il est perime, il ne
// decrit plus la nuit en cours et vider la batterie dessus serait aveugle.
// Ajoute apres qu'une mutation eut montre que rien ne testait cette garde.
{
id: 'p12-bilan-perime-pas-de-charge-batterie',
label: 'Nuit HC, P12 ON, SOC 69 mais bilan vieux de 3 h -> P7 comme avant, pas de puisage batterie',
time: '2026-09-15T02:30:00+02:00',
overrides: {
...VEHICLE_NIGHT, soc_percent: '69',
allow_grid_hc_night: 'on',
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
grid_state: 'off', victron_acin1_available: '0',
battery_current_a: '-16',
evcs_charging_switch: 'off',
balance_status: 'ok', balance_last_run: '14/09 23:25',
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
balance_energy_to_cover_kwh: '3.18',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_hc_allowed',
takeoverNeeded: true,
edfOnAction: true,
},
},
]; ];

File diff suppressed because one or more lines are too long