Budget voiture de la nuit : ce que la batterie peut donner sans EDF, arret au compteur, choix sans EDF / complete

This commit is contained in:
Tallagrand Thierry
2026-09-30 13:26:45 +02:00
parent fb98a8cbb8
commit e0d855f33f
16 changed files with 370 additions and 10 deletions

View File

@@ -421,6 +421,48 @@ if (nightHcActive) {
}
}
// ---------------------------------------------------------------------
// BUDGET VOITURE DE LA NUIT (30/09/2026, decisions du proprietaire)
//
// « Definir combien la voiture peut prendre sans utiliser EDF, et brider la
// charge a cette valeur ou autoriser une charge complete avec EDF. » Choix du
// 30/09 : profil PRUDENT (p80) et plancher du bilan (22 %) ; par defaut « sans
// EDF » ; en charge complete, le budget batterie d'abord, EDF pour le reste.
//
// Budget = la plus petite reserve au-dessus du plancher que la batterie
// connaitra entre l'ouverture de la fenetre HC de nuit (ou maintenant si elle
// est ouverte) et le debut de la fenetre de nuit SUIVANTE, sur le profil
// prudent. Retirer X kWh a l'ouverture abaisse toute la trajectoire de X :
// le budget est donc le creux de la trajectoire. L'horizon jusqu'a la nuit
// suivante applique « la batterie tient jusqu'a la prochaine HC » ; la
// batterie pleine ecrete la recharge (on ne stocke pas au-dela de 100 %).
// Le 30/09 01:25 : SOC 57, prudent 44,7 -> ~3,7 kWh.
// ---------------------------------------------------------------------
let nightVehicleBudgetKwh = null;
if (nightHcActive && Number.isFinite(socPct)) {
const maintenant = num(input.nowMs, Date.now());
const depuis = Math.max(nightHcStartMs, maintenant);
const jusqua = nightHcStartMs + 24 * 3600000;
const plein = energyBetweenSoc(socFloorPct, 100);
let reserve = socPct >= socFloorPct
? energyBetweenSoc(socFloorPct, socPct) : -energyBetweenSoc(socPct, socFloorPct);
let creux = maintenant >= depuis ? reserve : Infinity;
for (const sl of slotsP80) {
const debut = num(sl.startMs, NaN);
if (!Number.isFinite(debut)) continue;
const a = Math.max(debut, maintenant), b = Math.min(debut + 3600000, jusqua);
if (b <= a) continue;
// La tranche qui contient l'ouverture est coupee en deux : le creux ne se
// lit qu'a partir de l'ouverture.
const coupes = depuis > a && depuis < b ? [a, depuis, b] : [a, b];
for (let i = 0; i + 1 < coupes.length; i++) {
reserve = Math.min(plein, reserve + sl.netKwh * (coupes[i + 1] - coupes[i]) / 3600000);
if (coupes[i + 1] >= depuis) creux = Math.min(creux, reserve);
}
}
nightVehicleBudgetKwh = Number.isFinite(creux) ? Math.max(0, creux) : null;
}
// ---------------------------------------------------------------------
// VEHICULE BRANCHE EN HC AVEC UN DEPART AVANT LA PROCHAINE HC (28/09/2026)
//
@@ -702,6 +744,12 @@ return [{
houseKwh: nightHcHouseKwh === null ? null : round2(nightHcHouseKwh),
},
// Ce que la voiture peut prendre sur la batterie cette nuit sans EDF (30/09).
nightVehicleBudget: {
active: nightVehicleBudgetKwh !== null,
kwh: nightVehicleBudgetKwh === null ? null : round2(nightVehicleBudgetKwh),
},
// Voiture branchee en HC, depart avant la prochaine HC (28/09).
vehicleMidday: {
active: vehicleRuleActive,

View File

@@ -95,6 +95,10 @@ setNumber('input_number.evcs_balance_vehicle_solar_kwh', vm.active ? vm.solarKwh
// Maison sur le reseau pendant la fenetre HC de nuit si la session s'engage (29/09).
const nh = balance.nightHcImport || {};
setNumber('input_number.evcs_balance_night_hc_house_kwh', nh.active && nh.houseKwh !== null ? nh.houseKwh : 0);
// Budget voiture de la nuit sans EDF (30/09) : 0 si inconnu — le controleur
// ne donne alors rien de la batterie.
const nb = balance.nightVehicleBudget || {};
setNumber('input_number.evcs_balance_night_vehicle_budget_kwh', nb.active && nb.kwh !== null ? nb.kwh : 0);
setNumber('input_number.evcs_balance_energy_to_cover_kwh', c.energyToCoverKwh);
setNumber('input_number.evcs_balance_grid_energy_needed_kwh', c.gridEnergyNeededKwh);
setNumber('input_number.evcs_balance_vehicle_surplus_kwh', balance.vehicle.surplusAvailableKwh);