Jour faible : charge maximale en HC de midi (bilan vise 100 %, plafond ACM = abonnement - maison + solaire)

This commit is contained in:
Tallagrand Thierry
2026-10-08 00:07:54 +02:00
parent 36ceb41933
commit 043fb85383
9 changed files with 136 additions and 11 deletions

View File

@@ -436,9 +436,30 @@ function coverage(slots, hcFloorPct = 0) {
const slotsP50 = buildSlots(consumptionP50);
const slotsP80 = buildSlots(consumptionP80, true);
const hcFloorTypicalPct = hcReachFloorPct(slotsP50, nextHcStartMs);
const hcFloorReachPct = hcReachFloorPct(slotsP50, nextHcStartMs);
const hcFloorNightPrudentPct = hcNightReachActive ? hcReachFloorPct(slotsP80, nextNightHcStartMs) : 0;
const hcFloorPrudentPct = Math.max(hcReachFloorPct(slotsP80, nextHcStartMs), hcFloorNightPrudentPct);
const hcFloorPrudentReachPct = Math.max(hcReachFloorPct(slotsP80, nextHcStartMs), hcFloorNightPrudentPct);
// ---------------------------------------------------------------------
// JOUR FAIBLE : LA HC DE MIDI CHARGE AU MAXIMUM (08/10/2026, proprietaire)
//
// Le 07/10, « tenir jusqu'a 01:24 » visait 44 % (profil habituel) : la soiree
// a ete plus lourde, EDF s'est ferme en heures pleines a 18:03. Un jour faible
// (decision du bilan apres 07:24, `ecs_nuit_jour` = on), l'apres-midi ne
// remplira pas la batterie : un kWh pris a midi remplace un kWh d'heures pleines
// du soir, ou au pire un kWh d'heures creuses de la nuit (meme prix, ~10 % de
// pertes). Viser juste ne protege de rien.
//
// La cible devient 100 % pendant la fenetre de MIDI (la prochaine HC est celle
// de la nuit) : ce qui est REELLEMENT charge est fixe par la physique, pas par
// un nombre — l'ACM regle le courant sur ce que l'abonnement laisse libre une
// fois la maison (et la voiture) servie, plus le solaire, plafonne par les
// 140 A des deux MultiPlus. 65, 70 ou 80 % selon la cuisine et le soleil.
// La nuit, la regle « tenir jusqu'a midi » est inchangee.
// ---------------------------------------------------------------------
const hcMiddayWindow = hcReachActive && hcNightReachActive && nextHcStartMs === nextNightHcStartMs;
const hcMaxCharge = hcMiddayWindow && ecsNuitAujourdhui;
const hcFloorTypicalPct = hcMaxCharge ? 100 : hcFloorReachPct;
const hcFloorPrudentPct = hcMaxCharge ? 100 : hcFloorPrudentReachPct;
const typical = coverage(slotsP50, hcFloorTypicalPct);
const prudent = coverage(slotsP80, hcFloorPrudentPct);
@@ -893,6 +914,10 @@ return [{
windowEndMs: hcReachActive ? hcWindowEndMs : null,
nextHcStartMs: hcReachActive ? nextHcStartMs : null,
floorPct: round1(Math.min(100, hcFloorTypicalPct)),
// Jour faible a midi : cible 100 % (charge maximale) ; floorReachPct garde
// le plancher « tenir jusqu'a 01:24 » calcule, pour comparaison.
maxCharge: hcMaxCharge,
floorReachPct: round1(Math.min(100, hcFloorReachPct)),
floorPrudentPct: round1(Math.min(100, hcFloorPrudentPct)),
nextNightHcStartMs: hcNightReachActive ? nextNightHcStartMs : null,
nightFloorPrudentPct: round1(Math.min(100, hcFloorNightPrudentPct)),

View File

@@ -160,7 +160,9 @@ const reason = balance.crossing.found
// Fenetre HC (28/09) : dire quand c'est « tenir jusqu'a la prochaine HC » qui
// fixe l'exigence, sinon la raison affiche un croisement qui ne l'explique pas.
const hc = balance.hcReach || {};
const hcText = hc.active && hc.raisesRequirement
const hcText = hc.active && hc.maxCharge
? ` Jour faible : charge maximale en HC de midi (tenir jusqu'à ${new Date(hc.nextHcStartMs).toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' })} demandait ${hc.floorReachPct}%).`
: hc.active && hc.raisesRequirement
? ` Tenir jusqu'à la prochaine HC (${new Date(hc.nextHcStartMs).toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' })}) → ${hc.floorPct}% en fin de fenêtre.`
: '';
const hhmm = ms => new Date(ms).toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' });