Bilan : la nuit prevue suit la soiree reelle (regression sur 24 nuits), nuage Grafana corrige
Le service publie le facteur nuit prevue / nuit moyenne (sensor.evcs_nuit_maison_prevue_kwh), le bilan l'applique a son profil de 21:30 a 09:00. Diagnostics 81/81, bilan 68/68. 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:
@@ -140,11 +140,57 @@ if (missing.length) {
|
||||
// - profil PRUDENT (p80) → planification : cible de nuit, dimensionnement
|
||||
// du stockage, choix qui engagent plusieurs heures.
|
||||
// ---------------------------------------------------------------------
|
||||
function buildSlots(consumption) {
|
||||
// ---------------------------------------------------------------------
|
||||
// LA NUIT PREVUE PAR LA SOIREE (28/09/2026, demande du proprietaire)
|
||||
//
|
||||
// Le profil p50 est une mediane HORAIRE : il ignore la soiree qui vient de se
|
||||
// passer. Or sur 24 nuits, la maison consomme d'autant plus de 21:30 a 09:00
|
||||
// que la soiree 18:00-21:30 a ete chargee (four) : nuit = 6,98 + 0,74 x soiree,
|
||||
// r2 = 0,40. Le service d'historisation publie le FACTEUR nuit prevue / nuit
|
||||
// moyenne (sensor.evcs_nuit_maison_prevue_kwh) ; on multiplie le profil par ce
|
||||
// facteur sur la seule fenetre 21:30 -> 09:00, au prorata des heures a cheval.
|
||||
// Le 28/09 : soiree 6,27 kWh -> facteur 1,09.
|
||||
//
|
||||
// Un FACTEUR et non des kWh : le profil vient des compteurs d'energie, la
|
||||
// regression des puissances integrees ; le rapport annule l'ecart de methode.
|
||||
// Dans les deux sens pour le profil TYPIQUE (c'est un estimateur, une soiree
|
||||
// legere allege la nuit) ; le PRUDENT ne descend jamais : max(p80, p50 recale).
|
||||
// Filet : facteur borne a [0,75 ; 1,45], les extremes des 24 nuits observees
|
||||
// (0,77 et 1,41 fois la moyenne) — hors de la, c'est une mesure fausse, pas une
|
||||
// nuit. Facteur absent, perime (> 30 min) ou hors fenetre : profil inchange.
|
||||
const NIGHT_FACTOR_MIN = 0.75;
|
||||
const NIGHT_FACTOR_MAX = 1.45;
|
||||
const nowMsNight = num(input.nowMs, Date.now());
|
||||
const nightStartMs = num(input.night_house_start_ms, NaN);
|
||||
const nightEndMs = num(input.night_house_end_ms, NaN);
|
||||
const nightFactorRaw = num(input.night_house_factor, NaN);
|
||||
const nightComputedMs = num(input.night_house_computed_ms, NaN);
|
||||
const nightModelActive =
|
||||
Number.isFinite(nightFactorRaw) && nightFactorRaw > 0 &&
|
||||
Number.isFinite(nightStartMs) && Number.isFinite(nightEndMs) && nightEndMs > nightStartMs &&
|
||||
nowMsNight >= nightStartMs && nowMsNight < nightEndMs &&
|
||||
Number.isFinite(nightComputedMs) && nowMsNight - nightComputedMs <= 30 * 60000;
|
||||
const nightFactor = nightModelActive
|
||||
? Math.min(NIGHT_FACTOR_MAX, Math.max(NIGHT_FACTOR_MIN, nightFactorRaw))
|
||||
: 1;
|
||||
function nightShare(startMs) {
|
||||
const debut = num(startMs, NaN);
|
||||
if (!nightModelActive || !Number.isFinite(debut)) return 0;
|
||||
const a = Math.max(debut, nightStartMs), b = Math.min(debut + 3600000, nightEndMs);
|
||||
return b > a ? (b - a) / 3600000 : 0;
|
||||
}
|
||||
function nightScaled(profile, slot) {
|
||||
const h = num(slot.hour, 0);
|
||||
return (profile[h] ?? 0) * (1 + (nightFactor - 1) * nightShare(slot.startMs));
|
||||
}
|
||||
|
||||
function buildSlots(consumption, prudent = false) {
|
||||
return horizon.slice(0, CFG.SEARCH_HOURS).map(slot => {
|
||||
const hour = num(slot.hour, 0);
|
||||
const productionKwh = Math.max(0, num(slot.expectedKwh, 0));
|
||||
const consumptionKwh = consumption[hour] ?? 0;
|
||||
const consumptionKwh = prudent
|
||||
? Math.max(consumption[hour] ?? 0, nightScaled(consumptionP50, slot))
|
||||
: nightScaled(consumption, slot);
|
||||
return {
|
||||
startMs: slot.startMs,
|
||||
hour,
|
||||
@@ -341,7 +387,7 @@ function coverage(slots, hcFloorPct = 0) {
|
||||
}
|
||||
|
||||
const slotsP50 = buildSlots(consumptionP50);
|
||||
const slotsP80 = buildSlots(consumptionP80);
|
||||
const slotsP80 = buildSlots(consumptionP80, true);
|
||||
const hcFloorTypicalPct = hcReachFloorPct(slotsP50, nextHcStartMs);
|
||||
const hcFloorNightPrudentPct = hcNightReachActive ? hcReachFloorPct(slotsP80, nextNightHcStartMs) : 0;
|
||||
const hcFloorPrudentPct = Math.max(hcReachFloorPct(slotsP80, nextHcStartMs), hcFloorNightPrudentPct);
|
||||
@@ -613,6 +659,14 @@ return [{
|
||||
surplusAvailableKwh: round2(surplusAfterCrossingKwh),
|
||||
},
|
||||
|
||||
// Nuit prevue par la soiree (28/09) : facteur applique au profil.
|
||||
nightModel: {
|
||||
active: nightModelActive,
|
||||
factor: Math.round(nightFactor * 1000) / 1000,
|
||||
rawFactor: Number.isFinite(nightFactorRaw) ? nightFactorRaw : null,
|
||||
forecastKwh: num(input.night_house_forecast_kwh, NaN) || null,
|
||||
},
|
||||
|
||||
// Voiture branchee en HC, depart avant la prochaine HC (28/09).
|
||||
vehicleMidday: {
|
||||
active: vehicleRuleActive,
|
||||
|
||||
Reference in New Issue
Block a user