Prevision de nuit : annoncer l'achat EDF reel (maison en HC + manque batterie)

Nuit du 28-29/09 : « environ 0,14 kWh a prendre sur EDF » annonces, 4,6 importes.
0,14 etait le manque de la batterie ; une session engagee fait passer toute la
maison sur le reseau jusqu'a 07:24. Le bilan publie la maison non couverte sur
la fenetre HC de nuit (input_number.evcs_balance_night_hc_house_kwh) et
sensor.evcs_prevision_nuit annonce import_prevu_kwh = maison + manque ;
reseau_necessaire_kwh supprime. Bilan 72/72, diagnostics 85/85.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z
This commit is contained in:
Tallagrand Thierry
2026-09-29 10:46:13 +02:00
parent ebcba4637b
commit 24ccecbc02
11 changed files with 198 additions and 14 deletions

View File

@@ -394,6 +394,33 @@ const hcFloorPrudentPct = Math.max(hcReachFloorPct(slotsP80, nextHcStartMs), hcF
const typical = coverage(slotsP50, hcFloorTypicalPct);
const prudent = coverage(slotsP80, hcFloorPrudentPct);
// ---------------------------------------------------------------------
// CE QUE LA SESSION DE NUIT ACHETERA AU RESEAU (29/09/2026)
//
// Incident du 28/09 : la prevision annoncait « 0,14 kWh a prendre sur EDF » et
// le reseau en a fourni 4,6. Les deux etaient justes, mais le premier est le
// MANQUE de la batterie : une fois engagee, la session tient la batterie et la
// maison passe TOUTE la fenetre HC de nuit sur le reseau (§40). Le chiffre qui
// repond a « combien vais-je acheter » est donc la maison non couverte par le
// solaire sur la fenetre (profil habituel, recale sur la soiree), depuis
// maintenant si la fenetre est ouverte. Le manque batterie s'y ajoute, a part.
// ---------------------------------------------------------------------
const nightHcStartMs = num(input.night_hc_start_ms, NaN);
const nightHcEndMs = num(input.night_hc_end_ms, NaN);
const nightHcActive = Number.isFinite(nightHcStartMs) && Number.isFinite(nightHcEndMs)
&& nightHcEndMs > nightHcStartMs;
let nightHcHouseKwh = null;
if (nightHcActive) {
const depuis = Math.max(nightHcStartMs, num(input.nowMs, Date.now()));
nightHcHouseKwh = 0;
for (const sl of slotsP50) {
const debut = num(sl.startMs, NaN);
if (!Number.isFinite(debut)) continue;
const a = Math.max(debut, depuis), b = Math.min(debut + 3600000, nightHcEndMs);
if (b > a) nightHcHouseKwh += Math.max(0, -sl.netKwh) * (b - a) / 3600000;
}
}
// ---------------------------------------------------------------------
// VEHICULE BRANCHE EN HC AVEC UN DEPART AVANT LA PROCHAINE HC (28/09/2026)
//
@@ -667,6 +694,14 @@ return [{
forecastKwh: num(input.night_house_forecast_kwh, NaN) || null,
},
// Maison sur le reseau pendant la fenetre HC de nuit, si la session s'engage (29/09).
nightHcImport: {
active: nightHcActive,
startMs: nightHcActive ? nightHcStartMs : null,
endMs: nightHcActive ? nightHcEndMs : null,
houseKwh: nightHcHouseKwh === null ? null : round2(nightHcHouseKwh),
},
// Voiture branchee en HC, depart avant la prochaine HC (28/09).
vehicleMidday: {
active: vehicleRuleActive,

View File

@@ -92,6 +92,9 @@ const vm = balance.vehicleMidday || {};
setBool('input_boolean.evcs_balance_vehicle_grid_needed', !!vm.gridNeeded);
setNumber('input_number.evcs_balance_vehicle_need_kwh', vm.active ? vm.reachableKwh : 0);
setNumber('input_number.evcs_balance_vehicle_solar_kwh', vm.active ? vm.solarKwh : 0);
// 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);
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);

View File

@@ -3,7 +3,7 @@
instants : la fin de la fenetre HC en cours (vide hors fenetre) et le debut
de la fenetre HC suivante. Strategie du proprietaire : la batterie doit
tenir jusqu'a la prochaine fenetre HC. -#}
{%- set ns = namespace(fin='', suivant='', nuit='', fen=[]) -%}
{%- set ns = namespace(fin='', suivant='', nuit='', fen=[], nd='', nf='') -%}
{%- for j in [0, 1, 2] -%}
{%- for a, b, t in [('victron_evcs_hc_night_start', 'victron_evcs_hc_night_stop', 'nuit'), ('victron_evcs_hc_midday_start', 'victron_evcs_hc_midday_stop', 'midi')] -%}
{%- set d = today_at(states('input_datetime.' ~ a)) + timedelta(days=j) -%}
@@ -20,6 +20,11 @@
{%- if w[0] >= repere and ns.suivant == '' -%}{%- set ns.suivant = w[0] -%}{%- endif -%}
{%- if w[0] >= repere and w[2] == 'nuit' and ns.nuit == '' -%}{%- set ns.nuit = w[0] -%}{%- endif -%}
{%- endfor -%}
{#- Fenetre HC de NUIT en cours, sinon la prochaine (29/09) : sert a prevoir
ce que la maison achetera au reseau si la session de nuit s'engage. -#}
{%- for w in ns.fen | sort(attribute='0') -%}
{%- if w[2] == 'nuit' and w[1] > now() and ns.nd == '' -%}{%- set ns.nd = w[0] -%}{%- set ns.nf = w[1] -%}{%- endif -%}
{%- endfor -%}
{#- Vehicule branche a midi (28/09/2026) — JEEP, VW ou ZERO, selon le
selecteur. Pour chacun, HA porte le calendrier (jours et heure de depart
apres le dejeuner), la capacite et la puissance de charge MESUREES ; le
@@ -74,6 +79,8 @@
"hc_window_end_ms": (as_timestamp(ns.fin) * 1000) if ns.fin != '' else none,
"next_hc_start_ms": (as_timestamp(ns.suivant) * 1000) if ns.suivant != '' else none,
"next_night_hc_start_ms": (as_timestamp(ns.nuit) * 1000) if ns.nuit != '' else none,
"night_hc_start_ms": (as_timestamp(ns.nd) * 1000) if ns.nd != '' else none,
"night_hc_end_ms": (as_timestamp(ns.nf) * 1000) if ns.nf != '' else none,
"nowMs": as_timestamp(now()) * 1000,
"balance_enabled": states("input_boolean.evcs_balance_enabled"),

View File

@@ -77,6 +77,10 @@ return [{
hc_window_end_ms: snapshot.hc_window_end_ms,
next_hc_start_ms: snapshot.next_hc_start_ms,
next_night_hc_start_ms: snapshot.next_night_hc_start_ms,
// Fenetre HC de nuit en cours ou prochaine (29/09) : import prevu si la
// session de nuit s'engage.
night_hc_start_ms: snapshot.night_hc_start_ms,
night_hc_end_ms: snapshot.night_hc_end_ms,
// Repris pour l'écriture des helpers, sans influencer le calcul.
resourceUsable: resource.usable,