Le contacteur ne se rend plus au milieu du service
La regle du 05/09 ferme quand la maison depasse 80 % du plafond de decharge du parc — elle a mordu au bon instant hier soir, a -97,4 A pour 100 A autorises. Mais elle relachait sur un seul echantillon sous 50 %, et la soiree offrait quatorze occasions de le faire. Le troisieme pic est tombe deux minutes avant l'ouverture reelle : decale de cinq minutes, elle refermait, et chaque aller-retour du contacteur coute 50 a 150 Wh. Les accalmies mesurees sont franchement bimodales — treize de 5 min au plus pendant la cuisson, une de 201 min apres. Le relachement se mesure donc en duree, et la duree vient du bilan energetique : 20 min tant que la batterie est condamnee a toucher son plancher avant les heures creuses (l'energie reseau du soir est due de toute facon, la tenir ne change que son heure), 6 min des qu'elle passerait la nuit seule (chaque kWh du soir deplacerait alors un kWh d'heures creuses vers des heures pleines). Premiere lecture du bilan par le workflow d'hysteresis. Le dwell se compte sur la maison, jamais sur le contacteur : l'horodatage est ecrit qu'il soit ouvert ou ferme, sans quoi la decision detruirait sa propre cause. Et parce que la tension batterie s'est tue 27 minutes hier soir en pleine cuisson, le plafond retombe sur la tension nominale du parc, et a defaut l'horodatage — un fait persiste — tient la protection jusqu'au bout du dwell. Golden-hysteresis 11 -> 20. Six mutations, chacune tombant sur un jeu different. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
@@ -2,31 +2,31 @@
|
||||
"soc_percent": "50",
|
||||
"grid_state": "off",
|
||||
"pv_power_w": "2000",
|
||||
|
||||
"house_ac_power_w": "700",
|
||||
"grid_power_w": "0",
|
||||
|
||||
"evcs_ac_power_w": "0",
|
||||
"evcs_charging": "off",
|
||||
"evcs_status_raw": "6",
|
||||
|
||||
"hysteresis_enabled": "on",
|
||||
"notifications_enabled": "on",
|
||||
"grid_latch": "off",
|
||||
|
||||
"grid_soc_on": "20",
|
||||
"grid_soc_off": "30",
|
||||
"grid_min_on_minutes": "25",
|
||||
"grid_cooldown_minutes": "15",
|
||||
"ac_loads_max_safe_w": "7800",
|
||||
|
||||
"last_on_at": "",
|
||||
"last_off_at": "",
|
||||
"last_signature": "",
|
||||
|
||||
"evcs_priority_active": "off",
|
||||
"night_grid_takeover_needed": "off",
|
||||
"evcs_source_mode": "Bloqué",
|
||||
"user_strategy": "auto_solar_plus",
|
||||
"evcs_surplus_state": "idle_wait_conditions"
|
||||
"evcs_surplus_state": "idle_wait_conditions",
|
||||
"charge_high_last_at": "",
|
||||
"charge_dwell_long_min": "20",
|
||||
"charge_dwell_court_min": "6",
|
||||
"balance_required_soc": "0",
|
||||
"balance_status": "ok",
|
||||
"balance_last_run": ""
|
||||
}
|
||||
|
||||
@@ -65,6 +65,11 @@ function summarize(out) {
|
||||
latchAction: d.latch_action,
|
||||
shouldNotify: !!d.should_notify,
|
||||
evcsGridOverride: !!c.evcs_grid_override,
|
||||
plafondDechargeW: c.plafond_decharge_w,
|
||||
dwellMinutes: c.dwell_minutes,
|
||||
dwellEcoule: c.dwell_ecoule,
|
||||
reserveDejaCondamnee: !!c.reserve_deja_condamnee,
|
||||
setChargeHighLastAt: !!d.set_charge_high_last_at,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,16 @@ function minutesAgo(fixedMs, minutes) {
|
||||
return stamp(fixedMs - minutes * 60000);
|
||||
}
|
||||
|
||||
// Le bilan energetique publie son horodatage au format "DD/MM HH:mm".
|
||||
function bilanRunAgo(fixedMs, minutes) {
|
||||
const p = new Intl.DateTimeFormat('fr-FR', {
|
||||
timeZone: 'Europe/Paris', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', hour12: false,
|
||||
}).formatToParts(new Date(fixedMs - minutes * 60000));
|
||||
const g = t => p.find(x => x.type === t).value;
|
||||
return `${g('day')}/${g('month')} ${g('hour')}:${g('minute')}`;
|
||||
}
|
||||
|
||||
const T = '2026-08-24T06:45:00+02:00'; // heure de l'incident de yoyo du 24/08
|
||||
const T_MS = new Date(T).getTime();
|
||||
|
||||
@@ -238,4 +248,215 @@ export const scenarios = [
|
||||
gridAction: 'turn_off',
|
||||
},
|
||||
},
|
||||
// =====================================================
|
||||
// RELACHEMENT SUR UNE DUREE (06/09/2026)
|
||||
// =====================================================
|
||||
// Mesure de la soiree du 05/09, seuil de relachement 2550 W, pas d'une
|
||||
// minute : pendant la cuisson (20:33 -> 21:37) treize accalmies, la plus
|
||||
// longue de CINQ minutes ; apres la cuisson (21:40 -> 01:00) une seule
|
||||
// accalmie de 201 minutes. La distribution est bimodale, le fosse est net,
|
||||
// et le relachement instantane tombait treize fois dedans.
|
||||
//
|
||||
// Le releve exact de cette soiree : SOC 65 %, bilan exigeant 92 % pour
|
||||
// atteindre le croisement PV — la reserve est deja condamnee, donc le
|
||||
// reseau du soir est du de toute facon et le long dwell s'applique.
|
||||
{
|
||||
id: 'h-dwell-accalmie-de-cuisson-ne-relache-pas',
|
||||
label: "LE CAS REEL : accalmie de 5 min au milieu du service (la plus longue mesuree) — le reseau n'est PAS rendu",
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
...parcQuatreModules,
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 5),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'hold_on_charge_encore_elevee',
|
||||
gridAction: 'none',
|
||||
dwellMinutes: 20,
|
||||
dwellEcoule: false,
|
||||
reserveDejaCondamnee: true,
|
||||
setChargeHighLastAt: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'h-dwell-ecoule-relache',
|
||||
label: 'CONTREFACTUEL : meme soiree, mais la maison est calme depuis 25 min — le reseau est rendu',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
...parcQuatreModules,
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 25),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'turn_off_vehicle_unplugged',
|
||||
gridAction: 'turn_off',
|
||||
dwellMinutes: 20,
|
||||
dwellEcoule: true,
|
||||
reserveDejaCondamnee: true,
|
||||
},
|
||||
},
|
||||
|
||||
// Les trois scenarios suivants sont a 8 MINUTES de calme, entre le dwell
|
||||
// court (6) et le long (20) : seul l'etat du bilan les separe.
|
||||
{
|
||||
id: 'h-dwell-long-quand-la-reserve-est-condamnee',
|
||||
label: 'Bilan frais, SOC 65 % sous les 92 % requis : dwell long, on garde le reseau a 8 min',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
...parcQuatreModules,
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 8),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'hold_on_charge_encore_elevee',
|
||||
gridAction: 'none',
|
||||
dwellMinutes: 20,
|
||||
reserveDejaCondamnee: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'h-dwell-court-quand-la-reserve-tient',
|
||||
label: 'PAIRE : bilan frais mais SOC 80 % au-dessus des 40 % requis — chaque kWh du soir couterait, dwell court, on rend a 8 min',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
...parcQuatreModules,
|
||||
soc_percent: '80',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 8),
|
||||
balance_required_soc: '40',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'turn_off_vehicle_unplugged',
|
||||
gridAction: 'turn_off',
|
||||
dwellMinutes: 6,
|
||||
reserveDejaCondamnee: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'h-dwell-bilan-perime-duree-courte',
|
||||
label: 'GARDE DE FRAICHEUR : memes chiffres que le dwell long, mais bilan vieux de 2 h — on retombe sur la duree courte',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
...parcQuatreModules,
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 8),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 120),
|
||||
},
|
||||
expect: {
|
||||
state: 'turn_off_vehicle_unplugged',
|
||||
gridAction: 'turn_off',
|
||||
dwellMinutes: 6,
|
||||
reserveDejaCondamnee: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'h-dwell-horodatage-ecrit-quand-la-charge-est-haute',
|
||||
label: "L'horodatage suit la MAISON : charge haute contacteur OUVERT — il est ecrit quand meme",
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
...parcQuatreModules,
|
||||
grid_latch: 'off',
|
||||
grid_state: 'off',
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '3000',
|
||||
charge_high_last_at: minutesAgo(T_MS, 30),
|
||||
},
|
||||
expect: {
|
||||
setChargeHighLastAt: true,
|
||||
},
|
||||
},
|
||||
// =====================================================
|
||||
// CAPTEURS MUETS (releve du 05/09/2026)
|
||||
// =====================================================
|
||||
// De 21:22:21 a 21:49:35, `sensor.victron_battery_current` et
|
||||
// `sensor.victron_battery_voltage` n'ont RIEN publie — 27 minutes — pendant
|
||||
// que `sensor.victron_consumption_power_l1` continuait normalement. Un
|
||||
// courant fige a -73,2 A pendant que la maison oscille entre 1,4 et 3,4 kW
|
||||
// est physiquement impossible : la valeur etait perimee. Sans repli, la
|
||||
// protection s'evanouissait pile a l'heure du diner.
|
||||
{
|
||||
id: 'h-capteur-tension-muette-la-regle-tient',
|
||||
label: 'Tension du parc muette, plafond en amperes lisible : nominal 51 V, la regle continue',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
battery_max_discharge_a: '100',
|
||||
battery_voltage_v: 'unavailable',
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 5),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'hold_on_charge_encore_elevee',
|
||||
gridAction: 'none',
|
||||
plafondDechargeW: 5100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'h-capteur-tout-muet-le-dwell-tient',
|
||||
label: 'Plafond ET tension muets en pleine protection : l horodatage persiste, on tient jusqu au bout du dwell',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
battery_max_discharge_a: 'unavailable',
|
||||
battery_voltage_v: 'unavailable',
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 5),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'hold_on_charge_encore_elevee',
|
||||
gridAction: 'none',
|
||||
plafondDechargeW: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'h-capteur-tout-muet-memoire-expiree',
|
||||
label: 'CONTREFACTUEL : memes capteurs muets mais derniere charge haute il y a 30 min — la memoire a expire, le reseau est rendu',
|
||||
time: T,
|
||||
overrides: {
|
||||
...recoveryBase,
|
||||
battery_max_discharge_a: 'unavailable',
|
||||
battery_voltage_v: 'unavailable',
|
||||
soc_percent: '65',
|
||||
house_ac_power_w: '2000',
|
||||
last_on_at: minutesAgo(T_MS, 40),
|
||||
charge_high_last_at: minutesAgo(T_MS, 30),
|
||||
balance_required_soc: '92',
|
||||
balance_last_run: bilanRunAgo(T_MS, 3),
|
||||
},
|
||||
expect: {
|
||||
state: 'turn_off_vehicle_unplugged',
|
||||
gridAction: 'turn_off',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user