Files
EVCS/tests/golden-acm/scenarios.mjs
Tallagrand Thierry 283ef42203 pv_mid passe aussi a 130 A
Le patch precedent n'avait releve que pv_boost : au-dessus de 80 % de SOC
c'est pv_mid qui commande, donc son plafond de 110 A restait le plus bas et
rien n'etait observable cet apres-midi. Table finale : 150 / 130 / 80 / 30,
toujours decroissante.

Applique a 13:31. Le courant est passe de 90 a 116 A dans la minute — mais
la charge de la maison a chute de 2,6 kW au meme moment, et le courant n'a
repris que 1,3 kW des 2,6 liberes tandis qu'un chargeur de plus se bridait.
Ce n'est donc PAS une preuve propre, et je ne la presente pas comme telle.

Ce que le releve montre vraiment : dans la tranche 80-90, le courant a
plafonne vers 116 A sans jamais approcher les 130, pendant que la tension
montait de 50,82 a 51,07 V vers la consigne. A ce niveau de SOC c'est la
TENSION qui contraint, pas le courant.

La ou le plafond mordait : a 12:36-12:42, SOC 78-80, le courant etait a
129 A pour une DVCC de 130 — colle dessus. C'est la tranche pv_boost, donc
c'est la charge du matin qui dira ce que le 150 A libere.

Cellules a 2 mV et aucune alarme sur toute la sequence. Golden ACM 20/20,
attendu de acm-jour-mid mis a jour dans le meme commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
2026-09-13 13:41:19 +02:00

218 lines
9.9 KiB
JavaScript

// Scenarios golden pour le node "Compute Charge Targets"
// (workflow "Victron - Auto Charge Manager V1.2", id cTeeHy4ePiVeELLh).
//
// Ce workflow decide ce que la BATTERIE a le droit de prendre : les limites
// DVCC en courant et en tension. Il gouverne donc le haut de la reserve — c'est
// lui qui plafonnait le parc a 93 % de SOC jusqu'au 06/09 — et il n'avait aucun
// test : il n'etait meme pas suivi dans le depot.
//
// Le fixture est un snapshot REEL, capture le 06/09/2026 en rendant le gabarit
// du node "HA Snapshot via Template" contre Home Assistant. Ne pas le remplacer
// par des valeurs inventees : c'est la lecon du 24/08, ou le fixture de
// l'hysteresis portait les valeurs de REPLI du code (22/35) au lieu des seuils
// de production (20/30), ce qui masquait un bug de bande.
//
// ⚠️ ORDRE DES BRANCHES, a garder en tete en lisant les attendus :
// 1. automatisation desactivee
// 2. secours SOC < 20 hors fenetres HC
// 3. takeover EVCS nuit (recover / hold)
// 4. hiver + HC nuit
// 5. ete + HC nuit
// 6. HC midi (rattrapage, seulement si SOC < 85 ET forecast < 8 ET PV < 1500)
// 7. fenetre jour (boost / mid / topoff / hold selon le SOC)
// 8. hors fenetre
// puis l'override se superpose a tout.
const ETE = '2026-07-15';
const HIVER = '2026-01-15';
const h = (jour, hhmm) => `${jour}T${hhmm}:00+02:00`;
const hHiver = (hhmm) => `${HIVER}T${hhmm}:00+01:00`;
// Le parc a quatre modules du 06/09 : le BMS autorise 53,2 V, et nos profils
// restent tous en dessous.
const parcNeuf = { soc_percent: '60', battery_voltage_v: '51.0', battery_current_a: '20' };
export const scenarios = [
// ---------------------------------------------------------------- gardes
{
id: 'acm-automatisation-desactivee',
label: "Toggle d'automatisation OFF : les reglages en place sont conserves tels quels",
time: h(ETE, '11:00'),
overrides: { ...parcNeuf, auto_enabled: 'off',
current_max_charge_current_a: '77', current_max_charge_voltage_v: '50.9' },
expect: { mode: 'automation_disabled', profil: 'manual_keep_current',
courantA: 77, tensionV: 50.9, publieCourant: false, publieTension: false },
},
{
id: 'acm-secours-soc-critique',
label: 'SOC 18 % hors fenetres HC : secours a 60 A / 50,4 V et alerte critique',
time: h(ETE, '10:00'),
overrides: { ...parcNeuf, soc_percent: '18' },
expect: { mode: 'emergency_rescue', profil: 'emergency_rescue',
courantA: 60, tensionV: 50.4, alerte: true },
},
// ------------------------------------------------------- takeover de nuit
{
id: 'acm-takeover-recharge-vers-la-cible',
label: 'Takeover EVCS, EDF ferme, SOC 30 sous la cible 50 : recharge a 30 A',
time: h(ETE, '02:30'),
overrides: { ...parcNeuf, soc_percent: '30', night_takeover: 'on',
edf_switch: 'on', night_target_soc: '50' },
expect: { mode: 'evcs_takeover_recover', profil: 'night_slow',
courantA: 30, tensionV: 50.1 },
},
{
id: 'acm-takeover-maintien-a-la-cible',
label: 'RELEVE 06/09 05:06 : cible atteinte, la batterie est tenue a 2 A pendant que le reseau porte la maison',
time: h(ETE, '05:06'),
overrides: { ...parcNeuf, soc_percent: '55', night_takeover: 'on',
edf_switch: 'on', night_target_soc: '49.4' },
expect: { mode: 'evcs_takeover_hold', profil: 'takeover_hold',
courantA: 2, tensionV: 50.1 },
},
{
id: 'acm-takeover-cible-plafonnee-a-95',
label: 'GARDE : une cible aberrante de 120 % est ramenee a 95, donc SOC 96 tient deja',
time: h(ETE, '03:00'),
overrides: { ...parcNeuf, soc_percent: '96', night_takeover: 'on',
edf_switch: 'on', night_target_soc: '120' },
expect: { mode: 'evcs_takeover_hold', courantA: 2 },
},
{
id: 'acm-takeover-sans-edf-ne-mord-pas',
label: "CONTREFACTUEL : takeover demande mais contacteur OUVERT — on ne bride pas la batterie sur du reseau absent",
time: h(ETE, '03:00'),
overrides: { ...parcNeuf, soc_percent: '30', night_takeover: 'on',
edf_switch: 'off', night_target_soc: '50' },
expect: { mode: 'summer_night_idle' },
},
// ------------------------------------------------------------ nuit HC
{
id: 'acm-hiver-nuit-forecast-sombre',
label: 'Hiver, HC nuit, demain sombre (5 kWh) : on remonte vers 80 %',
time: hHiver('03:00'),
overrides: { ...parcNeuf, soc_percent: '50',
today_remaining_kwh: '0', tomorrow_kwh: '5' },
expect: { mode: 'winter_night_charge_window', profil: 'night_slow', courantA: 30 },
},
{
id: 'acm-hiver-nuit-forecast-ensoleille',
label: 'PAIRE : meme nuit d hiver mais demain ensoleille (30 kWh), cible 40 % deja depassee',
time: hHiver('03:00'),
overrides: { ...parcNeuf, soc_percent: '50',
today_remaining_kwh: '0', tomorrow_kwh: '30' },
expect: { mode: 'winter_night_idle' },
},
{
id: 'acm-ete-nuit-secours',
label: 'Ete, HC nuit, SOC 20 sous le seuil de 22 : secours',
time: h(ETE, '03:00'),
overrides: { ...parcNeuf, soc_percent: '20' },
expect: { mode: 'summer_night_rescue_window', profil: 'night_slow' },
},
{
id: 'acm-ete-nuit-repos',
label: 'PAIRE : meme nuit d ete a SOC 50, aucun besoin reseau',
time: h(ETE, '03:00'),
overrides: { ...parcNeuf, soc_percent: '50' },
expect: { mode: 'summer_night_idle' },
},
// ------------------------------------------------------------ midi HC
{
id: 'acm-midi-rattrapage',
label: 'HC midi, SOC 60, journee sombre (5 kWh) et PV a 800 W : rattrapage reseau a 30 A',
time: h(ETE, '13:00'),
overrides: { ...parcNeuf, soc_percent: '60', today_remaining_kwh: '5', pv_power_w: '800' },
expect: { mode: 'midday_catchup_window', profil: 'midday_grid_catchup',
courantA: 30, tensionV: 50.8 },
},
{
id: 'acm-midi-pas-de-rattrapage-si-le-soleil-revient',
label: 'CONTREFACTUEL : meme midi mais PV a 2000 W — le rattrapage ne mord plus, on repasse en profil jour',
time: h(ETE, '13:00'),
overrides: { ...parcNeuf, soc_percent: '60', today_remaining_kwh: '5', pv_power_w: '2000' },
expect: { mode: 'day_pv_boost', profil: 'pv_boost' },
},
// ------------------------------------------------------------ jour
{
// 13/09/2026 — 130 -> 150 A. Mesure de 10:39 a 12:50 : la DVCC etait a
// 130 A et la batterie prenait 114 a 127 A, soit 88 a 98 % du plafond,
// avec une tension plate a 50,6 V (donc aucune absorption : le courant ne
// retombait pas de lui-meme, il butait). Le BMS declare 250 A et 53,2 V,
// les 135 cellules tiennent dans 2 mV, aucun module ne bloque la charge.
// Le 130 A venait de l'ANCIEN parc, sorti le 12/09. 150 A est un palier,
// pas une cible : on regarde ce qu'il libere avant d'aller plus loin.
id: 'acm-jour-boost',
label: 'Jour, SOC 60 sous 80 : capture PV maximale, 150 A a 51,5 V (releve du 13/09 : la batterie butait sur 130)',
time: h(ETE, '11:00'),
overrides: { ...parcNeuf, soc_percent: '60', pv_power_w: '4000' },
expect: { mode: 'day_pv_boost', profil: 'pv_boost', courantA: 150, tensionV: 51.5 },
},
{
id: 'acm-jour-mid',
// 13/09/2026 — 110 -> 130 A, meme raison que pv_boost : le 110 A venait de
// l'ANCIEN parc. A 12:49, SOC 82, la batterie prenait encore 127 A juste
// avant que l'ACM ne la ramene a son plafond. La table reste decroissante :
// 150 / 130 / 80 / 30.
label: 'Jour, SOC 85 entre 80 et 90 : 130 A a 51,5 V',
time: h(ETE, '11:00'),
overrides: { ...parcNeuf, soc_percent: '85', pv_power_w: '4000', battery_voltage_v: '50.5' },
expect: { mode: 'day_pv_mid', profil: 'pv_mid', courantA: 130, tensionV: 51.5 },
},
{
id: 'acm-jour-topoff-52v4',
label: "LE CORRECTIF DU 06/09 : SOC 93 dans la tranche 90-95, la tension monte a 52,4 V et le parc reprend de la charge",
time: h(ETE, '15:00'),
overrides: { ...parcNeuf, soc_percent: '93', pv_power_w: '4000', battery_voltage_v: '51.1' },
expect: { mode: 'day_pv_topoff', profil: 'pv_topoff', courantA: 80, tensionV: 52.4 },
},
{
id: 'acm-jour-hold-au-dessus-de-95',
label: "GARDE : au-dessus de 95 % on redescend a 50,6 V — on atteint le haut, on n'y sejourne pas",
time: h(ETE, '15:00'),
overrides: { ...parcNeuf, soc_percent: '96', pv_power_w: '4000' },
expect: { mode: 'day_pv_hold', profil: 'pv_hold', courantA: 30, tensionV: 50.6 },
},
{
id: 'acm-hors-fenetre',
label: 'Hors fenetre utile (21 h en ete) : profil de maintien',
time: h(ETE, '21:00'),
overrides: { ...parcNeuf, soc_percent: '70' },
expect: { mode: 'off_window_idle', profil: 'pv_hold', courantA: 30, tensionV: 50.6 },
},
// ------------------------------------------------------------ override
{
id: 'acm-override-manuel-actif',
label: 'Override manuel valide : les valeurs saisies prennent la main sur le profil calcule',
time: h(ETE, '11:00'),
overrides: { ...parcNeuf, soc_percent: '60', override_enabled: 'on',
override_profile: 'manual', override_current_a: '12', override_voltage_v: '50.0',
override_until: '2026-07-15 23:00:00' },
expect: { overrideActif: true, profil: 'override_manual', courantA: 12, tensionV: 50 },
},
{
id: 'acm-override-expire-sans-effet',
label: "CONTREFACTUEL : meme override mais l'echeance est passee — le profil calcule reprend",
time: h(ETE, '11:00'),
overrides: { ...parcNeuf, soc_percent: '60', override_enabled: 'on',
override_profile: 'manual', override_current_a: '12', override_voltage_v: '50.0',
override_until: '2026-07-15 09:00:00' },
expect: { overrideActif: false, mode: 'day_pv_boost', courantA: 150 },
},
// ------------------------------------------------------------ publication
{
id: 'acm-pas-de-publication-si-identique',
label: "GARDE : cible egale a ce qui est deja regle — on n'ecrit ni le courant ni la tension",
time: h(ETE, '21:00'),
overrides: { ...parcNeuf, soc_percent: '70',
current_max_charge_current_a: '30', current_max_charge_voltage_v: '50.6' },
expect: { publieCourant: false, publieTension: false },
},
];