Bilan : la nuit prepare jusqu'a la nuit suivante (la regle du 08/10 prenait le creux de midi pour le soir)
This commit is contained in:
18
HANDOFF.md
18
HANDOFF.md
@@ -2230,5 +2230,21 @@ approximations successives ») ; cette nuit la VW a charge A FOND de 05:17 a 07:
|
||||
budget 0 kWh … activer complete ») ; EDF HP **partiel** ; midi **toujours faux** (« consigne inferieure au
|
||||
solaire »). 1 juste / 1 partiel / 1 faux, contre 0 juste avant. Pas encore un role actif.
|
||||
|
||||
### 09/10 01:35 — La regle « la nuit prepare le soir » etait muette : corrigee
|
||||
- Nuit du 08-09/10 : VRM annonce **14,7 kWh** le 09/10 et la regle ne s'est pas declenchee. Cause (rejeu de l'entree
|
||||
reelle de 01:30) : `nextDeficitWindow` prenait le creux de 12 h (apres un surplus a 11 h) pour « la traversee du
|
||||
soir » -> 22,1 % requis. Defaut de ma version du 08/10.
|
||||
- **Nouvelle definition** : de la fin de la fenetre de nuit au debut de la NUIT SUIVANTE (01:24), la batterie ne doit
|
||||
jamais passer sous le plancher (profil habituel, solaire ecrete a 100 %, HC de midi NON comptee) ; plus petite
|
||||
energie de fin de nuit qui le garantit (recherche dichotomique sur l'energie au-dessus du plancher) ; hors
|
||||
d'atteinte -> le mieux possible sans gaspiller. Sorties `nightEvening.floorPct`, `shortfallKwh`. Raison : « Journee
|
||||
faible annoncee : la nuit charge a x % (sinon il manquerait y kWh avant la nuit prochaine…) ».
|
||||
- Tests bilan 92/92 : nouveau scenario sur l'ENTREE REELLE du 09/10 01:30 (`tests/energy-balance/fixtures/bilan-0910-0130.json`)
|
||||
-> 50,4 % (27,1 + 7 / 0,30, verifie a la main) ; les deux rejeux du 27/09 reviennent a leurs valeurs d'avant le 08/10
|
||||
(25,3 % ; requis 41,4 %) : avec 18-25 kWh la journee remonte assez la batterie au profil habituel. Mutations : regle
|
||||
coupee -> 1 ; minimum du parcours ignore -> 1.
|
||||
- Deploye 01:37 ; calcul reel 01:40 : plancher et requis **50,4 %** (SOC 55 %). La session de nuit s'engagera quand le
|
||||
SOC passera sous 50,4 % (la comparaison se fait au SOC courant : la batterie descend d'abord a 50,4 % puis est tenue).
|
||||
|
||||
---
|
||||
*Document mis a jour le : 08/10/2026 (~08:00) — conseiller relecteur de la journee (revue calculee + pistes) ; nuit : voiture qui profite d'EDF ferme pour la batterie, nuit qui prepare le soir les jours annonces faibles ; 08/10/2026 (~00:15) — jour faible : charge maximale en HC de midi (bilan 100 %, ACM plafond + solaire) ; 07/10/2026 (~22:45) — reprise de 16 h verifiee, EDF en HP a 18:03, bilan : reprise du soir et prevision recalee sur la matinee ; bilan HC de midi, reprise du soir apres coupure en pleine chauffe, mesure ECS sur 10 min ; recharge reseau HC : courant calcule (12 kVA) et consigne non tenue ; HC de midi : AUTO ne chauffe pas (BOOST), alerte v2, Jeep (contact, relecture, zone), recharge batterie bridee a 30 A par l'ACM ; jour faible = EDF les deux fenetres HC pour le chauffe-eau, alerte BOOST, Victron Mobile refait ; 1re decision jour faible en reel, mesure ECS reparee, douches 06:00/20:00, Grafana chauffe-eau, tableaux ECS ; chauffe-eau en HC de nuit les jours faibles (decision du bilan, workflow ECS sous controle du depot, VRM, V2.3 desactive) ; garde batterie reseau : voiture bridee a 6 A au lieu d'un arret ; garde-fou concierge 2 h ; allers-retours brefs de la borne retrouves dans l'historique HA (echange VW -> JEEP de 20 s), production 14,5 kWh contre 22 prevus, defaut garde batterie en mode reseau a trancher ; voiture a midi : la batterie n'est plus comptee comme du solaire (EDF automatique les jours de depart si le surplus ne couvre pas) ; nuit 05->06 : JEEP chargee sur EDF en complete (6,32 kWh), budget 0 du a la prevision, Windy/ECMWF contre VRM, a comparer ce soir ; concierge aligne sur la preuve fin de trajet ; a verifier : nuit 05->06 et mardi faible, comparaison VRM/Forecast.Solar ; JEEP identifiee aussi par sa fin de trajet, prevision de la nuit 05->06 ; identification JEEP reussie en reel, correction humaine qui prime (refus supprime), session du 04/10 coupee ; EVCS Master en quatre onglets ; Jeep lue par Uconnect et identifiee seule, capacite JEEP 10 kWh, concierge variante (a) ; releve par photo (SPARK), hass-uconnect en attente ; ecart semaine/week-end mesure (pas de profil week-end la nuit) ; nuit du budget : budget borne au plancher prudent, sans EDF = voiture bloquee meme EDF ferme (batterie seule) ; Energie : sauts corriges ; tableau Energie complete (Sud/Ouest, batterie par integration, borne ; aucun redemarrage HA) ; kilometrage des vehicules (km/kWh, capacite, releves de reference, agent cable 15/15) ; a verifier le 04/10 au matin : nuit du budget JEEP ; concierge : vehicule non confirme demande, heure de saisie ; Grafana vehicules en tableau ; Grafana vehicules : ZERO et Invite ajoutes, session 16:46 reetiquetee ZERO ; prevision SOC 10 h ~35 % ; concierge fiable : niveau note par le script, une ligne, dedoublonnage, simulation par drapeau ; agent : message incompris = question, bourse sur demande explicite (banc final 6/6) ; a verifier : nuit du samedi 03 au dimanche 04/10 (budget < besoin JEEP), cron orange-cloture lundi 05/10 apres 17:40, comparaison VRM / Forecast.Solar lundi 05/10*
|
||||
*Document mis a jour le : 09/10/2026 (~01:45) — conseiller relecteur de la journee (revue calculee + pistes) ; nuit : voiture qui profite d'EDF ferme pour la batterie, nuit qui prepare le soir les jours annonces faibles ; 08/10/2026 (~00:15) — jour faible : charge maximale en HC de midi (bilan 100 %, ACM plafond + solaire) ; 07/10/2026 (~22:45) — reprise de 16 h verifiee, EDF en HP a 18:03, bilan : reprise du soir et prevision recalee sur la matinee ; bilan HC de midi, reprise du soir apres coupure en pleine chauffe, mesure ECS sur 10 min ; recharge reseau HC : courant calcule (12 kVA) et consigne non tenue ; HC de midi : AUTO ne chauffe pas (BOOST), alerte v2, Jeep (contact, relecture, zone), recharge batterie bridee a 30 A par l'ACM ; jour faible = EDF les deux fenetres HC pour le chauffe-eau, alerte BOOST, Victron Mobile refait ; 1re decision jour faible en reel, mesure ECS reparee, douches 06:00/20:00, Grafana chauffe-eau, tableaux ECS ; chauffe-eau en HC de nuit les jours faibles (decision du bilan, workflow ECS sous controle du depot, VRM, V2.3 desactive) ; garde batterie reseau : voiture bridee a 6 A au lieu d'un arret ; garde-fou concierge 2 h ; allers-retours brefs de la borne retrouves dans l'historique HA (echange VW -> JEEP de 20 s), production 14,5 kWh contre 22 prevus, defaut garde batterie en mode reseau a trancher ; voiture a midi : la batterie n'est plus comptee comme du solaire (EDF automatique les jours de depart si le surplus ne couvre pas) ; nuit 05->06 : JEEP chargee sur EDF en complete (6,32 kWh), budget 0 du a la prevision, Windy/ECMWF contre VRM, a comparer ce soir ; concierge aligne sur la preuve fin de trajet ; a verifier : nuit 05->06 et mardi faible, comparaison VRM/Forecast.Solar ; JEEP identifiee aussi par sa fin de trajet, prevision de la nuit 05->06 ; identification JEEP reussie en reel, correction humaine qui prime (refus supprime), session du 04/10 coupee ; EVCS Master en quatre onglets ; Jeep lue par Uconnect et identifiee seule, capacite JEEP 10 kWh, concierge variante (a) ; releve par photo (SPARK), hass-uconnect en attente ; ecart semaine/week-end mesure (pas de profil week-end la nuit) ; nuit du budget : budget borne au plancher prudent, sans EDF = voiture bloquee meme EDF ferme (batterie seule) ; Energie : sauts corriges ; tableau Energie complete (Sud/Ouest, batterie par integration, borne ; aucun redemarrage HA) ; kilometrage des vehicules (km/kWh, capacite, releves de reference, agent cable 15/15) ; a verifier le 04/10 au matin : nuit du budget JEEP ; concierge : vehicule non confirme demande, heure de saisie ; Grafana vehicules en tableau ; Grafana vehicules : ZERO et Invite ajoutes, session 16:46 reetiquetee ZERO ; prevision SOC 10 h ~35 % ; concierge fiable : niveau note par le script, une ligne, dedoublonnage, simulation par drapeau ; agent : message incompris = question, bourse sur demande explicite (banc final 6/6) ; a verifier : nuit du samedi 03 au dimanche 04/10 (budget < besoin JEEP), cron orange-cloture lundi 05/10 apres 17:40, comparaison VRM / Forecast.Solar lundi 05/10*
|
||||
|
||||
@@ -473,42 +473,51 @@ const hcMaxCharge = hcMiddayWindow && ecsNuitAujourdhui;
|
||||
// « tenir jusqu'a midi »).
|
||||
// ---------------------------------------------------------------------
|
||||
const hcNightWindow = hcReachActive && !hcMiddayWindow;
|
||||
// ⚠️ CORRIGE LE 09/10 : la premiere version (08/10) visait « le debut de la
|
||||
// traversee du soir » = la premiere fenetre de deficit apres le premier surplus.
|
||||
// Nuit du 08-09/10 (VRM 14,7 kWh) : un surplus a 11 h puis un creux a 12 h ->
|
||||
// « traversee » a 12 h, 22 % requis, regle muette alors que la journee etait
|
||||
// faible. L'objectif est maintenant sans ambiguite : de la fin de la fenetre de
|
||||
// nuit au debut de la NUIT SUIVANTE (01:24), la batterie ne doit JAMAIS passer
|
||||
// sous le plancher, au profil habituel, solaire ecrete a 100 %. La HC de midi
|
||||
// n'est pas comptee (prudence : elle est bornee par la maison, et ce que la nuit
|
||||
// charge au meme prix ne lui coute rien).
|
||||
let nightEvening = { active: false };
|
||||
if (hcNightWindow) {
|
||||
const ndEve = nextDeficitWindow(slotsP50);
|
||||
if (ndEve.found && Number.isFinite(num(ndEve.startMs, NaN)) && ndEve.startMs > hcWindowEndMs) {
|
||||
const socAtEvening = (s0) => {
|
||||
let soc = s0;
|
||||
if (hcNightWindow && Number.isFinite(nextNightHcStartMs) && nextNightHcStartMs > hcWindowEndMs) {
|
||||
const capKwh = energyBetweenSoc(socFloorPct, 100);
|
||||
// Energie minimale au-dessus du plancher sur le parcours, depuis e0 en fin de nuit.
|
||||
const minAuDessus = (e0) => {
|
||||
let e = e0, mini = e0;
|
||||
for (const sl of slotsP50) {
|
||||
const debut = num(sl.startMs, NaN);
|
||||
if (!Number.isFinite(debut)) continue;
|
||||
const a = Math.max(debut, hcWindowEndMs);
|
||||
const b = Math.min(debut + 3600000, ndEve.startMs);
|
||||
const b = Math.min(debut + 3600000, nextNightHcStartMs);
|
||||
if (b <= a) continue;
|
||||
const net = sl.netKwh * (b - a) / 3600000;
|
||||
soc = net >= 0 ? Math.min(100, socAfterEnergy(soc, net))
|
||||
: Math.max(socFloorPct, socForEnergy(Math.max(0, energyBetweenSoc(0, soc) + net), 0));
|
||||
e = Math.min(capKwh, e + sl.netKwh * (b - a) / 3600000);
|
||||
if (e < mini) mini = e;
|
||||
}
|
||||
return soc;
|
||||
return mini;
|
||||
};
|
||||
const requis = ndEve.requiredSocAtStartPct;
|
||||
const base = Math.max(socFloorPct, hcFloorReachPct);
|
||||
const objectif = Math.min(requis, socAtEvening(100)) - 0.05;
|
||||
let floor = base;
|
||||
if (socAtEvening(base) < objectif) {
|
||||
let lo = base, hi = 100;
|
||||
for (let k = 0; k < 30; k++) { const m = (lo + hi) / 2; if (socAtEvening(m) >= objectif) hi = m; else lo = m; }
|
||||
floor = hi;
|
||||
const eBase = energyBetweenSoc(socFloorPct, base);
|
||||
// Hors d'atteinte meme pleine : on vise le mieux possible, sans gaspiller.
|
||||
const objectif = Math.min(0, minAuDessus(capKwh)) - 0.01;
|
||||
let e0 = eBase;
|
||||
if (minAuDessus(eBase) < objectif) {
|
||||
let lo = eBase, hi = capKwh;
|
||||
for (let k = 0; k < 40; k++) { const m = (lo + hi) / 2; if (minAuDessus(m) >= objectif) hi = m; else lo = m; }
|
||||
e0 = hi;
|
||||
}
|
||||
const floor = socAfterEnergy(socFloorPct, e0);
|
||||
nightEvening = {
|
||||
active: floor > base + 0.05,
|
||||
floorPct: round1(floor),
|
||||
eveningRequiredPct: round1(requis),
|
||||
eveningProjectedPct: round1(socAtEvening(base)),
|
||||
eveningStartHour: ndEve.startHour,
|
||||
floorPct: round1(Math.min(100, floor)),
|
||||
// Sans relever : energie qui manquerait sous le plancher avant la nuit suivante.
|
||||
shortfallKwh: round2(Math.max(0, -minAuDessus(eBase))),
|
||||
untilMs: nextNightHcStartMs,
|
||||
};
|
||||
}
|
||||
}
|
||||
const hcFloorTypicalPct = hcMaxCharge ? 100
|
||||
: (nightEvening.active ? Math.max(hcFloorReachPct, nightEvening.floorPct) : hcFloorReachPct);
|
||||
const hcFloorPrudentPct = hcMaxCharge ? 100 : hcFloorPrudentReachPct;
|
||||
|
||||
@@ -162,7 +162,7 @@ const reason = balance.crossing.found
|
||||
const hc = balance.hcReach || {};
|
||||
const ne = hc.nightEvening || {};
|
||||
const hcText = hc.active && ne.active
|
||||
? ` Journée faible annoncée : la nuit charge à ${ne.floorPct}% (batterie prévue à ${ne.eveningProjectedPct}% à ${ne.eveningStartHour} h pour ${ne.eveningRequiredPct}% requis ; tenir jusqu'à midi demandait ${hc.floorReachPct}%).`
|
||||
? ` Journée faible annoncée : la nuit charge à ${ne.floorPct}% (sinon il manquerait ${ne.shortfallKwh} kWh avant la nuit prochaine ; tenir jusqu'à midi demandait ${hc.floorReachPct}%).`
|
||||
: 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
|
||||
|
||||
1
tests/energy-balance/fixtures/bilan-0910-0130.json
Normal file
1
tests/energy-balance/fixtures/bilan-0910-0130.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
import fs from 'node:fs';
|
||||
// Scénarios du nœud BALANCE.
|
||||
// Le scénario central est un REJEU de la matinée du 24/08/2026 : la production
|
||||
// horaire est celle réellement mesurée ce jour-là (heures 06-09 h non
|
||||
@@ -421,8 +422,10 @@ export const balanceScenarios = [
|
||||
// 59,6 % a 19 h pour 64,7 % requis -> +5,1 points (courbe plate, pas
|
||||
// d'ecretage) : 25,3 + 5,0 = 30,3 % en fin de nuit. Le 27/09 reel (22 kWh)
|
||||
// a vide la batterie a 01:00 : le sens est le bon.
|
||||
// 09/10 : la regle « la nuit prepare jusqu'a la nuit suivante » ne joue pas ici :
|
||||
// au profil habituel, 25,1 kWh remontent assez la batterie pour passer 01:24.
|
||||
expect: { usable: true, requiredSocPct: 40.2, prudentRequiredSocPct: 48.4, gridNeeded: false,
|
||||
hcReachActive: true, nightEveningActive: true, hcFloorPct: 30.3, hcNightFloorPrudentPct: 48.4 },
|
||||
hcReachActive: true, nightEveningActive: false, hcFloorPct: 25.3, hcNightFloorPrudentPct: 48.4 },
|
||||
},
|
||||
{
|
||||
// Meme nuit, journee annoncee sombre (18 kWh, prevision VRM du 27) : le
|
||||
@@ -432,7 +435,7 @@ export const balanceScenarios = [
|
||||
id: 'bilan-hc-nuit-2709-journee-sombre-bloque-p12',
|
||||
label: 'Nuit 26-27/09, journee sombre (18 kWh) : P12 plancher 72,1 % > SOC 72 → voiture sur le reseau HC',
|
||||
snapshot: snap2709('2026-09-27T01:20:00+02:00', 72, NUIT_2709, SOMBRE),
|
||||
expect: { usable: true, requiredSocPct: 54, prudentRequiredSocPct: 72.1, gridNeeded: false,
|
||||
expect: { usable: true, requiredSocPct: 41.4, prudentRequiredSocPct: 72.1, gridNeeded: false,
|
||||
hcReachActive: true, hcNightFloorPrudentPct: 72.1 },
|
||||
},
|
||||
{
|
||||
@@ -602,9 +605,19 @@ export const balanceScenarios = [
|
||||
label: 'Fenetre HC de NUIT, decision jour faible du jour : la nuit garde « tenir jusqu a midi »',
|
||||
snapshot: Object.assign(snap2709('2026-09-27T01:20:00+02:00', 72, NUIT_2709),
|
||||
{ ecs_nuit_jour: '2026-09-27|on|x' }),
|
||||
// Pas de charge maximale la nuit ; la nuit prepare le soir (30,3 %, voir le
|
||||
// scenario du 27/09 ci-dessus).
|
||||
expect: { usable: true, hcReachActive: true, hcMaxCharge: false, nightEveningActive: true, hcFloorPct: 30.3 },
|
||||
expect: { usable: true, hcReachActive: true, hcMaxCharge: false, hcFloorPct: 25.3 },
|
||||
},
|
||||
{
|
||||
// ENTREE REELLE du 09/10 01:30 (PREPARE_BALANCE, VRM 14,7 kWh) : la version du
|
||||
// 08/10 prenait le creux de 12 h (apres un surplus a 11 h) pour la « traversee du
|
||||
// soir » et restait muette. Au profil habituel, sans relever, il manque 7 kWh
|
||||
// sous le plancher avant 01:24 le 10/10 -> fin de nuit a 27,1 + 7 / 0,30 = 50,4 %
|
||||
// (verifie a la main, courbe plate, pas d'ecretage).
|
||||
node: 'BALANCE',
|
||||
id: 'bilan-hc-nuit-0910-journee-faible-reelle',
|
||||
label: 'Nuit du 08-09/10, journee faible (VRM 14,7 kWh) : la nuit charge a 50,4 % pour passer la soiree',
|
||||
snapshot: JSON.parse(fs.readFileSync(new URL('./fixtures/bilan-0910-0130.json', import.meta.url), 'utf8')),
|
||||
expect: { usable: true, hcReachActive: true, nightEveningActive: true, hcFloorPct: 50.4 },
|
||||
},
|
||||
{
|
||||
node: 'BALANCE',
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user