3562 lines
149 KiB
JavaScript
3562 lines
149 KiB
JavaScript
// R3 — Scénarios golden EVCS. Chaque scénario = instant simulé + overrides du
|
||
// snapshot HA réel (tests/golden/fixture-snapshot.json) + résumé attendu.
|
||
// Les attendus sont FIGÉS après vérification manuelle : un diff = régression
|
||
// (ou évolution volontaire → mettre à jour l'attendu dans le même commit).
|
||
//
|
||
// Fenêtres (helpers réels) : night_hc 01:24-07:24 | morning 07:30-11:00 |
|
||
// midday 12:24-14:24 | evening 17:00-20:30 | sinon outside_window.
|
||
// Seuils : hard floor nuit 22 | cible matin forecast fort 35 (tomorrow_kwh 57) |
|
||
// soc_start_min 80 | morning_soc_min 35 | midday_soc_min 75.
|
||
|
||
const NIGHT = '2026-08-08T01:30:00+02:00';
|
||
const MORNING = '2026-08-08T09:00:00+02:00';
|
||
const MIDDAY = '2026-08-08T13:00:00+02:00';
|
||
const AFTERNOON = '2026-08-08T15:30:00+02:00';
|
||
const EVENING = '2026-08-08T18:00:00+02:00';
|
||
|
||
// Fenêtre d'établissement du réseau (31/08/2026) : horodatage de la dernière
|
||
// transition d'AC-IN, en secondes epoch. Le harnais fige l'horloge sur `time`,
|
||
// donc on exprime l'âge de la transition par rapport à cet instant simulé.
|
||
const MORNING_TS = Date.parse(MORNING) / 1000;
|
||
|
||
// Budget voiture de la nuit (30/09) : releve du 30/09 01:25.
|
||
const BUDGET_NUIT_BASE = {
|
||
soc_percent: '57', allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-16', evcs_charging_switch: 'off',
|
||
balance_status: 'ok', balance_last_run: '30/09 01:20',
|
||
balance_required_soc: '39.2', balance_required_soc_prudent: '44.7',
|
||
balance_energy_to_cover_kwh: '5.2', balance_night_vehicle_budget_kwh: '3.7',
|
||
};
|
||
const EN_CHARGE_BATTERIE = {
|
||
evcs_charging_switch: 'on', charge_permission_now: 'on', control_latch: 'on',
|
||
// P12 charge doucement : 6 A, batterie a -25 A (le garde dur de la bande
|
||
// 40-60 % est a -30 A).
|
||
evcs_current_a: '6', evcs_ac_power_w: '1400', battery_current_a: '-25',
|
||
prev_surplus_state: 'charging_hold_battery_assist_night',
|
||
};
|
||
const VEHICLE_NIGHT = {
|
||
// 30/09 : les scenarios de nuit historiques decrivent le mode « charge
|
||
// complete » (EDF finit la charge). Le mode par defaut « sans EDF » a ses
|
||
// propres scenarios (budget-*).
|
||
night_charge_mode: 'complete',
|
||
evcs_vehicle_connected: 'on',
|
||
evcs_device_connected: 'on',
|
||
pv_power_w: '0',
|
||
pv_power_10m_avg_w: '0',
|
||
pv_trend_w: '0',
|
||
house_ac_power_w: '700',
|
||
vebus_ac_out_l1_power_w: '700',
|
||
battery_current_a: '-14',
|
||
};
|
||
const BUDGET_NUIT = { ...VEHICLE_NIGHT, ...BUDGET_NUIT_BASE };
|
||
|
||
export const scenarios = [
|
||
|
||
// ================= NUIT HC (P7 + historique) =================
|
||
{
|
||
id: 'night-hc-p7-toggle-on',
|
||
label: 'Nuit HC, VW branchée, toggle HC nuit ON, SOC 53, EDF OFF → demande réseau (P7)',
|
||
time: NIGHT,
|
||
overrides: { ...VEHICLE_NIGHT, soc_percent: '53', allow_grid_hc_night: 'on' },
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
state: 'night_wait_grid_takeover',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-hc-p7-edf-on-start',
|
||
label: 'Nuit HC, cycle suivant : EDF ON, batterie tenue +2A → démarrage charge HC',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '53', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
state: 'start_grid_hc_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-hc-toggle-off-historique',
|
||
label: 'Nuit HC, toggle OFF → comportement historique battery_assist_night (idle)',
|
||
time: NIGHT,
|
||
overrides: { ...VEHICLE_NIGHT, soc_percent: '53', allow_grid_hc_night: 'off' },
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'battery_assist_night',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-soc-sous-cible',
|
||
label: 'Nuit HC, SOC 30 < cible matin 35 → grid_forced',
|
||
time: NIGHT,
|
||
overrides: { ...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on' },
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_forced',
|
||
state: 'night_wait_grid_takeover',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-hard-floor',
|
||
label: 'Nuit HC, SOC 20 ≤ hard floor 22 → hard stop protection batterie + demande réseau',
|
||
time: NIGHT,
|
||
overrides: { ...VEHICLE_NIGHT, soc_percent: '20', allow_grid_hc_night: 'on' },
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_forced',
|
||
state: 'battery_current_hard_stop',
|
||
decision: 'stop_hard',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-sans-vehicule',
|
||
label: 'Nuit HC sans véhicule → bloqué, takeover relâché',
|
||
time: NIGHT,
|
||
overrides: { ...VEHICLE_NIGHT, evcs_vehicle_connected: 'off', soc_percent: '53' },
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'vehicle_not_connected',
|
||
decision: 'stop_not_connected',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ===== LE CONSEILLER NE FIXE PLUS LA CIBLE (13/09/2026) =====
|
||
// Les trois scenarios du premier cablage (applique / perime / toggle OFF)
|
||
// sont remplaces par celui-ci, qui pinne l'ABSENCE de cablage : toutes les
|
||
// conditions de l'ancienne regle sont reunies — toggle ON, statut ok,
|
||
// horodatage frais, valeur dans les bornes — et la cible doit rester celle
|
||
// des regles de prevision. Si quelqu'un recable un jour, ce test tombe.
|
||
// Reference : night-soc-sous-cible (SOC 30 < cible regle 35 → grid_forced).
|
||
{
|
||
id: 'advisor-cible-ignoree',
|
||
label: 'Nuit HC, SOC 30, helpers advisor au complet et frais → IGNORES, cible des regles (grid_forced)',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on',
|
||
advisor_apply_night_target: 'on',
|
||
advisor_night_target_soc: '22',
|
||
advisor_status: 'ok',
|
||
advisor_last_run: '08/08 01:20',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_forced',
|
||
state: 'night_wait_grid_takeover',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'p9-depart-vw-grid',
|
||
label: 'Matin 08:00, VW sélectionnée, départ 08:50, toggle P9 ON, SOC 45, PV nul → réseau autorisé + EDF demandé',
|
||
time: '2026-08-10T08:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
evcs_vehicle: 'VW',
|
||
departure_time: '08:50:00',
|
||
departure_time_jeep: '06:30:00',
|
||
allow_grid_departure_urgent: 'on',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'grid_hc_allowed',
|
||
state: 'wait_grid_source',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'p9-depart-jeep-passe',
|
||
label: 'Matin 08:00, JEEP sélectionnée (départ 06:30 passé), toggle P9 ON → aucune fenêtre, bloqué',
|
||
time: '2026-08-10T08:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
evcs_vehicle: 'JEEP',
|
||
departure_time: '08:50:00',
|
||
departure_time_jeep: '06:30:00',
|
||
allow_grid_departure_urgent: 'on',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'idle_wait_source',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
// ZERO (moto, 28/09/2026) : elle a SA propre heure de depart. Meme matin que
|
||
// p9-depart-vw-grid, VW a 08:50 : la ZERO part a 08:30, donc la fenetre P9
|
||
// est ouverte pour elle aussi.
|
||
id: 'p9-depart-zero-sa-propre-heure',
|
||
label: 'Matin 08:00, ZERO sélectionnée, départ ZERO 08:30, toggle P9 ON → réseau autorisé sur SON heure',
|
||
time: '2026-08-10T08:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
evcs_vehicle: 'ZERO',
|
||
departure_time: '08:50:00',
|
||
departure_time_jeep: '06:30:00',
|
||
departure_time_zero: '08:30:00',
|
||
allow_grid_departure_urgent: 'on',
|
||
},
|
||
expect: { profile: 'morning_solar', sourceMode: 'grid_hc_allowed', edfOnAction: true },
|
||
},
|
||
{
|
||
id: 'p9-depart-zero-passe',
|
||
label: 'CONTREFACTUEL — ZERO partie a 07:00 : sa fenetre est passee, meme si celle de la VW (08:50) serait ouverte',
|
||
time: '2026-08-10T08:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
evcs_vehicle: 'ZERO',
|
||
departure_time: '08:50:00',
|
||
departure_time_jeep: '06:30:00',
|
||
departure_time_zero: '07:00:00',
|
||
allow_grid_departure_urgent: 'on',
|
||
},
|
||
expect: { profile: 'morning_solar', sourceMode: 'blocked', edfOnAction: false },
|
||
},
|
||
{
|
||
id: 'p9-done-idle-collant',
|
||
label: 'Matin 08:10, VW pleine détectée (prev=vehicle_full_or_paused) pendant la fenêtre départ → veille collante, réseau relâché',
|
||
time: '2026-08-10T08:10:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
evcs_vehicle: 'VW',
|
||
departure_time: '08:50:00',
|
||
departure_time_jeep: '06:30:00',
|
||
allow_grid_departure_urgent: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2',
|
||
prev_surplus_state: 'vehicle_full_or_paused',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'departure_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'p9-toggle-off',
|
||
label: 'Matin 08:00, VW départ 08:50 mais toggle P9 OFF → comportement historique (bloqué, pas de réseau)',
|
||
time: '2026-08-10T08:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
evcs_vehicle: 'VW',
|
||
departure_time: '08:50:00',
|
||
departure_time_jeep: '06:30:00',
|
||
allow_grid_departure_urgent: 'off',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'idle_wait_source',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ===== P10 09/08/2026 : mémoire "véhicule plein" en journée =====
|
||
// Incident 09/08 09:49 : VW pleine, le surplus solaire retentait une charge
|
||
// toutes les ~15 min (statut "Charged", 0 W) → usure relais. Désormais
|
||
// collant jusqu'au débranchement, avec héritage des états done de la nuit.
|
||
// ===== Fix H (07/09/2026) : credibilite du statut "charged" =====
|
||
// Releve reel : la borne a annonce "charged" a 14:28:18 apres 2 min 42 s de
|
||
// charge et 0,10 kWh livre, ce qui a bloque le vehicule deux heures. Les deux
|
||
// coupures (vraie fin de charge et interruption) sont ELECTRIQUEMENT
|
||
// identiques, mesurees a la seconde : seule la duree de session les separe.
|
||
{
|
||
id: 'charged-interrompu-ne-latche-pas',
|
||
label: 'Statut 3 apres 2 min 42 s et 0,10 kWh livre (releve 07/09) -> interruption, PAS de collant',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '96', pv_power_w: '2600', pv_power_10m_avg_w: '2500',
|
||
pv_trend_w: '0', battery_current_a: '2',
|
||
house_ac_power_w: '2100', vebus_ac_out_l1_power_w: '2100',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
evcs_current_a: '0',
|
||
evcs_ac_power_w: '0',
|
||
evcs_status_raw: '3',
|
||
evcs_charging_time: '2m 42s',
|
||
evcs_session_energy_kwh: '0.1',
|
||
prev_surplus_state: 'charging_hold',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'charging_step_up_battery',
|
||
decision: 'step_up',
|
||
desiredCharging: true,
|
||
currentA: 7,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'charged-session-etablie-latche',
|
||
label: 'CONTREFACTUEL : meme statut 3 apres 1 h 32 et 4,30 kWh (releve VW 06/09) -> vraie fin, collant',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '96', pv_power_w: '2600', pv_power_10m_avg_w: '2500',
|
||
pv_trend_w: '0', battery_current_a: '2',
|
||
house_ac_power_w: '2100', vebus_ac_out_l1_power_w: '2100',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
evcs_current_a: '0',
|
||
evcs_ac_power_w: '0',
|
||
evcs_status_raw: '3',
|
||
evcs_charging_time: '1h 32m 0s',
|
||
evcs_session_energy_kwh: '4.3',
|
||
prev_surplus_state: 'charging_hold',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'blocked',
|
||
state: 'day_vehicle_full_idle',
|
||
decision: 'stop_vehicle_full_or_paused',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'charged-energie-nulle-latche',
|
||
label: 'CONTREFACTUEL : statut 3 avec ZERO kWh livre (vehicule plein a l arrivee, cas P10) -> collant',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '96', pv_power_w: '2600', pv_power_10m_avg_w: '2500',
|
||
pv_trend_w: '0', battery_current_a: '2',
|
||
house_ac_power_w: '2100', vebus_ac_out_l1_power_w: '2100',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
evcs_current_a: '0',
|
||
evcs_ac_power_w: '0',
|
||
evcs_status_raw: '3',
|
||
evcs_charging_time: '3m 20s',
|
||
evcs_session_energy_kwh: '0',
|
||
prev_surplus_state: 'charging_hold',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'blocked',
|
||
state: 'day_vehicle_full_idle',
|
||
decision: 'stop_vehicle_full_or_paused',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-charged-interrompu-ne-latche-pas',
|
||
label: 'Nuit HC : statut 3 apres 3 min et 0,10 kWh -> interruption, la charge HC continue',
|
||
time: NIGHT,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
night_session_active: 'on',
|
||
allow_grid_hc_night: 'on', night_charge_mode: 'complete',
|
||
soc_percent: '55', pv_power_w: '0', pv_power_10m_avg_w: '0',
|
||
pv_trend_w: '0', battery_current_a: '0',
|
||
house_ac_power_w: '800', vebus_ac_out_l1_power_w: '800',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
grid_switch: 'on',
|
||
acin1_available: '1',
|
||
evcs_current_a: '0',
|
||
evcs_ac_power_w: '0',
|
||
evcs_status_raw: '3',
|
||
evcs_charging_time: '3m 0s',
|
||
evcs_session_energy_kwh: '0.1',
|
||
prev_surplus_state: 'charging_step_up_grid_hc',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
state: 'night_wait_grid_takeover_runtime',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'day-vw-pleine-statut3',
|
||
label: 'Matin PV fort, session armée 0 W, statut 3 → day_vehicle_full_idle collant (stop propre, plus de solaire)',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
evcs_current_a: '0',
|
||
evcs_ac_power_w: '0',
|
||
evcs_status_raw: '3',
|
||
prev_surplus_state: 'charging_hold',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'day_vehicle_full_idle',
|
||
decision: 'stop_vehicle_full_or_paused',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'day-full-heritage-nuit',
|
||
label: 'Matin PV fort, prev=night_recovery_done_idle (nuit finie pleine) → AUCUNE re-tentative solaire',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'day_vehicle_full_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'day-full-echappatoire-force-charge',
|
||
label: 'Matin PV fort, mémoire plein active MAIS force_charge_now → la charge repart (échappatoire utilisateur)',
|
||
time: MORNING,
|
||
overrides: {
|
||
victron_evcs_strategy: 'force_charge_now',
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'day_vehicle_full_idle',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'start_manual_local_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ===== P11 12/08/2026 : reset du collant au changement de véhicule =====
|
||
// Incident : JEEP chargée (night_recovery_done_idle) → débranchée → VW
|
||
// branchée à sa place → l'état collant survivait et bloquait la VW neuve.
|
||
// Signal : charging_time repart de 0 (< 90 s) = session neuve → on neutralise.
|
||
{
|
||
id: 'p11-changement-vehicule-nuit',
|
||
label: 'Nuit HC, prev=night_recovery_done_idle (JEEP finie) MAIS charging_time 15 s (VW fraîche) → collant neutralisé, retest',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '54', allow_grid_hc_night: 'on',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
evcs_charging_time: '15s',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
state: 'night_wait_grid_takeover',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'p11-changement-vehicule-jour',
|
||
label: 'Jour PV fort, prev=day_vehicle_full_idle MAIS charging_time 20 s (nouveau véhicule) → retest solaire',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'day_vehicle_full_idle',
|
||
evcs_charging_time: '20s',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'p11-session-etablie-reste-collant',
|
||
label: 'Jour PV fort, prev=day_vehicle_full_idle, charging_time 2h (session établie) → reste collant (pas de reset abusif)',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'day_vehicle_full_idle',
|
||
evcs_charging_time: '2h 05m 12s',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'day_vehicle_full_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ============ P11-bis : SWAP RAPIDE, BORNE FIGEE (19/09/2026) ============
|
||
// Incident du 19/09 : VW debranchee ~14:50, JEEP branchee, mais le swap n'a
|
||
// dure que 14 s → la borne a GELE son etat (charging_time reste a 1h25, status
|
||
// fige). P11 (base sur charging_time <= 90s) restait aveugle → le collant VW
|
||
// bloquait la JEEP. Le last_changed du capteur de connexion, lui, a bouge :
|
||
// c'est le second signal de fraicheur.
|
||
{
|
||
id: 'p11bis-swap-rapide-borne-figee',
|
||
label: 'Jour PV fort, prev=day_vehicle_full_idle, charging_time FIGE 1h25 MAIS reconnexion il y a 30 s → retest (second signal)',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'day_vehicle_full_idle',
|
||
// la borne n'a PAS reinitialise : charging_time fige haut
|
||
evcs_charging_time: '1h 25m',
|
||
// mais la connexion a change il y a 30 s (le swap rapide)
|
||
evcs_vehicle_connected_ts: String(MORNING_TS - 30),
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
{
|
||
id: 'p11bis-session-etablie-reconnexion-vieille',
|
||
label: 'CONTREFACTUEL : charging_time 1h25 ET reconnexion il y a 2h → PAS frais → reste collant (pas de reset abusif)',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'day_vehicle_full_idle',
|
||
evcs_charging_time: '1h 25m',
|
||
evcs_vehicle_connected_ts: String(MORNING_TS - 7200),
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'day_vehicle_full_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
},
|
||
},
|
||
|
||
{
|
||
id: 'reset-borne-force-retest',
|
||
label: 'Bouton reset borne ON : session pourtant etablie (charging_time 1h25 + reconnexion vieille) → retest force + ack off',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'day_vehicle_full_idle',
|
||
evcs_charging_time: '1h 25m',
|
||
evcs_vehicle_connected_ts: String(MORNING_TS - 7200),
|
||
reset_borne: 'on',
|
||
},
|
||
expect: {
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
resetBorneAck: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ================= FIN DE CHARGE NUIT (P8) =================
|
||
{
|
||
id: 'night-vw-pleine-transition',
|
||
label: 'Nuit HC 04:00, cycle après vehicle_full_or_paused, SOC ≥ cible → recovery done, takeover relâché',
|
||
time: '2026-08-08T04:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '60', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'vehicle_full_or_paused',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-vw-pleine-autoentretien',
|
||
label: 'Nuit HC 05:00, veille auto-entretenue SOC ≥ cible, EDF déjà coupé → reste en recovery done',
|
||
time: '2026-08-08T05:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '55', allow_grid_hc_night: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-vw-pleine-batterie-basse-edf-maintenu',
|
||
label: 'Nuit HC 05:00, VW pleine MAIS SOC 30 ≤ cible 35 → takeover maintenu (recharge batterie), EVCS en veille',
|
||
time: '2026-08-08T05:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_session_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
|
||
// ===== FIX A/D 08/08/2026 : anti-rebond EDF post-charge (incident nuit 07-08/08) =====
|
||
{
|
||
id: 'night-recovery-atteinte-soc-cible',
|
||
label: 'Nuit HC 04:15, VW pleine, SOC 35 == cible (le cas du rebond) → recovery done, takeover coupé une fois pour toutes',
|
||
time: '2026-08-08T04:15:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '35', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-recovery-done-bande-collante',
|
||
label: 'Nuit HC 04:45, recovery done, SOC 34 dans la bande (cible−2 < 34 < 35) → PAS de ré-engagement (anti-rebond)',
|
||
time: '2026-08-08T04:45:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '34', allow_grid_hc_night: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-recovery-done-pas-de-reengagement',
|
||
label: 'Nuit HC 05:30, recovery done et SOC 32 sous la cible → AUCUN ré-engagement (la recovery est une passe unique, 27/08)',
|
||
time: '2026-08-08T05:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '32', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
// Collant désormais : la recovery ayant eu lieu, l'état reste
|
||
// 'night_recovery_done_idle' même sous la cible — c'est lui qui autorise
|
||
// la coupure EDF immédiate côté hystérésis (E2) dès SOC >= grid_soc_off.
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
{
|
||
// L'oscillateur mesuré : nuit du 26-27/08, Jeep pleine restée branchée,
|
||
// cible 35. L'ancienne bande de 2 points ré-engageait le takeover pile à
|
||
// SOC 33 -> EDF ON 04:24/05:31/07:05, OFF 05:16/06:50/07:30, 2,5 kWh
|
||
// importés une nuit où le lendemain était annoncé à 43 kWh.
|
||
id: 'night-oscillateur-nuit-2627',
|
||
label: 'Nuit HC 05:31, recovery done et SOC 33 (cible 35 − 2) → le takeover NE se ré-engage pas : plus d\'oscillateur',
|
||
time: '2026-08-27T05:31:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '33', allow_grid_hc_night: 'on',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-11', night_takeover_prev: 'off',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
{
|
||
// Le filet de sécurité doit rester intact : sous le plancher dur, le
|
||
// takeover se ré-engage même si la recovery a déjà eu lieu, parce que
|
||
// batteryBelowHardFloor est un terme INDÉPENDANT de nightGridTakeoverNeeded.
|
||
id: 'night-recovery-done-plancher-dur',
|
||
label: 'Nuit HC 05:31, recovery done mais SOC 21 sous le plancher dur 22 → ré-engagement du takeover (sécurité)',
|
||
time: '2026-08-27T05:31:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '21', allow_grid_hc_night: 'on',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-11', night_takeover_prev: 'off',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
|
||
{
|
||
id: 'night-vw-statut-charged',
|
||
label: 'Nuit HC 03:00, statut EVCS brut 3 (charged), sans état VDF préalable → fin de charge immédiate (Fix D)',
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '60', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
evcs_status_raw: '3',
|
||
prev_surplus_state: '',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ===== FIX E 09/08/2026 : latch "charge terminée" PENDANT la session active =====
|
||
// Incident nuit 08-09/08 : status 3 en session armée → source-lost zombie 25 min,
|
||
// état collant jamais écrit, statut retombe à l'ouverture du relais → rebond EDF.
|
||
{
|
||
id: 'night-charged-en-session-active',
|
||
label: 'Nuit HC 04:30, session ARMÉE (relais fermé, 0 W), statut 3 → arrêt propre + latch collant immédiat',
|
||
time: '2026-08-09T04:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '51', allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
evcs_current_a: '0',
|
||
evcs_ac_power_w: '0',
|
||
evcs_status_raw: '3',
|
||
prev_surplus_state: 'charging_hold',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'stop_vehicle_full_or_paused',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-flap-statut-apres-latch',
|
||
label: 'Nuit HC 05:00, relais ouvert, statut retombé à 1, prev=night_recovery_done_idle → collant, AUCUN re-P7/EDF',
|
||
time: '2026-08-09T05:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '51', allow_grid_hc_night: 'on',
|
||
evcs_charging_switch: 'off',
|
||
evcs_status_raw: '1',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-hard-stop-necrase-pas-done',
|
||
label: 'Nuit HC 06:55, VW pleine relance une micro-charge sur batterie (−60 A) → le hard stop N\'ÉCRASE PAS le collant (Fix F)',
|
||
time: '2026-08-09T06:55:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '40', allow_grid_hc_night: 'on',
|
||
battery_current_a: '-60',
|
||
evcs_charging_switch: 'on',
|
||
charge_permission_now: 'on',
|
||
control_latch: 'on',
|
||
evcs_current_a: '6',
|
||
evcs_ac_power_w: '1400',
|
||
evcs_status_raw: '2',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'blocked',
|
||
state: 'night_recovery_done_idle',
|
||
decision: 'stop_vehicle_full_or_paused',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ================= JOUR SOLAIRE =================
|
||
{
|
||
id: 'morning-solaire-fort',
|
||
label: 'Matin, PV 4,5 kW, SOC 60, batterie +10A → charge solaire',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'morning-nuageux',
|
||
label: 'Matin nuageux, PV 300 W, SOC 40 → pas de démarrage',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '40', pv_power_w: '300', pv_power_10m_avg_w: '250',
|
||
pv_trend_w: '-50', battery_current_a: '-3',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'blocked',
|
||
state: 'idle_wait_source',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'midday-abondance-p6',
|
||
label: 'Midi abondance P6 : PV avg 4,5 kW + batterie +25A, SOC 66 < plancher 75 → démarre quand même',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '66', pv_power_w: '5000', pv_power_10m_avg_w: '4500',
|
||
pv_trend_w: '200', battery_current_a: '25',
|
||
house_ac_power_w: '1200', vebus_ac_out_l1_power_w: '1200',
|
||
},
|
||
expect: {
|
||
profile: 'midday_hc_or_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'midday-inversion-start-hold',
|
||
label: 'INVARIANT maintien<=demarrage : midi, SOC 58 dans la bande [40,60], charge 18 A en cours sous abondance → doit CONTINUER (incident 24/08 12:24-12:28)',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 11:30:00', // min_run largement écoulé
|
||
// Relevé VRM du 24/08 12:25 : PV 10,24 kW, charges 3815 W (dont EVCS 3502 W),
|
||
// batterie +127 A. SOC 58 % : au-dessus du plancher de démarrage sous
|
||
// abondance (40 %) mais sous le plancher de maintien de la règle midi
|
||
// (max(29, 75-15) = 60 %). Avant correctif : arrêt, puis redémarrage au
|
||
// cycle suivant → cycle limite de 3 min et PV écrêtée.
|
||
soc_percent: '58', pv_power_w: '10240', pv_power_10m_avg_w: '9800',
|
||
pv_trend_w: '200', battery_current_a: '127',
|
||
evcs_current_a: '18', evcs_set_current_a: '18', evcs_command_current_a: '18',
|
||
evcs_ac_power_w: '3502',
|
||
house_ac_power_w: '3815', vebus_ac_out_l1_power_w: '3815',
|
||
},
|
||
expect: {
|
||
profile: 'midday_hc_or_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'charging_step_up_battery',
|
||
decision: 'step_up',
|
||
desiredCharging: true,
|
||
currentA: 19,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
// ======== REPLI SANS BILAN : LA BORNE N'EST PLUS EMPRUNTEE (18/09/2026) ========
|
||
// Quand la garde du bilan tombe, la consigne revenait a `maison + plafond de
|
||
// borne` meme sans voiture -- le defaut d'origine, revenu au moment ou le
|
||
// systeme est deja degrade. Ce scenario fige le comportement corrige, et rien
|
||
// d'autre dans la suite ne le couvre : sans lui, remettre le plafond brut
|
||
// dans la branche de repli laisse les 105 autres scenarios verts.
|
||
// ⚠️ L'attendu est calcule sur la FIXTURE, jamais sur une mesure de
|
||
// production : maison 900 W + part voiture nulle, plus la marge d'un pas de
|
||
// courant. Le rejouer sur l'installation vivante donnerait autre chose sans
|
||
// que rien ne soit casse.
|
||
{
|
||
id: 'consigne-repli-sans-vehicule-n-emprunte-plus-la-borne',
|
||
label: "REPLI 18/09 — midi, fenetre HC autorisee, AUCUN vehicule, bilan indisponible : la consigne couvre la maison au lieu d'emprunter le plafond de la borne",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '50', pv_power_w: '100', pv_power_10m_avg_w: '100',
|
||
pv_trend_w: '0', battery_current_a: '-4',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0', evcs_current_a: '0',
|
||
grid_setpoint_w: '700',
|
||
// Aucun champ `balance_*` : la garde tombe, on est dans la branche de repli.
|
||
},
|
||
expect: {
|
||
profile: 'midday_hc_or_solar',
|
||
sourceMode: 'grid_hc_allowed',
|
||
gridSetpointW: 1130,
|
||
},
|
||
},
|
||
{
|
||
id: 'midday-hc-garde-soc',
|
||
label: 'Midi sans PV, SOC 50 > 35, HC midi autorisé + EDF ON → grid proposé mais gardé (soc>35)',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '50', pv_power_w: '100', pv_power_10m_avg_w: '100',
|
||
pv_trend_w: '0', battery_current_a: '-4',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
},
|
||
expect: {
|
||
profile: 'midday_hc_or_solar',
|
||
sourceMode: 'grid_hc_allowed',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'apresmidi-provocation-p3',
|
||
// 03/09/2026 — echantillon rendu COHERENT EN ENERGIE. Il portait PV 300 W,
|
||
// maison 600 W et batterie +2 A : un trou de 300 W que rien n'alimente,
|
||
// etat physiquement inatteignable reseau ouvert. Il passait quand meme,
|
||
// parce que le demarrage ne regardait que la production. Corrige en gardant
|
||
// l'intention d'origine — production mesuree SOUS pv_hold_min (500) et sous
|
||
// pv_start_min (700), donc seul solar_potential peut demarrer — avec une
|
||
// maison qui, elle, ferme le bilan : c'est la signature de la censure
|
||
// (batterie pleine, MPPT cale sur la maison, excedent ~0).
|
||
label: 'Après-midi hors fenêtre, SOC 95, PV bridé 430 W (< hold 500) mais maison 400 W → censure, excédent +30 W → provocation PV (solar_potential)',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '95', pv_power_w: '430', pv_power_10m_avg_w: '430',
|
||
pv_trend_w: '0', battery_current_a: '2',
|
||
house_ac_power_w: '400', vebus_ac_out_l1_power_w: '400',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_potential',
|
||
state: 'start_forecast_solar_potential',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ===== 03/09/2026 : un demarrage se decide sur l'EXCEDENT, pas sur la PV =====
|
||
// Les deux premiers scenarios sont des releves REELS du 03/09, pris a 54 min
|
||
// d'intervalle. Leurs lectures de production ne different que de 11 W
|
||
// (769 W puis 758 W) : pour l'ancien code, deux fois "758 >= 700, il y a du
|
||
// soleil". Ce qui les separe est la MAISON — 755 W puis 1202 W — donc
|
||
// l'excedent : +14 W (production refusee, batterie pleine, rien a absorber)
|
||
// puis -444 W (la batterie porte deja la maison, la nuit tombe).
|
||
// La paire est le coeur du correctif : sans elle, aucune preuve que la
|
||
// nouvelle grandeur discrimine ce que l'ancienne confondait.
|
||
{
|
||
id: 'excedent-censure-demarre',
|
||
label: "Relevé du 03/09 18:50 (pv_censored=ON) : PV 769 W, maison 755 W, batterie 0 A, SOC 94 → excédent +14 W = ressource refusée → la voiture DOIT démarrer",
|
||
time: '2026-08-08T18:50:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '94', battery_current_a: '0',
|
||
pv_power_w: '769.1', pv_power_10m_avg_w: '800', pv_trend_w: '0',
|
||
house_ac_power_w: '755', vebus_ac_out_l1_power_w: '755',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
sourceMode: 'solar_only',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
|
||
{
|
||
id: 'excedent-deficit-ne-demarre-pas',
|
||
label: "Relevé du 03/09 19:44 (l'incident) : PV 758 W mais maison 1202 W, batterie déjà à -12 A, SOC 94 → excédent -444 W → AUCUN démarrage, ni chemin solaire ni assistance batterie",
|
||
time: '2026-08-08T19:44:30+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '94', battery_current_a: '-12.2',
|
||
pv_power_w: '758.3', pv_power_10m_avg_w: '790', pv_trend_w: '0',
|
||
house_ac_power_w: '1202', vebus_ac_out_l1_power_w: '1202',
|
||
evcs_ac_power_w: '0',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
sourceMode: 'solar_only',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
},
|
||
},
|
||
|
||
{
|
||
// Exerce le chemin "soleil reel" (rawSolarStartOk), que la paire ci-dessus
|
||
// ne touche pas : elle passe par l'assistance batterie. Batterie a 60 %,
|
||
// donc pas de censure possible — la production mesuree EST la ressource, et
|
||
// elle est deja prise par la maison. L'ancien code demarrait (3000 >= 700).
|
||
id: 'excedent-grosse-production-grosse-maison',
|
||
label: "Matin, PV 3000 W mais maison 2800 W et SOC 60 (pas de censure possible) → excédent 200 W < seuil de démarrage → pas de charge malgré une forte production",
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', battery_current_a: '-4',
|
||
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||
house_ac_power_w: '2800', vebus_ac_out_l1_power_w: '2800',
|
||
evcs_ac_power_w: '0',
|
||
},
|
||
expect: {
|
||
profile: 'morning_solar',
|
||
sourceMode: 'solar_only',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
},
|
||
},
|
||
|
||
// ===== 03/09/2026 : la provocation doit etre justifiee par les chargeurs =====
|
||
// Un excedent proche de zero est AMBIGU. Meme releve, meme excedent, deux
|
||
// situations opposees a 51 min d'ecart le 03/09 :
|
||
// 18:50 excedent +14 W, trackers LIMITES -> ressource refusee, on y va
|
||
// 19:41 excedent +1 W, trackers MPPT_ACTIVE -> plus rien a donner, on reste
|
||
// Aucun seuil de puissance ne les separe ; les chargeurs, eux, le disent.
|
||
{
|
||
id: 'provocation-trackers-a-fond-ne-demarre-pas',
|
||
label: "Relevé du 03/09 19:41 : excédent +1 W (équilibre au crépuscule), SOC 94, mais les 7 trackers en mppt_active → plus rien à révéler → PAS de provocation",
|
||
time: '2026-08-08T19:41:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '94', battery_current_a: '-1.4',
|
||
pv_power_w: '816.9', pv_power_10m_avg_w: '850', pv_trend_w: '0',
|
||
house_ac_power_w: '816', vebus_ac_out_l1_power_w: '816',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
},
|
||
},
|
||
|
||
{
|
||
// Le repli doit etre sur le comportement ANTERIEUR, jamais sur une
|
||
// supposition : integration muette => on ne sait pas => on autorise comme
|
||
// avant. Meme releve que ci-dessus, seules les lectures changent.
|
||
id: 'provocation-trackers-illisibles-comportement-anterieur',
|
||
label: "Même relevé, mais chargeurs illisibles (unavailable) → on ne sait pas → repli sur le comportement antérieur, la charge démarre",
|
||
time: '2026-08-08T19:41:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '94', battery_current_a: '-1.4',
|
||
pv_power_w: '816.9', pv_power_10m_avg_w: '850', pv_trend_w: '0',
|
||
house_ac_power_w: '816', vebus_ac_out_l1_power_w: '816',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'unavailable,unknown,',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
|
||
// ===== 03/09/2026 : la cible du matin est une energie, pas un palier =====
|
||
// Releve de la nuit 02-03/09 a 03:00 : le bilan demandait 50,4 % pour tenir
|
||
// jusqu'au croisement de 9 h, la regle des paliers disait 35 %, le SOC etait
|
||
// a 45 %. EDF a ete relache 5 points sous la ligne, la batterie s'est videe
|
||
// a 20 % vers 06:30, et le reseau est revenu de 07:24 a 10:33 AU PLEIN TARIF.
|
||
{
|
||
id: 'cible-bilan-tient-le-reseau',
|
||
label: "Nuit HC 03:00 (relevé du 03/09), VW pleine, SOC 45 : le bilan demande 50,4 % → cible relevée → le réseau est MAINTENU au lieu d'être relâché",
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
soc_percent: '45',
|
||
balance_required_soc: '50.4', balance_energy_to_cover_kwh: '4.3',
|
||
balance_status: 'ok', balance_last_run: '08/08 02:58',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
state: 'night_session_done_idle',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
|
||
{
|
||
// CONTREFACTUEL : exactement la nuit du 03/09. Bilan indisponible => regle
|
||
// des paliers (35 %) => SOC 45 >= 35 => relache. C'est ce qui s'est passe.
|
||
id: 'cible-bilan-perime-relache-comme-avant',
|
||
label: "Même relevé, bilan vieux de 2 h → repli sur le palier 35 % → EDF relâché (le comportement qui a coûté 1,49 kWh au plein tarif)",
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
soc_percent: '45',
|
||
balance_required_soc: '50.4', balance_energy_to_cover_kwh: '4.3',
|
||
balance_status: 'ok', balance_last_run: '08/08 01:00',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
state: 'night_recovery_done_idle',
|
||
takeoverNeeded: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
{
|
||
// 01/10/2026, DECISION DU PROPRIETAIRE : le bilan fixe la cible DANS LES
|
||
// DEUX SENS (« 35 % c'est idiot si le besoin est prevu 26,6 % »). Ce
|
||
// scenario figeait l'ancien sens unique ; il fige desormais l'inverse.
|
||
id: 'cible-bilan-abaisse-la-cible',
|
||
label: "Nuit HC, SOC 30, le bilan ne demande que 25 % → la cible est 25 %, le palier 35 % n'est plus maître → le réseau est relâché",
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
soc_percent: '30',
|
||
balance_required_soc: '25', balance_energy_to_cover_kwh: '0.5',
|
||
balance_status: 'ok', balance_last_run: '08/08 02:58',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
{
|
||
// PLAFOND 90 % : le bilan publie sans ecretage des valeurs > 100 (c'est
|
||
// voulu, §26 : signal de dimensionnement). Sans plafond ici, la cible
|
||
// deviendrait inatteignable et EDF resterait allume toute la nuit a
|
||
// charger la batterie — en evincant au passage la production du matin.
|
||
id: 'cible-bilan-plafonnee-a-90',
|
||
label: "Nuit HC, SOC 92, le bilan demande 118,9 % (stockage insuffisant) → cible plafonnée à 90 % → recovery terminée, EDF relâché",
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
soc_percent: '92',
|
||
balance_required_soc: '118.9', balance_energy_to_cover_kwh: '14.54',
|
||
balance_status: 'ok', balance_last_run: '08/08 02:58',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
state: 'night_recovery_done_idle',
|
||
takeoverNeeded: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ===== 04/09/2026 : LA SESSION TENAIT LE CONTACTEUR, PAS L'ENERGIE =====
|
||
// Releve de la nuit du 03-04/09. La session unique a fonctionne — une seule
|
||
// bascule du contacteur, EDF ferme jusqu'a 07:24. Mais a 02:50, la voiture
|
||
// finie, le sourceMode est passe a 'blocked' et la consigne reseau est
|
||
// retombee de 5078 W a 50 W. Contacteur ferme, heures creuses, et la maison
|
||
// a tire 770 W SUR LA BATTERIE pendant trois heures et demie :
|
||
// SOC 44 % -> 17 %, 2,60 kWh pris a la batterie quand le reseau etait la.
|
||
{
|
||
id: 'consigne-session-engagee-couvre-la-maison',
|
||
label: "Nuit 03:30, session engagée mais voiture finie (source bloquée) → la consigne DOIT couvrir la maison au lieu de retomber au repos",
|
||
time: '2026-08-08T03:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '-16', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off', evcs_ac_power_w: '0',
|
||
house_ac_power_w: '770', vebus_ac_out_l1_power_w: '770',
|
||
prev_surplus_state: 'night_session_done_idle',
|
||
night_grid_session_committed_prev: 'on',
|
||
soc_percent: '40',
|
||
balance_required_soc: '57.2', balance_energy_to_cover_kwh: '7.58',
|
||
balance_status: 'ok', balance_last_run: '08/08 03:28',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: true,
|
||
// 770 W de maison + le plafond de courant en marge. Ce qui compte n'est
|
||
// pas la valeur exacte mais qu'on ne retombe PAS au repos.
|
||
gridSetpointW: 5595,
|
||
},
|
||
},
|
||
|
||
{
|
||
// CONTREFACTUEL. La difference est PHYSIQUE et non un drapeau force : le
|
||
// SOC est au-dessus de la ligne du bilan, donc la nuit est couverte, donc
|
||
// aucune session ne s'engage — et la consigne doit bien retomber au repos.
|
||
// Sans ce scenario, le correctif pourrait fuir hors session.
|
||
//
|
||
// ⚠️ Premiere redaction fausse : elle portait DEUX fois `balance_status`
|
||
// dans le meme objet. En JavaScript la derniere gagne, donc le scenario
|
||
// testait l'inverse de ce que son libelle annoncait — et il l'a signale en
|
||
// renvoyant 5595. Ne jamais construire un contrefactuel en surchargeant une
|
||
// clef deja presente.
|
||
// ⚠️ ATTENDU MIS A JOUR LE 06/09/2026 : la consigne de repos est passee de
|
||
// 200 a 700 W. Elle ne s'applique que contacteur FERME sans session reseau,
|
||
// et a 200 W une plaque qui cycle faisait passer le point de fonctionnement
|
||
// sous zero (mesure du 06/09, fenetre 12:07-12:22 : 9 % d'echantillons
|
||
// negatifs, minimum -477 W, mediane des negatifs -146 W). Les deux seules
|
||
// raisons de fermer le contacteur sans session EVCS sont « batterie au
|
||
// plancher » et « la maison demande plus que le parc ne sait donner » :
|
||
// dans les deux cas on veut que le reseau contribue plus que 200 W.
|
||
id: 'consigne-sans-engagement-retombe-au-repos',
|
||
label: "Même relevé mais SOC 70 au-dessus de la ligne : aucune session engagée → la consigne retombe à la valeur de repos",
|
||
time: '2026-08-08T03:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '-16', night_takeover_prev: 'off',
|
||
evcs_charging_switch: 'off', evcs_ac_power_w: '0',
|
||
house_ac_power_w: '770', vebus_ac_out_l1_power_w: '770',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
night_grid_session_committed_prev: 'off',
|
||
soc_percent: '70',
|
||
balance_required_soc: '57.2', balance_energy_to_cover_kwh: '7.58',
|
||
balance_status: 'ok', balance_last_run: '08/08 03:28',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: false,
|
||
gridSetpointW: 700,
|
||
},
|
||
},
|
||
|
||
// ===== 03/09/2026 : SESSION RESEAU UNIQUE DE LA NUIT =====
|
||
// Engagement sur le bilan, relachement a la FIN DE LA FENETRE — jamais sur
|
||
// un seuil de SOC. Deux raisons, toutes deux mesurees le 03/09 : un seuil de
|
||
// relachement que le SOC repasse ensuite rouvre l'oscillateur du 27/08, et
|
||
// il porterait de toute facon sur une grandeur fausse (la conversion
|
||
// kWh -> points du bilan est lineaire a 15 kWh alors que la jauge vaut
|
||
// ~17 kWh au-dessus de 42 % et 8 a 12 kWh en dessous).
|
||
//
|
||
// Le premier scenario etait, quelques heures plus tot, epingle comme LIMITE
|
||
// CONNUE : le reseau restait relache alors que le SOC etait repasse sous la
|
||
// ligne. C'est exactement ce que ce correctif change.
|
||
{
|
||
id: 'session-unique-tient-sous-la-ligne',
|
||
label: "Nuit HC 05:30, recovery déjà latchée, SOC 30 sous le requis 37,1 % → la session réseau s'ENGAGE et tient",
|
||
time: '2026-08-08T05:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-14', night_takeover_prev: 'off',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
soc_percent: '30',
|
||
balance_required_soc: '37.1', balance_energy_to_cover_kwh: '2.3',
|
||
balance_status: 'ok', balance_last_run: '08/08 05:28',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
sessionCommittedService: 'turn_on',
|
||
},
|
||
},
|
||
|
||
{
|
||
// LA PREUVE ANTI-OSCILLATEUR. Une fois engagee, la session ne se relache
|
||
// PAS parce que le SOC est repasse au-dessus de la ligne. Sans ce
|
||
// scenario, un relachement sur seuil passerait les tests et recreerait le
|
||
// cycle de 31 min corrige le 27/08.
|
||
id: 'session-unique-ne-relache-pas-sur-le-soc',
|
||
label: "Session déjà engagée, SOC 45 repassé AU-DESSUS du requis 37,1 % → le réseau reste tenu, et rien n'est réécrit",
|
||
time: '2026-08-08T05:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
night_grid_session_committed_prev: 'on',
|
||
soc_percent: '45',
|
||
balance_required_soc: '37.1', balance_energy_to_cover_kwh: '2.3',
|
||
balance_status: 'ok', balance_last_run: '08/08 05:28',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
sessionCommittedService: null,
|
||
},
|
||
},
|
||
|
||
{
|
||
// LA SORTIE, et la seule : la fenetre se ferme. Condition monotone, donc
|
||
// aucune oscillation possible.
|
||
id: 'session-unique-fin-de-fenetre',
|
||
label: "08:00, hors fenêtre HC, session encore marquée engagée → réseau relâché et bit nettoyé",
|
||
time: '2026-08-08T08:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '2', night_takeover_prev: 'on',
|
||
evcs_charging_switch: 'off',
|
||
night_grid_session_committed_prev: 'on',
|
||
soc_percent: '45',
|
||
balance_required_soc: '37.1', balance_energy_to_cover_kwh: '2.3',
|
||
balance_status: 'ok', balance_last_run: '08/08 07:58',
|
||
},
|
||
expect: {
|
||
takeoverNeeded: false,
|
||
takeoverHelperService: 'turn_off',
|
||
sessionCommittedService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
{
|
||
id: 'session-unique-bilan-indisponible',
|
||
label: "Même relevé qu'en tête, bilan périmé → aucun engagement, comportement historique (réseau relâché)",
|
||
time: '2026-08-08T05:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, allow_grid_hc_night: 'on',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-14', night_takeover_prev: 'off',
|
||
evcs_charging_switch: 'off',
|
||
prev_surplus_state: 'night_recovery_done_idle',
|
||
soc_percent: '30',
|
||
balance_required_soc: '37.1', balance_energy_to_cover_kwh: '2.3',
|
||
balance_status: 'ok', balance_last_run: '08/08 03:00',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: false,
|
||
takeoverHelperService: 'turn_off',
|
||
sessionCommittedService: null,
|
||
},
|
||
},
|
||
|
||
// ===== 03/09/2026 : le plancher du soir vient de l'energie a couvrir =====
|
||
// Tous ces scenarios ont un VRAI excedent (PV 2000 W, maison 1000 W) et des
|
||
// chargeurs qui se retiennent : les gardes de §36 et §37 sont donc passantes,
|
||
// et le plancher est le SEUL discriminant.
|
||
{
|
||
id: 'bilan-plancher-soir-durci',
|
||
label: "Soir, SOC 94 et vrai excédent, mais le bilan demande 99,7 % pour tenir jusqu'au croisement → le plancher passe de 85 à 99,7 → pas de charge",
|
||
time: EVENING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
battery_current_a: '2',
|
||
pv_power_w: '2000', pv_power_10m_avg_w: '2000', pv_trend_w: '0',
|
||
house_ac_power_w: '1000', vebus_ac_out_l1_power_w: '1000',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
soc_percent: '94',
|
||
balance_required_soc: '99.7', balance_energy_to_cover_kwh: '11.66',
|
||
balance_status: 'ok', balance_last_run: '08/08 17:58',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
},
|
||
},
|
||
|
||
{
|
||
// SENS UNIQUE : le bilan ne doit JAMAIS relacher le plancher historique.
|
||
// Sans ce scenario, remplacer Math.max par une affectation directe
|
||
// passerait inapercu et autoriserait des charges qui n'ont pas lieu
|
||
// aujourd'hui.
|
||
id: 'bilan-plancher-soir-ne-relache-pas',
|
||
label: "Soir, SOC 70, le bilan ne demande que 60 % → le plancher historique 85 % reste maître, pas de charge",
|
||
time: EVENING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
battery_current_a: '2',
|
||
pv_power_w: '2000', pv_power_10m_avg_w: '2000', pv_trend_w: '0',
|
||
house_ac_power_w: '1000', vebus_ac_out_l1_power_w: '1000',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
soc_percent: '70',
|
||
balance_required_soc: '60', balance_energy_to_cover_kwh: '4.2',
|
||
balance_status: 'ok', balance_last_run: '08/08 17:58',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
},
|
||
},
|
||
|
||
{
|
||
id: 'bilan-perime-regle-historique',
|
||
label: "Soir, SOC 94, bilan exigeant (99,7 %) mais vieux de 2 h → repli sur la règle historique 85 % → la charge démarre",
|
||
time: EVENING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
battery_current_a: '2',
|
||
pv_power_w: '2000', pv_power_10m_avg_w: '2000', pv_trend_w: '0',
|
||
house_ac_power_w: '1000', vebus_ac_out_l1_power_w: '1000',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
soc_percent: '94',
|
||
balance_required_soc: '99.7', balance_energy_to_cover_kwh: '11.66',
|
||
balance_status: 'ok', balance_last_run: '08/08 16:00',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
|
||
{
|
||
id: 'bilan-statut-degrade-regle-historique',
|
||
label: "Soir, SOC 94, bilan frais mais statut non 'ok' → repli sur la règle historique → la charge démarre",
|
||
time: EVENING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
battery_current_a: '2',
|
||
pv_power_w: '2000', pv_power_10m_avg_w: '2000', pv_trend_w: '0',
|
||
house_ac_power_w: '1000', vebus_ac_out_l1_power_w: '1000',
|
||
evcs_ac_power_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
soc_percent: '94',
|
||
balance_required_soc: '99.7', balance_energy_to_cover_kwh: '11.66',
|
||
balance_status: 'donnees_insuffisantes', balance_last_run: '08/08 17:58',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
state: 'start_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
|
||
// ===== 01/09/2026 : le depassement souple de la batterie doit DURER =====
|
||
// Incident du 01/09 (14:26 et 14:56) : SOC 94, batterie en float, courant
|
||
// oscillant entre +1,8 et -2,6 A d'un echantillon a l'autre. Le controleur
|
||
// comparait un echantillon instantane a un plancher de 0 A toutes les 30 s :
|
||
// pile ou face. Chaque arret coutait ~0,45 kWh de production refusee, la
|
||
// voiture etant le seul preneur d'une batterie pleine.
|
||
// La table des bandes SOC declarait deja une tolerance de 12 min a SOC > 90 ;
|
||
// elle n'etait branchee nulle part. Aucun seuil n'est deplace ici.
|
||
{
|
||
id: 'batt-float-bruit-ne-coupe-pas',
|
||
label: "Relevé du 01/09 14:26 : SOC 94, batterie en float à -1 A, charge à 20 A → le bruit de régulation NE DOIT PAS arrêter la charge",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '6600', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '6000', vebus_ac_out_l1_power_w: '6000',
|
||
battery_current_a: '-1',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'charging_hold',
|
||
decision: 'hold',
|
||
desiredCharging: true,
|
||
currentA: 20,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'batt-decharge-soutenue-coupe',
|
||
label: "CONTREFACTUEL : mêmes mesures, mais le dépassement dure depuis 20 min > tolérance 12 min de la bande gt90 → la garde reprend la main et arrête",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '6600', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '6000', vebus_ac_out_l1_power_w: '6000',
|
||
battery_current_a: '-1',
|
||
battery_soft_breach_at: '2026-08-08 15:10:00',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'stop_conditions_lost',
|
||
decision: 'stop',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'batt-pointe-maison-ne-coupe-pas',
|
||
label: "Relevé du 01/09 14:56 : pointe maison à 8 kW, batterie à -42 A → réduction graduée au plafond système (18 A), PAS un arrêt",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '9100', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '8000', vebus_ac_out_l1_power_w: '8000',
|
||
battery_current_a: '-42',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'charging_step_down_system_cap',
|
||
decision: 'step_down_fast',
|
||
desiredCharging: true,
|
||
currentA: 18,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'batt-decharge-dure-arret-immediat',
|
||
label: "SÉCURITÉ : batterie à -120 A, sous le seuil DUR de la bande (-100 A) → arrêt immédiat, aucune tolérance. La tolérance est du confort, jamais du risque",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '1000', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '9000', vebus_ac_out_l1_power_w: '9000',
|
||
battery_current_a: '-120',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'battery_current_hard_stop',
|
||
decision: 'stop_hard',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'batt-plancher-demarrage-reste-instantane',
|
||
label: "FRONTIÈRE : véhicule branché mais à l'arrêt, batterie à -1 A → on NE DÉMARRE PAS sur une batterie qui se décharge. Le plancher de démarrage reste instantané",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '94',
|
||
pv_power_w: '6600', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '6000', vebus_ac_out_l1_power_w: '6000',
|
||
battery_current_a: '-1',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'idle_wait_conditions',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ================= STRATÉGIES MANUELLES =================
|
||
{
|
||
id: 'force-grid-jour',
|
||
label: 'force_grid_now l\'après-midi, EDF ON, SOC 60 → charge réseau forcée',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '500', pv_power_10m_avg_w: '500',
|
||
pv_trend_w: '0', battery_current_a: '2',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'grid_forced',
|
||
state: 'start_grid_forced_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'force-charge-nuit',
|
||
label: 'force_charge_now la nuit (toggle HC ON) → démarrage manuel local (non-régression P7)',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT, victron_evcs_strategy: 'force_charge_now',
|
||
soc_percent: '60', allow_grid_hc_night: 'on',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'solar_potential',
|
||
state: 'start_manual_local_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'strategie-stop',
|
||
label: 'Stratégie stop en journée → tout bloqué',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
victron_evcs_strategy: 'stop',
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '80', pv_power_w: '3000', pv_power_10m_avg_w: '3000',
|
||
battery_current_a: '10',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'blocked',
|
||
state: 'stopped_user',
|
||
decision: 'stop_stop_mode',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
|
||
// ================= SÉCURITÉ =================
|
||
{
|
||
id: 'securite-overload',
|
||
label: 'Charge solaire en cours + surcharge VE.Bus → protection (stop/step-down)',
|
||
time: EVENING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 17:30:00',
|
||
soc_percent: '90', pv_power_w: '3500', pv_power_10m_avg_w: '3400',
|
||
battery_current_a: '5', evcs_current_a: '13', evcs_set_current_a: '13',
|
||
vebus_overload: 'on', vebus_l1_overload: 'on',
|
||
house_ac_power_w: '4800', vebus_ac_out_l1_power_w: '4800',
|
||
},
|
||
expect: {
|
||
profile: 'evening_high_soc',
|
||
sourceMode: 'solar_only',
|
||
state: 'vebus_overload_stop_min_current',
|
||
decision: 'stop',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
{
|
||
id: 'vehicule-debranche-jour',
|
||
label: 'Après-midi véhicule débranché → stop_not_connected (état de repos)',
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '93', pv_power_w: '1800', pv_power_10m_avg_w: '1800',
|
||
battery_current_a: '3',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
sourceMode: 'solar_only',
|
||
state: 'vehicle_not_connected',
|
||
decision: 'stop_not_connected',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: 'turn_off',
|
||
},
|
||
},
|
||
// ======= FENÊTRE D'ÉTABLISSEMENT DU RÉSEAU (31/08/2026) =======
|
||
// Mesuré ce jour-là : à la fermeture du contacteur, le réseau reprend toute la
|
||
// charge de la maison, le solaire bascule vers la batterie (ou se fait brider
|
||
// s'il n'y a plus de tampon : 2663 -> 559 W), puis l'import redescend à la
|
||
// consigne en ~150 s. Pendant ce temps la boucle de 30 s prend cinq décisions
|
||
// sur des mesures qui ne décrivent aucun régime permanent.
|
||
// Chaque cas est apparié à son contrefactuel hors fenêtre : sans cet appariement
|
||
// le test ne prouverait rien.
|
||
{
|
||
id: 'etablissement-maintien-charge',
|
||
label: 'Réseau fermé il y a 30 s, PV effondrée à 600 W, batterie à -15 A depuis 10 min : dans la fenêtre, la mesure batterie est neutralisée, la charge NE DOIT PAS être arrêtée',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
soc_percent: '60',
|
||
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
|
||
evcs_ac_power_w: '3680',
|
||
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
|
||
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
|
||
battery_current_a: '-15',
|
||
battery_soft_breach_at: '2026-08-08 08:50:00',
|
||
acin1_last_changed_ts: String(MORNING_TS - 30),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 30),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "charging_step_down_battery",
|
||
decision: "step_down",
|
||
desiredCharging: true,
|
||
currentA: 14,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'etablissement-termine-arret-normal',
|
||
label: 'CONTREFACTUEL : mêmes mesures ET même dépassement de 10 min, transition vieille de 400 s → fenêtre fermée, la garde reprend la main et arrête (10 min > tolérance 4 min de la bande 40_60)',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
soc_percent: '60',
|
||
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
|
||
evcs_ac_power_w: '3680',
|
||
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
|
||
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
|
||
battery_current_a: '-15',
|
||
battery_soft_breach_at: '2026-08-08 08:50:00',
|
||
acin1_last_changed_ts: String(MORNING_TS - 400),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 400),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "stop_conditions_lost",
|
||
decision: "stop",
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'etablissement-securite-non-suspendue',
|
||
label: "SÉCURITÉ : dans la fenêtre, batterie à -40 A → l'arrêt dur reste actif. La fenêtre suspend le confort, jamais le risque",
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
soc_percent: '60',
|
||
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
|
||
evcs_ac_power_w: '3680',
|
||
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
|
||
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
|
||
battery_current_a: '-40',
|
||
acin1_last_changed_ts: String(MORNING_TS - 30),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 30),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "battery_current_hard_stop",
|
||
decision: "stop_hard",
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'etablissement-ouverture-aucune-fenetre',
|
||
label: "ASYMÉTRIE : réseau DISPARU il y a 30 s → aucune fenêtre. Les onduleurs reprennent tout, les gardes doivent agir plus vite, pas moins (dépassement de 10 min > tolérance 4 min)",
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
soc_percent: '60',
|
||
evcs_current_a: '16', evcs_set_current_a: '16', evcs_command_current_a: '16',
|
||
evcs_ac_power_w: '3680',
|
||
house_ac_power_w: '4400', vebus_ac_out_l1_power_w: '4400',
|
||
pv_power_w: '600', pv_power_10m_avg_w: '3900', pv_trend_w: '-2000',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
|
||
battery_current_a: '-15',
|
||
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
|
||
battery_soft_breach_at: '2026-08-08 08:50:00',
|
||
acin1_last_changed_ts: String(MORNING_TS - 30),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 30),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "stop_conditions_lost",
|
||
decision: "stop",
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'etablissement-pas-de-demarrage',
|
||
label: "Réseau fermé il y a 30 s, rien en charge : on ne démarre pas non plus sur des mesures d'établissement",
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
|
||
acin1_last_changed_ts: String(MORNING_TS - 30),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 30),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "idle_wait_conditions",
|
||
decision: "hold",
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'etablissement-demarrage-hors-fenetre',
|
||
label: 'CONTREFACTUEL : mêmes mesures, transition vieille de 400 s → le démarrage solaire a bien lieu',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
pv_trend_w: '100', battery_current_a: '10',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '1800',
|
||
acin1_last_changed_ts: String(MORNING_TS - 400),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 400),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "start_min_current",
|
||
decision: "start",
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
// ======= SÉQUENCE D'OUVERTURE DU CONTACTEUR (31/08/2026) =======
|
||
// Le plafond majoré (28 A en BOOST) n'est légitime que tant que le réseau est
|
||
// là. C'est l'hystérésis qui ouvre, une minute après que la demande réseau de
|
||
// l'EVCS a disparu : l'EVCS doit donc baisser AVANT, de lui-même.
|
||
{
|
||
id: 'ouverture-plafond-tombe-hors-demande-reseau',
|
||
label: "Matin solaire, AC-IN encore présent mais l'EVCS ne demande plus le réseau : le plafond retombe à 20 A et la voiture est réduite IMMÉDIATEMENT",
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
// BOOST EDF : plafond réseau 28 A, voiture effectivement à 28 A.
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
evcs_current_a: '28', evcs_set_current_a: '28', evcs_command_current_a: '28',
|
||
evcs_ac_power_w: '6440',
|
||
house_ac_power_w: '8440', vebus_ac_out_l1_power_w: '8440',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '2000',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
battery_current_a: '10',
|
||
// Cooldown de courant NON écoulé : la réduction ne doit pas l'attendre.
|
||
last_current_change_at: '2026-08-08 08:59:50',
|
||
acin1_last_changed_ts: String(MORNING_TS - 4000),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 4000),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "charging_step_down_system_cap",
|
||
decision: "step_down_fast",
|
||
desiredCharging: true,
|
||
currentA: 20,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'ouverture-plafond-maintenu-en-demande-reseau',
|
||
label: "CONTREFACTUEL : force_grid_now, l'EVCS demande le réseau → le plafond BOOST 28 A est conservé, aucune réduction",
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
// BOOST EDF : plafond réseau 28 A, voiture effectivement à 28 A.
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
evcs_current_a: '28', evcs_set_current_a: '28', evcs_command_current_a: '28',
|
||
evcs_ac_power_w: '6440',
|
||
house_ac_power_w: '8440', vebus_ac_out_l1_power_w: '8440',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '2000',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
battery_current_a: '10',
|
||
last_current_change_at: '2026-08-08 08:59:50',
|
||
acin1_last_changed_ts: String(MORNING_TS - 4000),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 4000),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "grid_forced",
|
||
state: "charging_hold_grid",
|
||
decision: "hold",
|
||
desiredCharging: true,
|
||
currentA: 28,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
{
|
||
id: 'ouverture-reseau-disparu-reduction-immediate',
|
||
label: 'Réseau DISPARU : plafond câblé 20 A, voiture encore à 28 A, cooldown non écoulé → réduction immédiate, sans attendre le compteur de confort',
|
||
time: MORNING,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 08:00:00',
|
||
// BOOST EDF : plafond réseau 28 A, voiture effectivement à 28 A.
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
evcs_current_a: '28', evcs_set_current_a: '28', evcs_command_current_a: '28',
|
||
evcs_ac_power_w: '6440',
|
||
house_ac_power_w: '8440', vebus_ac_out_l1_power_w: '8440',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '2000',
|
||
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
|
||
soc_percent: '60', pv_power_w: '4500', pv_power_10m_avg_w: '4200',
|
||
battery_current_a: '10',
|
||
last_current_change_at: '2026-08-08 08:59:50',
|
||
acin1_last_changed_ts: String(MORNING_TS - 20),
|
||
grid_switch_last_changed_ts: String(MORNING_TS - 20),
|
||
},
|
||
expect: {
|
||
profile: "morning_solar",
|
||
sourceMode: "solar_only",
|
||
state: "charging_step_down_system_cap",
|
||
decision: "step_down_fast",
|
||
desiredCharging: true,
|
||
currentA: 20,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
takeoverHelperService: "turn_off",
|
||
},
|
||
},
|
||
|
||
// ===================================================================
|
||
// CONSIGNE DE PUISSANCE RESEAU (01/09/2026)
|
||
// Fermer le contacteur ne suffit pas : au-dessus du SOC minimal, l'ESS en
|
||
// autoconsommation ne preleve que `ac_power_setpoint` et laisse la batterie
|
||
// porter maison + voiture. Incident de la nuit du 01/09 : consigne a 50 W,
|
||
// charge HC demarree puis arretee en 60 s par le garde batterie.
|
||
// ===================================================================
|
||
{
|
||
id: 'consigne-session-hc-reseau-porte-la-charge',
|
||
label: 'Nuit HC, reseau present, session reseau : la consigne monte au besoin mesure (maison hors EVCS + voiture au plafond)',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT,
|
||
soc_percent: '50',
|
||
allow_grid_hc_night: 'on',
|
||
grid_state: 'on',
|
||
victron_acin1_available: '1',
|
||
grid_setpoint_w: '50',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_hc_allowed',
|
||
gridSetpointW: 5525,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-hors-session-retour-au-repos',
|
||
label: 'CONTREFACTUEL — meme nuit sans session reseau : la consigne redescend a la valeur de repos',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT,
|
||
soc_percent: '50',
|
||
allow_grid_hc_night: 'off',
|
||
grid_state: 'off',
|
||
victron_acin1_available: '0',
|
||
grid_setpoint_w: '4000',
|
||
},
|
||
expect: {
|
||
gridSetpointW: 700,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-deja-genereuse-aucune-ecriture',
|
||
label: 'Session reseau, consigne a 5600 W pour un besoin de 5295 W : deja suffisante, aucune reecriture (le bruit de la maison ne la fait pas courir)',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT,
|
||
soc_percent: '50',
|
||
allow_grid_hc_night: 'on',
|
||
grid_state: 'on',
|
||
victron_acin1_available: '1',
|
||
grid_setpoint_w: '5600',
|
||
},
|
||
expect: {
|
||
gridSetpointW: null,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-insuffisante-corrigee-immediatement',
|
||
label: "LE DEFAUT DE LA NUIT DU 01/09 — consigne 45 W sous le besoin : la batterie porterait la difference, correction sans attendre",
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT,
|
||
soc_percent: '50',
|
||
allow_grid_hc_night: 'on',
|
||
grid_state: 'on',
|
||
victron_acin1_available: '1',
|
||
grid_setpoint_w: '5250',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_hc_allowed',
|
||
gridSetpointW: 5525,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-derive-externe-corrigee',
|
||
label: 'Hors session mais consigne laissee a 4000 W par un tiers : corrigee au cycle suivant',
|
||
time: MORNING,
|
||
overrides: {
|
||
soc_percent: '60',
|
||
grid_state: 'off',
|
||
victron_acin1_available: '0',
|
||
grid_setpoint_w: '4000',
|
||
},
|
||
expect: {
|
||
gridSetpointW: 700,
|
||
},
|
||
},
|
||
// ================= CREDIT SOLAIRE SUR LA CONSIGNE (11/09/2026) =================
|
||
// Releve reel : BOOST 28 A en plein soleil, consigne 8260 W, PV effondre a
|
||
// 121 W, reseau 7697 W, batterie 0 A. La consigne evincait le solaire.
|
||
{
|
||
id: 'consigne-credit-solaire-descend',
|
||
label: "Releve 11/09 17:02 — BOOST 28 A, 6 trackers brides, batterie neutre : la consigne DESCEND pour rendre la main au solaire",
|
||
time: '2026-08-08T17:02:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 16:30:00',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
soc_percent: '96',
|
||
battery_current_a: '0',
|
||
pv_power_w: '121', pv_power_10m_avg_w: '150', pv_trend_w: '0',
|
||
house_ac_power_w: '7695', vebus_ac_out_l1_power_w: '7695',
|
||
evcs_ac_power_w: '5893', evcs_current_a: '28',
|
||
grid_setpoint_w: '8260',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
gridSetpointW: 6799,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-credit-trackers-a-fond-pas-de-descente',
|
||
label: "CONTREFACTUEL — meme instant, mais les 6 trackers en mppt_active : plus rien a liberer, la consigne reste au plafond",
|
||
time: '2026-08-08T17:02:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 16:30:00',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
soc_percent: '96',
|
||
battery_current_a: '0',
|
||
pv_power_w: '121', pv_power_10m_avg_w: '150', pv_trend_w: '0',
|
||
house_ac_power_w: '7695', vebus_ac_out_l1_power_w: '7695',
|
||
evcs_ac_power_w: '5893', evcs_current_a: '28',
|
||
grid_setpoint_w: '8260',
|
||
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
gridSetpointW: null,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-credit-batterie-porte-remonte',
|
||
label: "CONTREFACTUEL — trackers brides MAIS batterie en depassement souple (-60 A) : securite d'abord, la consigne remonte au plafond",
|
||
time: '2026-08-08T17:02:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 16:30:00',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
soc_percent: '96',
|
||
battery_current_a: '-60',
|
||
pv_power_w: '121', pv_power_10m_avg_w: '150', pv_trend_w: '0',
|
||
house_ac_power_w: '7695', vebus_ac_out_l1_power_w: '7695',
|
||
evcs_ac_power_w: '5893', evcs_current_a: '28',
|
||
grid_setpoint_w: '2000',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
gridSetpointW: 8465,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-credit-nuit-trackers-eteints',
|
||
label: "ANTI-OSCILLATEUR — nuit HC, session reseau, les 6 trackers `off` : rien a liberer, comportement de nuit STRICTEMENT inchange (§31, §57)",
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT,
|
||
soc_percent: '50',
|
||
allow_grid_hc_night: 'on',
|
||
grid_state: 'on',
|
||
victron_acin1_available: '1',
|
||
grid_setpoint_w: '5000',
|
||
mppt_operation_modes: 'off,off,off,off,off,off',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_hc_allowed',
|
||
gridSetpointW: 5525,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-credit-plancher-maison',
|
||
label: "La descente s'arrete a la maison hors voiture : le reseau continue de la couvrir (garantie du 01/09 et du 04/09)",
|
||
time: '2026-08-08T17:02:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 16:30:00',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
grid_current_max_a: '6', current_max_auto_a: '6',
|
||
soc_percent: '96',
|
||
battery_current_a: '0',
|
||
pv_power_w: '4500', pv_power_10m_avg_w: '4500', pv_trend_w: '0',
|
||
house_ac_power_w: '7695', vebus_ac_out_l1_power_w: '7695',
|
||
evcs_ac_power_w: '5893', evcs_current_a: '28',
|
||
grid_setpoint_w: '2000',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited,voltage_current_limited',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
gridSetpointW: 1802,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-credit-converge-tient',
|
||
label: "ANTI-OSCILLATEUR (11/09 17:47) — consigne deja descendue a 2241 W et les 6 trackers repasses mppt_active : on TIENT, on ne repousse pas au plafond",
|
||
time: '2026-08-08T17:02:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 16:30:00',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
soc_percent: '92',
|
||
battery_current_a: '36',
|
||
pv_power_w: '7074', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '6895', vebus_ac_out_l1_power_w: '6895',
|
||
evcs_ac_power_w: '6059', evcs_current_a: '28',
|
||
grid_setpoint_w: '5500',
|
||
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
gridSetpointW: null,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-credit-plancher-nuage',
|
||
label: "INCIDENT 11/09 18:00 — consigne descendue a 1281 W, voiture a 28 A : un nuage a fait chuter la PV de 4,6 kW et la batterie a pris 87 A (limite dure 70) -> hard stop. La consigne doit etre RELEVEE avant le nuage.",
|
||
time: '2026-08-08T17:02:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 16:30:00',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
grid_current_max_a: '28', current_max_auto_a: '22',
|
||
soc_percent: '96',
|
||
battery_current_a: '-1',
|
||
pv_power_w: '6058', pv_power_10m_avg_w: '5800', pv_trend_w: '0',
|
||
house_ac_power_w: '7170', vebus_ac_out_l1_power_w: '7170',
|
||
evcs_ac_power_w: '6133', evcs_current_a: '28',
|
||
grid_setpoint_w: '1281',
|
||
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
gridSetpointW: 4829,
|
||
},
|
||
},
|
||
// ======== SESSION RESEAU DE NUIT SANS VEHICULE (12/09/2026) ========
|
||
// Nuit du 11-12/09, voiture rendue la veille : le bilan a reclame 92 % puis
|
||
// 59 % de SOC toute la nuit, `reseau_necessaire` vrai a chaque calcul, et
|
||
// rien ne s'est passe -- SOC fige a 20 %, consigne reseau a 700 W depuis
|
||
// 18:36. La session exigeait un vehicule branche.
|
||
{
|
||
id: 'nuit-sans-vehicule-session-engage',
|
||
label: "INCIDENT 12/09 04:00 — HC, AUCUN vehicule, SOC 20, le bilan demande 59,1 % : la session reseau doit s'engager pour la MAISON",
|
||
time: '2026-08-08T04:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
soc_percent: '20', battery_current_a: '0',
|
||
pv_power_w: '0', pv_power_10m_avg_w: '0', pv_trend_w: '0',
|
||
house_ac_power_w: '612', vebus_ac_out_l1_power_w: '612',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '700',
|
||
balance_required_soc: '59.1', balance_energy_to_cover_kwh: '3.13',
|
||
balance_status: 'ok', balance_last_run: '08/08 03:58',
|
||
// Le MANQUE, pose explicitement : sans lui le scenario heriterait du 0 de
|
||
// la fixture, ce qui decrirait une batterie couverte alors qu'il declare
|
||
// un SOC de 20 % pour un requis de 59,1 % — un etat impossible.
|
||
// Valeur calculee avec la courbe mesuree, parc de reference (7 modules,
|
||
// echelle 1) : 13 points sous le genou de 33 % a 0,097 = 1,261 kWh, puis
|
||
// 26,1 points a 0,169 = 4,411 kWh, soit 5,672 kWh. Ce scenario est le seul
|
||
// du corpus dont le deficit TRAVERSE le genou.
|
||
balance_grid_energy_needed_kwh: '5.672',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
sessionCommittedService: 'turn_on',
|
||
// 18/09/2026 : 5437 -> 2380. Les 5437 valaient 612 de maison + 4825
|
||
// empruntes au plafond d'une borne ou aucun vehicule n'est branche. La
|
||
// nouvelle valeur vaut 612 + le manque reel borne par DVCC (30 A, soit
|
||
// 1538 W a la tension de la fixture), plus la marge d'ecriture d'un pas
|
||
// de courant. ⚠️ Attendu calcule sur la FIXTURE et jamais sur la tension
|
||
// du jour : le recalculer sur l'installation vivante rendrait la suite
|
||
// rouge un matin ou la batterie est a 49 V sans que rien ne soit casse.
|
||
gridSetpointW: 2380,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-bilan-fin-de-fenetre-bornee-par-dvcc',
|
||
label: "CONSIGNE 18/09 — meme manque a 07:10, 14 min de fenetre : c'est DVCC qui borne, pas le temps",
|
||
time: '2026-08-08T07:10:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
soc_percent: '20', battery_current_a: '0',
|
||
pv_power_w: '0', pv_power_10m_avg_w: '0', pv_trend_w: '0',
|
||
house_ac_power_w: '612', vebus_ac_out_l1_power_w: '612',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '700',
|
||
balance_required_soc: '59.1', balance_energy_to_cover_kwh: '3.13',
|
||
balance_status: 'ok', balance_last_run: '08/08 07:08',
|
||
balance_grid_energy_needed_kwh: '5.672',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
// ⚠️ LE SCENARIO QUI PROTEGE CONTRE LA REGRESSION LA PLUS GRAVE de cette
|
||
// conception. 5,672 kWh a combler en 14 minutes font 17 kW : avec un
|
||
// simple plancher d'horizon, la consigne saturait sur GRID_SETPOINT_MAX_W
|
||
// (10 000 W), soit PIRE que les 5437 du plafond emprunte qu'on remplace.
|
||
// Le plafond DVCC ramene le terme a 1538 W, et la consigne a la meme
|
||
// valeur qu'a 04:00 — parce que ce qui borne n'est pas le temps qui
|
||
// reste, c'est ce que la batterie a le droit de prendre.
|
||
// Le plafond BMS n'aurait rien borne : 250 A a 49 V font 12,2 kW,
|
||
// au-dessus des 10 000 de l'ecretage.
|
||
gridSetpointW: 2380,
|
||
},
|
||
},
|
||
{
|
||
id: 'consigne-bilan-max-la-voiture-domine',
|
||
label: 'CONTREFACTUEL 18/09 — meme nuit, meme manque, mais VEHICULE BRANCHE : le max retient la voiture, pas la somme',
|
||
time: '2026-08-08T04:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
soc_percent: '20', battery_current_a: '0',
|
||
pv_power_w: '0', pv_power_10m_avg_w: '0', pv_trend_w: '0',
|
||
house_ac_power_w: '612', vebus_ac_out_l1_power_w: '612',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '700',
|
||
balance_required_soc: '59.1', balance_energy_to_cover_kwh: '3.13',
|
||
balance_status: 'ok', balance_last_run: '08/08 03:58',
|
||
balance_grid_energy_needed_kwh: '5.672',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
// Terme voiture 4825 W contre terme batterie 1538 W : le `max` retient la
|
||
// voiture, et la consigne vaut ce qu'elle vaut aujourd'hui. Avec une
|
||
// ADDITION elle monterait de 1538 W de plus — une autorisation en plus sur
|
||
// le chemin DOMINANT (85 nuits sur 88 ont un vehicule branche) pour
|
||
// corriger un chemin emprunte zero fois. C'est ce scenario qui epingle le
|
||
// choix du max, et il doit rester a la valeur historique.
|
||
gridSetpointW: 5437,
|
||
},
|
||
},
|
||
{
|
||
// ======== LA FENETRE DE MIDI EST UNE FENETRE D'HEURES CREUSES (18/09) ========
|
||
// L'engagement de session etait gate sur `night_hc` seul : la charge HC
|
||
// manuelle demandee par le proprietaire ne pouvait rien faire a midi. Le
|
||
// plancher relevait bien la cible du bilan, mais aucune session ne
|
||
// s'engageait — donc pas de contacteur, pas de consigne, et un plancher du
|
||
// soir releve pour rien.
|
||
id: 'hc-midi-bilan-reclame-engage-la-session',
|
||
label: 'MIDI 18/09 — journee sombre, le bilan reclame 85 % pour un SOC de 40 : la session HC de midi doit s engager',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '40', battery_current_a: '-8',
|
||
pv_power_w: '200', pv_power_10m_avg_w: '200', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '85', balance_energy_to_cover_kwh: '7.6',
|
||
balance_grid_energy_needed_kwh: '7.605',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
},
|
||
expect: {
|
||
profile: 'midday_hc_or_solar',
|
||
sessionCommittedService: 'turn_on',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'hc-midi-soleil-aucun-engagement',
|
||
label: 'CONTREFACTUEL — meme midi en plein soleil : le bilan ne reclame rien (22 %), aucune session malgre un SOC sous le palier du matin',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '30', battery_current_a: '60',
|
||
pv_power_w: '7000', pv_power_10m_avg_w: '6800', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
// Le croisement PV est passe : le bilan retombe au plancher dur.
|
||
balance_required_soc: '22', balance_energy_to_cover_kwh: '0',
|
||
balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
},
|
||
expect: {
|
||
profile: 'midday_hc_or_solar',
|
||
// ⚠️ LE SCENARIO QUI JUSTIFIE LE CHOIX DU CRITERE. A midi on compare le SOC
|
||
// au SOC REQUIS du bilan (22), et non a `batteryMorningTargetPct` qui
|
||
// contient le palier de prevision — celui-ci vaudrait 35 par beau temps,
|
||
// donc ce SOC de 30 % engagerait une session reseau EN PLEIN SOLEIL,
|
||
// c'est-a-dire la substitution qu'on a mis des semaines a eteindre.
|
||
takeoverNeeded: false,
|
||
},
|
||
},
|
||
// ======== JEEP BRANCHEE A MIDI, DEPART 15:30 (28/09/2026) ========
|
||
// « JEEP branchee engage le reseau en HC midi si l'analyse de production le
|
||
// justifie » : le BILAN fait l'analyse et publie un verdict, le controleur le
|
||
// relaie sous les memes gardes (statut, fraicheur) que la cible. Ici la
|
||
// batterie n'a rien a reclamer (SOC 45 > 30) : seul le verdict voiture engage.
|
||
{
|
||
id: 'hc-midi-jeep-verdict-bilan-engage-la-session',
|
||
label: 'MIDI 28/09 — JEEP branchee, le bilan juge le solaire insuffisant avant 15:30 : la session HC de midi s engage',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on',
|
||
evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '10',
|
||
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||
balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_vehicle_grid_needed: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', sessionCommittedService: 'turn_on', takeoverNeeded: true },
|
||
},
|
||
// CHAUFFE-EAU (07/10/2026) : un jour faible, l'eau chaude se fait en HC sur
|
||
// EDF a pleine consommation -> la session HC s'engage meme sans voiture et
|
||
// sans manque batterie.
|
||
{
|
||
id: 'hc-midi-jour-faible-chauffe-eau-engage-la-session',
|
||
label: 'MIDI — jour faible (decision du bilan), pas de voiture, SOC au-dessus du requis : la session HC s engage pour le chauffe-eau',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '2',
|
||
pv_power_w: '1200', pv_power_10m_avg_w: '1200', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900', evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0', balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_ecs_nuit: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', sessionCommittedService: 'turn_on', takeoverNeeded: true },
|
||
},
|
||
// CONSIGNE (07/10/2026, releve de 14:20) : HC de midi, solaire actif (chargeurs
|
||
// au max), manque batterie chiffre par le bilan, DVCC releve a 99 A par l'ACM.
|
||
// Avant : la consigne etait TENUE a 5 198 W (la maison), batterie a 3-6 A.
|
||
{
|
||
id: 'consigne-hc-midi-manque-batterie-pas-de-maintien',
|
||
label: 'MIDI 07/10 — session HC, soleil, manque batterie 2,6 kWh, DVCC 99 A : la consigne monte au plafond (maison + batterie) au lieu d etre tenue',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
night_grid_session_committed_prev: 'on', night_takeover_prev: 'on',
|
||
acin1_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 1800),
|
||
grid_switch_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 1800),
|
||
soc_percent: '35', battery_current_a: '3', battery_voltage_v: '49.5',
|
||
pv_power_w: '1700', pv_power_10m_avg_w: '1700', pv_trend_w: '0',
|
||
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
house_ac_power_w: '5300', vebus_ac_out_l1_power_w: '5300', evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '5198',
|
||
balance_required_soc: '43.6', balance_energy_to_cover_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_ecs_nuit: 'on',
|
||
balance_grid_energy_needed_kwh: '2.6', dvcc_max_charge_current_a: '99',
|
||
},
|
||
// 5 300 W de maison + min(2,6 kWh / 1,4 h restantes = 1 857 W, DVCC 99 A x 49,5 V)
|
||
// + la marge d'un pas EVCS (230 W) = 7 387 W. Tenue, elle serait restee a la maison.
|
||
expect: { profile: 'midday_hc_or_solar', gridSetpointW: 7387 },
|
||
},
|
||
{
|
||
id: 'consigne-hc-midi-sans-manque-maintien-garde',
|
||
label: 'CONTREFACTUEL — meme midi sans manque batterie : la consigne reste tenue (le solaire garde la priorite)',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
night_grid_session_committed_prev: 'on', night_takeover_prev: 'on',
|
||
acin1_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 1800),
|
||
grid_switch_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 1800),
|
||
soc_percent: '35', battery_current_a: '3', battery_voltage_v: '49.5',
|
||
pv_power_w: '1700', pv_power_10m_avg_w: '1700', pv_trend_w: '0',
|
||
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
house_ac_power_w: '5300', vebus_ac_out_l1_power_w: '5300', evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '5198',
|
||
balance_required_soc: '43.6', balance_energy_to_cover_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_ecs_nuit: 'on',
|
||
balance_grid_energy_needed_kwh: '0', dvcc_max_charge_current_a: '99',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', gridSetpointW: 5530 },
|
||
},
|
||
{
|
||
id: 'hc-midi-jour-faible-bilan-perime-aucun-engagement',
|
||
label: 'CONTREFACTUEL — jour faible mais bilan vieux de 2 h : on ne relaie pas une decision perimee',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '2',
|
||
pv_power_w: '1200', pv_power_10m_avg_w: '1200', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900', evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0', balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 11:00',
|
||
balance_ecs_nuit: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'hc-nuit-jour-faible-chauffe-eau-engage-la-session',
|
||
label: 'NUIT 03:00 — jour faible, SOC 45 au-dessus du requis 30 : la session de nuit s engage quand meme pour le chauffe-eau',
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '-3',
|
||
house_ac_power_w: '700', vebus_ac_out_l1_power_w: '700', evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '2.0',
|
||
balance_status: 'ok', balance_last_run: '08/08 02:58',
|
||
balance_ecs_nuit: 'on',
|
||
},
|
||
expect: { profile: 'night_hc', sessionCommittedService: 'turn_on', takeoverNeeded: true },
|
||
},
|
||
{
|
||
id: 'hc-nuit-jour-normal-aucun-engagement',
|
||
label: 'CONTREFACTUEL — meme nuit, jour normal : rien ne justifie EDF',
|
||
time: '2026-08-08T03:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '-3',
|
||
house_ac_power_w: '700', vebus_ac_out_l1_power_w: '700', evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '2.0',
|
||
balance_status: 'ok', balance_last_run: '08/08 02:58',
|
||
balance_ecs_nuit: 'off',
|
||
},
|
||
expect: { profile: 'night_hc', takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'hc-midi-jeep-sans-verdict-aucun-engagement',
|
||
label: 'CONTREFACTUEL — meme midi, le bilan juge le solaire suffisant : aucune session',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on',
|
||
evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '10',
|
||
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||
balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_vehicle_grid_needed: 'off',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'hc-midi-jeep-verdict-voiture-debranchee',
|
||
label: 'CONTREFACTUEL — verdict encore a ON mais voiture debranchee : aucune session',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off',
|
||
evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '10',
|
||
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||
balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_vehicle_grid_needed: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'hc-midi-jeep-verdict-bilan-perime',
|
||
label: 'CONTREFACTUEL — verdict a ON mais bilan vieux de 2 h : on ne relaie pas un verdict perime',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on',
|
||
evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '10',
|
||
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||
balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 11:00',
|
||
balance_vehicle_grid_needed: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'hc-midi-session-engagee-voiture-sur-reseau',
|
||
label: 'MIDI — session engagee, contacteur ferme, plein soleil : la voiture passe sur le reseau (batterie et voiture ensemble)',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
night_grid_session_committed_prev: 'on',
|
||
evcs_device_connected: 'on',
|
||
soc_percent: '45', battery_current_a: '10',
|
||
pv_power_w: '6000', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||
balance_grid_energy_needed_kwh: '0',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_vehicle_grid_needed: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', sourceMode: 'grid_hc_allowed', takeoverNeeded: true },
|
||
},
|
||
{
|
||
// RELEVE 09/10 13:59 : JEEP branchee 13:01 a 52 %, SOC 55-57, session de midi
|
||
// engagee (jour faible), EDF ferme : « Conditions de demarrage non reunies ».
|
||
// La garde gridAllowedBySoc (SOC <= 35 hors nuit) bloquait le demarrage. Le
|
||
// scenario precedent ne verifiait que la source, pas la charge.
|
||
id: 'hc-midi-session-engagee-voiture-demarre-soc-55',
|
||
label: 'RELEVE 09/10 13:59 — midi, session engagee, EDF ferme, SOC 55 : la JEEP DEMARRE sur le reseau',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
night_grid_session_committed_prev: 'on',
|
||
soc_percent: '55', battery_current_a: '30',
|
||
pv_power_w: '1750', pv_power_10m_avg_w: '1700', pv_trend_w: '0',
|
||
house_ac_power_w: '1650', vebus_ac_out_l1_power_w: '1650',
|
||
evcs_ac_power_w: '0',
|
||
balance_required_soc: '100', balance_energy_to_cover_kwh: '0.33',
|
||
balance_grid_energy_needed_kwh: '12.9',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_vehicle_grid_needed: 'off', balance_ecs_nuit: 'on',
|
||
},
|
||
expect: { profile: 'midday_hc_or_solar', sourceMode: 'grid_hc_allowed', desiredCharging: true },
|
||
},
|
||
{
|
||
id: 'consigne-hc-midi-chargeur-bride-batterie-loin-de-sa-limite',
|
||
label: 'RELEVE 09/10 13:59 — midi, manque 12,9 kWh, un chargeur bride, batterie a 32 A sur 140 : la consigne NE DESCEND PAS a la maison',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
night_grid_session_committed_prev: 'on',
|
||
soc_percent: '57', battery_current_a: '32',
|
||
pv_power_w: '1750', pv_power_10m_avg_w: '1700', pv_trend_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
house_ac_power_w: '1650', vebus_ac_out_l1_power_w: '1650',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '2524',
|
||
dvcc_max_charge_current_a: '140',
|
||
balance_required_soc: '100', balance_energy_to_cover_kwh: '0.33',
|
||
balance_grid_energy_needed_kwh: '12.9',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||
balance_ecs_nuit: 'on',
|
||
},
|
||
// Maison 1 650 + 140 A x 52,9 V (tension de reference) = 9 059 W ; sans le correctif : 2 087 W (descente).
|
||
expect: { gridSetpointW: 9059 },
|
||
},
|
||
{
|
||
id: 'consigne-bilan-plancher-nuage-exclut-la-batterie',
|
||
label: 'CONSIGNE 18/09 — midi, soleil bride, manque batterie, PAS de voiture : le terme batterie ne doit PAS remonter le plancher nuage',
|
||
time: MIDDAY,
|
||
overrides: {
|
||
// ⚠️ LA FENETRE CREUSE N'EST PAS FORCEMENT LA NUIT. Celle de midi
|
||
// (12:24-14:24) admet simultanement ce qu'on croyait incompatible : du
|
||
// soleil fort, des chargeurs brides, et un manque batterie. C'est la
|
||
// seule configuration qui distingue un plancher nuage correct d'un
|
||
// plancher pollue par le terme batterie — et le discriminant n'est pas
|
||
// l'heure, c'est un DVCC HAUT. A 30 A le terme batterie plafonnerait sous
|
||
// le budget de surtension et les deux variantes rendraient la meme chose.
|
||
//
|
||
// Forme reelle : c'est l'episode du 17/09 — force_grid pose a midi,
|
||
// soleil, aucune voiture — avec en plus un manque batterie et l'ACM en
|
||
// boost de journee (180 A, son profil de 07:30).
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
// 09/10 : batterie a sa limite (170 A sur 180) -> la descente est legitime ;
|
||
// a 20 A elle ne l'est plus (scenario consigne-hc-midi-chargeur-bride-batterie-loin-de-sa-limite).
|
||
soc_percent: '35', battery_current_a: '170',
|
||
pv_power_w: '7000', pv_power_10m_avg_w: '6500', pv_trend_w: '0',
|
||
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '8000',
|
||
dvcc_max_charge_current_a: '180',
|
||
// 35 % -> 90 % entierement au-dessus du genou : 55 points x 0,169 = 9,295 kWh.
|
||
balance_required_soc: '90', balance_grid_energy_needed_kwh: '9.295',
|
||
balance_energy_to_cover_kwh: '9.295',
|
||
balance_status: 'ok', balance_last_run: '08/08 12:30',
|
||
},
|
||
expect: {
|
||
// Des chargeurs brides prouvent qu'il reste de la ressource : la consigne
|
||
// DESCEND. Le plancher qui borne cette descente vaut maison + voiture,
|
||
// soit 900 W ici. Si le terme batterie y entrait, le plancher remonterait
|
||
// de plusieurs kW et BLOQUERAIT la descente — la consigne resterait haute
|
||
// et evincerait le solaire, c'est-a-dire le defaut du 11/09 reintroduit
|
||
// par la porte de derriere. Ce scenario ne teste donc pas une bizarrerie
|
||
// de forme, il teste le retour d'un incident connu.
|
||
gridSetpointW: 4219,
|
||
},
|
||
},
|
||
{
|
||
id: 'nuit-sans-vehicule-batterie-couverte',
|
||
label: "CONTREFACTUEL — meme nuit sans vehicule mais SOC 70 au-dessus des 59,1 % requis : aucune session, la batterie passe la nuit seule",
|
||
time: '2026-08-08T04:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
soc_percent: '70', battery_current_a: '-12',
|
||
pv_power_w: '0', pv_power_10m_avg_w: '0', pv_trend_w: '0',
|
||
house_ac_power_w: '612', vebus_ac_out_l1_power_w: '612',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '700',
|
||
balance_required_soc: '59.1', balance_energy_to_cover_kwh: '3.13',
|
||
balance_status: 'ok', balance_last_run: '08/08 03:58',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: false,
|
||
sessionCommittedService: null,
|
||
},
|
||
},
|
||
{
|
||
id: 'nuit-sans-vehicule-hors-fenetre',
|
||
label: "CONTREFACTUEL — meme deficit mais a 23:00, HORS fenetre HC : aucune session. Le relachement reste la fin de fenetre, donc monotone.",
|
||
time: '2026-08-08T23:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
soc_percent: '20', battery_current_a: '-12',
|
||
pv_power_w: '0', pv_power_10m_avg_w: '0', pv_trend_w: '0',
|
||
house_ac_power_w: '612', vebus_ac_out_l1_power_w: '612',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '700',
|
||
balance_required_soc: '92.2', balance_energy_to_cover_kwh: '6.33',
|
||
balance_status: 'ok', balance_last_run: '08/08 22:58',
|
||
},
|
||
expect: {
|
||
takeoverNeeded: false,
|
||
sessionCommittedService: null,
|
||
},
|
||
},
|
||
{
|
||
id: 'nuit-sans-vehicule-bilan-perime',
|
||
label: "CONTREFACTUEL — HC sans vehicule, SOC 20, mais bilan vieux de 2 h : comportement historique, aucune session",
|
||
time: '2026-08-08T04:00:00+02:00',
|
||
overrides: {
|
||
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
soc_percent: '20', battery_current_a: '0',
|
||
pv_power_w: '0', pv_power_10m_avg_w: '0', pv_trend_w: '0',
|
||
house_ac_power_w: '612', vebus_ac_out_l1_power_w: '612',
|
||
evcs_ac_power_w: '0',
|
||
grid_setpoint_w: '700',
|
||
balance_required_soc: '59.1', balance_energy_to_cover_kwh: '3.13',
|
||
balance_status: 'ok', balance_last_run: '08/08 02:00',
|
||
},
|
||
expect: {
|
||
takeoverNeeded: false,
|
||
sessionCommittedService: null,
|
||
},
|
||
},
|
||
// ======== MISE A L'ECHELLE DU PARC (12/09/2026) ========
|
||
// Les bandes de politique batterie sont en amperes absolus, decrivant le parc
|
||
// de 4 modules. Ajouter 4 US5000 (soit 12 equivalents US2000C) doit les
|
||
// mettre a l'echelle, sinon le nouveau parc reste bride au niveau de l'ancien.
|
||
{
|
||
id: 'parc-double-decharge-toleree',
|
||
label: "Parc porte a 12 equivalents (4 US2000C + 4 US5000), BMS declarant 300 A : les -120 A qui arretaient tout a 4 modules deviennent tolerables",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '1000', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '9000', vebus_ac_out_l1_power_w: '9000',
|
||
battery_current_a: '-120',
|
||
modules_en_service: '12',
|
||
bms_max_discharge_current_a: '300', bms_max_charge_current_a: '300',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
state: 'charging_step_down_system_cap',
|
||
},
|
||
},
|
||
{
|
||
id: 'parc-double-plafond-bms',
|
||
label: "CONTREFACTUEL — parc declare a 12 mais le BMS n'autorise que 100 A (cellule faible, cablage) : le plafond BMS prime, -120 A reste un arret dur",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '1000', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '9000', vebus_ac_out_l1_power_w: '9000',
|
||
battery_current_a: '-120',
|
||
modules_en_service: '12',
|
||
bms_max_discharge_current_a: '100', bms_max_charge_current_a: '100',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
state: 'battery_current_hard_stop',
|
||
decision: 'stop_hard',
|
||
},
|
||
},
|
||
{
|
||
id: 'parc-non-declare-comportement-inchange',
|
||
label: "CONTREFACTUEL — aucun parc declare, aucune limite BMS lisible : echelle 1, plafond inactif, comportement d'avant ce chantier",
|
||
time: AFTERNOON,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 14:00:00',
|
||
soc_percent: '94',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3600',
|
||
pv_power_w: '1000', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||
house_ac_power_w: '9000', vebus_ac_out_l1_power_w: '9000',
|
||
battery_current_a: '-120',
|
||
},
|
||
expect: {
|
||
profile: 'outside_window',
|
||
state: 'battery_current_hard_stop',
|
||
decision: 'stop_hard',
|
||
},
|
||
},
|
||
|
||
// =====================================================
|
||
// P12 — CHARGE NUIT SUR LA BATTERIE (15/09/2026)
|
||
// =====================================================
|
||
// Idee du proprietaire : plutot qu'acheter du reseau la nuit pour charger la
|
||
// voiture, lui donner le solaire DEJA STOCKE, et laisser ainsi de la place
|
||
// dans la batterie pour la production du lendemain. Releve de sa nuit du 14
|
||
// au 15/09 : 8 kWh achetes au reseau, batterie encore a 69 % a 5 h du matin,
|
||
// et une prevision de 38,7 kWh le lendemain dont 10,5 auraient ete ecretes.
|
||
//
|
||
// Le mode `battery_assist_night` existait pour exactement ca et etait MORT
|
||
// depuis toujours : il exigeait `batteryCurrentA >= 0`, impossible la nuit
|
||
// puisque la maison tire sur la batterie. P12 remplace ce test de COURANT par
|
||
// un test d'ENERGIE — rester au-dessus de `required_soc_prudent`, que le
|
||
// bilan publie deja. Aucune constante nouvelle.
|
||
|
||
{
|
||
id: 'p12-charge-nuit-sur-batterie',
|
||
label: 'Nuit HC, P12 ON, SOC 69 bien au-dessus du plancher 38,7 -> la voiture puise dans la batterie',
|
||
time: '2026-09-15T02:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '69',
|
||
allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-16',
|
||
evcs_charging_switch: 'off',
|
||
balance_status: 'ok', balance_last_run: '15/09 02:25',
|
||
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
|
||
balance_energy_to_cover_kwh: '3.18',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'battery_assist_night',
|
||
// ⚠️ Verifier que la voiture CHARGE REELLEMENT, pas seulement que le mode
|
||
// est choisi. Sans ces trois lignes, neutraliser la condition de demarrage
|
||
// dans BATTERY_CLOUD_LOCKS ne cassait aucun scenario — constate par
|
||
// mutation le 15/09.
|
||
state: 'start_battery_assist_night_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
takeoverNeeded: false,
|
||
edfOnAction: false,
|
||
},
|
||
},
|
||
|
||
// CONTREFACTUEL — surplus epuise : la bascule vers le reseau se fait TOUTE
|
||
// SEULE. C'est la soupape decrite par le proprietaire, sans regle horaire.
|
||
|
||
{
|
||
id: 'p12-surplus-epuise-le-reseau-prend-le-relais',
|
||
label: 'Nuit HC, P12 ON mais SOC 37 sous le plancher 38,7 -> P7 reprend, EDF se ferme',
|
||
time: '2026-09-15T02:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '37',
|
||
allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-16',
|
||
evcs_charging_switch: 'off',
|
||
balance_status: 'ok', balance_last_run: '15/09 02:25',
|
||
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
|
||
balance_energy_to_cover_kwh: '3.18',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
},
|
||
},
|
||
|
||
// ⚠️ LE SCENARIO ANTI-OSCILLATEUR, le plus important des quatre.
|
||
// Voiture arretee, le plancher descend PLUS VITE que le SOC (-3,7 pt/h
|
||
// contre -2,6) : le surplus reapparaitrait tout seul. Ici le SOC est
|
||
// repasse AU-DESSUS du plancher, mais le verrou est pose — pas de reprise.
|
||
|
||
{
|
||
id: 'p12-verrou-pas-de-reprise-du-surplus',
|
||
label: 'Nuit HC, P12 ON, SOC 45 repasse au-dessus du plancher MAIS verrou pose -> pas de reprise',
|
||
time: '2026-09-15T04:00:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '45',
|
||
allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'on', night_battery_done_prev: 'on',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-16',
|
||
evcs_charging_switch: 'off',
|
||
balance_status: 'ok', balance_last_run: '15/09 03:55',
|
||
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
|
||
balance_energy_to_cover_kwh: '3.18',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
takeoverNeeded: true,
|
||
},
|
||
},
|
||
|
||
// CONTREFACTUEL du toggle : a off, exactement le comportement d'avant.
|
||
|
||
{
|
||
id: 'p12-toggle-off-comportement-historique',
|
||
label: 'Nuit HC, memes conditions mais toggle P12 OFF -> P7 comme avant',
|
||
time: '2026-09-15T02:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '69',
|
||
allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'off', night_battery_done_prev: 'off',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-16',
|
||
evcs_charging_switch: 'off',
|
||
balance_status: 'ok', balance_last_run: '15/09 02:25',
|
||
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
|
||
balance_energy_to_cover_kwh: '3.18',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
},
|
||
},
|
||
|
||
// ⚠️ GARDE DE FRAICHEUR. Le plancher vient du bilan ; s'il est perime, il ne
|
||
// decrit plus la nuit en cours et vider la batterie dessus serait aveugle.
|
||
// Ajoute apres qu'une mutation eut montre que rien ne testait cette garde.
|
||
{
|
||
id: 'p12-bilan-perime-pas-de-charge-batterie',
|
||
label: 'Nuit HC, P12 ON, SOC 69 mais bilan vieux de 3 h -> P7 comme avant, pas de puisage batterie',
|
||
time: '2026-09-15T02:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '69',
|
||
allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
|
||
grid_state: 'off', victron_acin1_available: '0',
|
||
battery_current_a: '-16',
|
||
evcs_charging_switch: 'off',
|
||
balance_status: 'ok', balance_last_run: '14/09 23:25',
|
||
balance_required_soc: '36.8', balance_required_soc_prudent: '38.7',
|
||
balance_energy_to_cover_kwh: '3.18',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
},
|
||
},
|
||
|
||
// ⚠️ P12 DOIT GAGNER LE PREMIER CYCLE DE LA NUIT, OU IL NE GAGNE PAS.
|
||
//
|
||
// Decouvert en essai reel le 15/09 a 05:30. P7 s'etait engage a 05:13, avant
|
||
// que le proprietaire n'active P12 : le helper `night_grid_takeover_needed`
|
||
// etait donc deja ON, et le verrou anti-creneaux de juillet (P2) tenait le
|
||
// reseau — `nightGridHcPreferred` passe bien a false grace a P12, mais
|
||
// `nightTakeoverLatched` maintient `nightGridTakeoverNeeded` de son cote.
|
||
//
|
||
// Et c'est le comportement SUR : basculer reseau -> batterie en pleine
|
||
// session, c'est exactement le creneau que ce verrou existe pour empecher.
|
||
// Aucun des cinq scenarios P12 ne le montrait — ils partent tous d'une nuit
|
||
// vierge. Celui-ci epingle la contrainte pour qu'un changement futur soit
|
||
// delibere.
|
||
{
|
||
id: 'p12-verrou-p2-tient-si-le-reseau-est-deja-engage',
|
||
label: 'Nuit HC, P7 deja engage (takeover ON) puis P12 active -> le verrou anti-creneaux tient, pas de bascule',
|
||
time: '2026-09-15T05:30:00+02:00',
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '69',
|
||
allow_grid_hc_night: 'on',
|
||
night_battery_charge_enabled: 'on', night_battery_done_prev: 'off',
|
||
// LE point du scenario : la reprise reseau est deja engagee.
|
||
night_takeover_prev: 'on',
|
||
grid_state: 'on', victron_acin1_available: '1',
|
||
battery_current_a: '1',
|
||
evcs_charging_switch: 'on',
|
||
balance_status: 'ok', balance_last_run: '15/09 05:25',
|
||
balance_required_soc: '36.8', balance_required_soc_prudent: '36.2',
|
||
balance_energy_to_cover_kwh: '3.18',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
sourceMode: 'grid_hc_allowed',
|
||
takeoverNeeded: true,
|
||
},
|
||
},
|
||
|
||
// =====================================================
|
||
// BUDGET VOITURE DE LA NUIT (30/09/2026, decisions du proprietaire)
|
||
// =====================================================
|
||
// « Definir combien la voiture peut prendre sans utiliser EDF, et brider la
|
||
// charge a cette valeur ou autoriser une charge complete avec EDF. » Par
|
||
// defaut « sans EDF » ; « complete » = budget batterie puis EDF. Budget fige
|
||
// a l'ouverture avec l'index du compteur de la borne ; arret au compteur.
|
||
// Releve de reference : 30/09 01:25, SOC 57, requis 39,2, prudent 44,7.
|
||
{
|
||
id: 'budget-fige-a-l-ouverture',
|
||
label: 'Nuit HC, sans EDF, budget 3,7 kWh publie : fige avec l index 4000, la voiture part sur la batterie',
|
||
time: '2026-09-30T01:25:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 01:20', night_charge_mode: 'sans_edf', night_budget_prev: '', evcs_total_energy_kwh: '4000' },
|
||
expect: { profile: 'night_hc', sourceMode: 'battery_assist_night',
|
||
state: 'start_battery_assist_night_min_current', desiredCharging: true,
|
||
nightBudgetWrite: '3.7|4000', takeoverNeeded: false, edfOnAction: false },
|
||
},
|
||
{
|
||
id: 'budget-pas-atteint-la-charge-continue',
|
||
label: 'Budget 3,7, 2 kWh delivres, SOC 52 au-dessus du prudent : la voiture continue sur la batterie',
|
||
time: '2026-09-30T02:10:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 02:05', ...EN_CHARGE_BATTERIE, soc_percent: '52', night_charge_mode: 'sans_edf',
|
||
night_budget_prev: '3.7|4000', evcs_total_energy_kwh: '4002' },
|
||
expect: { sourceMode: 'battery_assist_night', desiredCharging: true, nightBudgetWrite: null,
|
||
takeoverNeeded: false, edfOnAction: false },
|
||
},
|
||
{
|
||
id: 'budget-atteint-sans-edf-la-voiture-attend',
|
||
label: 'Budget 3,7 atteint (3,75 delivres), SOC 50 encore au-dessus du prudent, sans EDF : arret, pas de reseau',
|
||
time: '2026-09-30T02:30:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 02:25', ...EN_CHARGE_BATTERIE, soc_percent: '50', night_charge_mode: 'sans_edf',
|
||
night_budget_prev: '3.7|4000', evcs_total_energy_kwh: '4003.75' },
|
||
expect: { sourceMode: 'blocked', desiredCharging: false, takeoverNeeded: false, edfOnAction: false },
|
||
},
|
||
{
|
||
id: 'budget-atteint-complete-edf-finit',
|
||
label: 'CONTREFACTUEL — meme nuit en charge complete : EDF prend le relais en HC',
|
||
time: '2026-09-30T02:30:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 02:25', ...EN_CHARGE_BATTERIE, soc_percent: '50', night_charge_mode: 'complete',
|
||
night_budget_prev: '3.7|4000', evcs_total_energy_kwh: '4003.75' },
|
||
expect: { sourceMode: 'grid_hc_allowed', takeoverNeeded: true, edfOnAction: true },
|
||
},
|
||
{
|
||
id: 'budget-sans-edf-plancher-dur-edf-protege',
|
||
label: 'Sans EDF mais SOC 20 sous le plancher dur : EDF se ferme quand meme pour la batterie',
|
||
time: '2026-09-30T04:00:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 03:55', soc_percent: '20', night_charge_mode: 'sans_edf',
|
||
night_budget_prev: '3.7|4000', evcs_total_energy_kwh: '4003.75' },
|
||
// 08/10 (proprietaire) : EDF se ferme pour la BATTERIE en heures creuses, la
|
||
// voiture en profite meme « sans EDF » (option A du 04/10 abandonnee).
|
||
expect: { sourceMode: 'grid_forced', edfOnAction: true, takeoverNeeded: true },
|
||
},
|
||
{
|
||
id: 'budget-sans-edf-reprise-reseau-voiture-bloquee',
|
||
label: 'Nuit du 03-04/10 rejouee : budget 1,41 atteint, SOC 39 sous la cible 39,5 a 02:57 : EDF ferme pour la batterie, la JEEP en profite (08/10)',
|
||
time: '2026-10-04T02:57:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '04/10 02:55', soc_percent: '39',
|
||
balance_required_soc: '39.5', balance_required_soc_prudent: '40.4',
|
||
night_charge_mode: 'sans_edf', night_budget_prev: '1.41|4038.8', evcs_total_energy_kwh: '4040.2' },
|
||
expect: { sourceMode: 'grid_forced', edfOnAction: true, takeoverNeeded: true },
|
||
},
|
||
{
|
||
id: 'budget-bilan-perime-sans-edf-rien',
|
||
label: 'Sans EDF, bilan vieux de 3 h : aucun budget fige, la voiture attend, pas de reseau',
|
||
time: '2026-09-30T01:25:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, night_charge_mode: 'sans_edf', balance_last_run: '29/09 22:20',
|
||
night_budget_prev: '', evcs_total_energy_kwh: '4000' },
|
||
expect: { sourceMode: 'blocked', desiredCharging: false, nightBudgetWrite: null,
|
||
takeoverNeeded: false, edfOnAction: false },
|
||
},
|
||
{
|
||
id: 'p12-verrou-pose-quand-le-budget-est-atteint',
|
||
label: 'Budget atteint en session : le verrou P12 est ecrit (une seule passe sur batterie)',
|
||
time: '2026-09-30T02:30:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, ...EN_CHARGE_BATTERIE, balance_last_run: '30/09 02:25', soc_percent: '50',
|
||
night_charge_mode: 'sans_edf', night_budget_prev: '3.7|4000', evcs_total_energy_kwh: '4003.75' },
|
||
expect: { nightBatteryDoneService: 'turn_on' },
|
||
},
|
||
{
|
||
id: 'p12-verrou-leve-en-fin-de-session',
|
||
label: '08:00, verrou encore pose : il est leve (sinon la charge sur batterie mourait pour toutes les nuits suivantes)',
|
||
time: '2026-09-30T08:00:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 07:55', night_battery_done_prev: 'on' },
|
||
expect: { nightBatteryDoneService: 'turn_off' },
|
||
},
|
||
// Jours de depart du matin (01/10/2026) : releve reel du jeudi 01/10 06:00,
|
||
// JEEP pleine a 95 %, SOC 37 pour une cible de 35, depart JEEP 06:30. La marge
|
||
// de 30 min declenchait la « recharge urgente avant depart » (0 kWh manquant)
|
||
// et fermait EDF. Le jeudi, la JEEP ne part pas.
|
||
{
|
||
id: 'depart-matin-jour-de-depart-rien-ne-manque',
|
||
label: 'Jour de depart, SOC 37 au-dessus de la cible du bilan (28,6) : il ne manque rien, aucune urgence a 06:00 (EDF ferme pour rien les 21, 22, 25, 27/09 et 01/10)',
|
||
time: '2026-10-01T06:00:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '01/10 05:55', soc_percent: '37',
|
||
balance_required_soc: '28.6', balance_required_soc_prudent: '29.7',
|
||
evcs_vehicle: 'JEEP', departure_time_jeep: '06:30:00', departure_day_ok: 'on',
|
||
night_charge_mode: 'sans_edf', night_budget_prev: '5.93|4010', evcs_total_energy_kwh: '4015' },
|
||
expect: { edfOnAction: false, takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'depart-matin-jour-de-depart-batterie-sous-la-cible',
|
||
label: 'Jour de depart, SOC 25 sous la cible du bilan (28,6) : il manque 1,1 kWh, l urgence engage le reseau avant 06:30',
|
||
time: '2026-10-01T06:00:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '01/10 05:55', soc_percent: '25',
|
||
balance_required_soc: '28.6', balance_required_soc_prudent: '29.7',
|
||
evcs_vehicle: 'JEEP', departure_time_jeep: '06:30:00', departure_day_ok: 'on',
|
||
night_charge_mode: 'sans_edf', night_budget_prev: '5.93|4010', evcs_total_energy_kwh: '4015' },
|
||
// « sans EDF » : l'urgence ferme EDF pour la batterie, la voiture en profite (08/10).
|
||
expect: { sourceMode: 'grid_forced', edfOnAction: true },
|
||
},
|
||
{
|
||
id: 'depart-matin-pas-un-jour-de-depart',
|
||
label: 'Jeudi : la JEEP ne part pas, aucune urgence de depart, EDF reste ouvert',
|
||
time: '2026-10-01T06:00:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '01/10 05:55', soc_percent: '37',
|
||
balance_required_soc: '28.6', balance_required_soc_prudent: '29.7',
|
||
evcs_vehicle: 'JEEP', departure_time_jeep: '06:30:00', departure_day_ok: 'off',
|
||
night_charge_mode: 'sans_edf', night_budget_prev: '5.93|4010', evcs_total_energy_kwh: '4015' },
|
||
expect: { edfOnAction: false, takeoverNeeded: false },
|
||
},
|
||
{
|
||
id: 'budget-efface-en-fin-de-session',
|
||
label: 'Fin de fenetre HC (08:00) : le budget fige est efface',
|
||
time: '2026-09-30T08:00:00+02:00',
|
||
overrides: { ...BUDGET_NUIT, balance_last_run: '30/09 07:55', night_budget_prev: '3.7|4000', evcs_total_energy_kwh: '4003.75' },
|
||
expect: { nightBudgetWrite: '' },
|
||
},
|
||
|
||
// ===================================================================
|
||
// BOOST 28 A du 18/09/2026 — le verrou survivait a sa cause (HANDOFF)
|
||
// Releve reel : clic BOOST 13:08:50, contacteur ferme 13:09:53, garde
|
||
// batterie a -32,4 A a 13:09:30 (23 s AVANT l'arrivee d'EDF), verrou
|
||
// 20 min reseau present. Trois volets, un scenario chacun + contrefactuel.
|
||
// ===================================================================
|
||
{
|
||
id: 'boost-attente-reseau-courant-mini',
|
||
label: "Session active, force_grid demande, reseau PAS ENCORE la → on retombe au courant MINI (le 13 A tenu sur la batterie est le defaut du 18/09)",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
// Charge solaire en cours a 13 A, min_run PAS ecoule (start recent).
|
||
last_start_at: '2026-08-08 12:58:30',
|
||
evcs_current_a: '13', evcs_set_current_a: '13', evcs_command_current_a: '13',
|
||
evcs_ac_power_w: '2990',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
// Le contacteur n'est PAS encore ferme : c'est la fenetre de l'incident.
|
||
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
|
||
soc_percent: '55', pv_power_w: '1200', pv_power_10m_avg_w: '1400',
|
||
house_ac_power_w: '3900', vebus_ac_out_l1_power_w: '3900',
|
||
// Batterie sollicitee mais SOUS le garde dur : la charge continue.
|
||
battery_current_a: '-20',
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
state: 'hold_min_run_wait_grid_takeover',
|
||
decision: 'hold',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
{
|
||
id: 'boost-garde-sans-reseau-emet-no-grid',
|
||
label: "Le garde batterie mord RESEAU ABSENT → blockReason 'grid_battery_guard_no_grid' (et pas le verrou plein)",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 12:58:30',
|
||
// 07/10 : deja AU COURANT MINI (6 A) — au-dessus, la voiture est d'abord
|
||
// bridee a 6 A (scenarios `garde-reseau-bride-a-6a-*`), l'arret ne vient
|
||
// qu'ensuite.
|
||
evcs_current_a: '6', evcs_set_current_a: '6', evcs_command_current_a: '6',
|
||
evcs_ac_power_w: '1380',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
|
||
// Le releve reel du 13:09:30 : -32,4 A, sous le garde dur de sa bande.
|
||
soc_percent: '55', pv_power_w: '300', pv_power_10m_avg_w: '400',
|
||
house_ac_power_w: '3900', vebus_ac_out_l1_power_w: '3900',
|
||
// ⚠️ Fenetre etroite VOULUE : le garde reseau (grid_battery_hard_a -20)
|
||
// doit mordre SANS que le hard general de la bande 40-60 (~-20,9 dans ce
|
||
// fixture) ne prenne la main — c'est exactement la hierarchie de
|
||
// production du 18/09 (-32,4 A avec un parc 13/4 ou le hard general est
|
||
// a -98 A). Le fixture n'etant pas a l'echelle du parc reel, on se place
|
||
// entre les deux seuils DU FIXTURE.
|
||
battery_current_a: '-20.5',
|
||
},
|
||
expect: {
|
||
state: 'stop_grid_battery_guard',
|
||
decision: 'stop',
|
||
desiredCharging: false,
|
||
blockReason: 'grid_battery_guard_no_grid',
|
||
},
|
||
},
|
||
{
|
||
id: 'garde-reseau-bride-a-6a-reseau-absent',
|
||
label: "07/10 : meme garde depasse a 13 A, reseau absent → voiture bridee a 6 A, ni arret ni verrou",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 12:58:30',
|
||
evcs_current_a: '13', evcs_set_current_a: '13', evcs_command_current_a: '13',
|
||
evcs_ac_power_w: '2990',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
|
||
soc_percent: '55', pv_power_w: '300', pv_power_10m_avg_w: '400',
|
||
house_ac_power_w: '3900', vebus_ac_out_l1_power_w: '3900',
|
||
battery_current_a: '-20.5',
|
||
},
|
||
expect: {
|
||
state: 'charging_cut_to_min_grid_guard',
|
||
decision: 'step_down',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
{
|
||
id: 'garde-reseau-bride-a-6a-pointe-maison-0610',
|
||
label: "06/10 13:46 rejoue : force_grid_now a 20 A, reseau present, pointe de cuisine → voiture bridee a 6 A au lieu d'un arret + 20 min de verrou",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'on', control_latch: 'on', charge_permission_now: 'on',
|
||
last_start_at: '2026-08-08 12:30:00',
|
||
evcs_current_a: '20', evcs_set_current_a: '20', evcs_command_current_a: '20',
|
||
evcs_ac_power_w: '3470',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '3970',
|
||
acin1_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 1800),
|
||
grid_switch_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 1800),
|
||
soc_percent: '55', pv_power_w: '1240', pv_power_10m_avg_w: '1500',
|
||
house_ac_power_w: '7100', vebus_ac_out_l1_power_w: '7100',
|
||
// Meme fenetre etroite que ci-dessus (garde reseau -20, hard general ~-20,9
|
||
// dans ce fixture) : en production -41,9 A pour un hard general a -98 A.
|
||
battery_current_a: '-20.5',
|
||
},
|
||
expect: {
|
||
state: 'charging_cut_to_min_grid_guard',
|
||
decision: 'step_down',
|
||
desiredCharging: true,
|
||
currentA: 6,
|
||
},
|
||
},
|
||
{
|
||
id: 'boost-verrou-no-grid-leve-a-l-arrivee-du-reseau',
|
||
label: "Verrou '_no_grid' pose, le reseau ARRIVE → le verrou se leve, la charge repart (le 18/09 elle restait bloquee 20 min)",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'off', control_latch: 'off', charge_permission_now: 'off',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
// Le verrou du releve reel : pose il y a 30 s, 20 min devant lui.
|
||
last_block_reason: 'grid_battery_guard_no_grid',
|
||
blocked_until: '2026-08-08 13:19:30',
|
||
last_stop_at: '2026-08-08 12:59:30',
|
||
prev_surplus_state: 'stop_grid_battery_guard',
|
||
// Le contacteur s'est ferme 23 s apres le stop : reseau PRESENT.
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '900',
|
||
soc_percent: '55', pv_power_w: '1200', pv_power_10m_avg_w: '1400',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
battery_current_a: '2',
|
||
acin1_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 400),
|
||
grid_switch_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 400),
|
||
},
|
||
expect: {
|
||
sourceMode: 'grid_forced',
|
||
state: 'start_grid_forced_min_current',
|
||
decision: 'start',
|
||
desiredCharging: true,
|
||
},
|
||
},
|
||
{
|
||
id: 'boost-verrou-plein-tient-reseau-present',
|
||
label: "CONTREFACTUEL anti-oscillateur : verrou 'grid_battery_guard' (garde mordu RESEAU PRESENT, incident du 01/09) → il tient ses 20 min malgre le reseau",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'off', control_latch: 'off', charge_permission_now: 'off',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
last_block_reason: 'grid_battery_guard',
|
||
blocked_until: '2026-08-08 13:19:30',
|
||
last_stop_at: '2026-08-08 12:59:30',
|
||
prev_surplus_state: 'stop_grid_battery_guard',
|
||
grid_state: 'on', victron_acin1_available: '1', grid_power_w: '900',
|
||
soc_percent: '55', pv_power_w: '1200', pv_power_10m_avg_w: '1400',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
battery_current_a: '2',
|
||
acin1_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 400),
|
||
grid_switch_last_changed_ts: String(Date.parse(MIDDAY) / 1000 - 400),
|
||
},
|
||
expect: {
|
||
state: 'wait_reason_lock',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
blockReason: 'grid_battery_guard',
|
||
},
|
||
},
|
||
{
|
||
id: 'boost-verrou-no-grid-tient-reseau-toujours-absent',
|
||
// NOTE : ce n'est pas le verrou qui protege ici mais la branche anterieure
|
||
// wait_grid_source (source reseau demandee, reseau absent => arret). La
|
||
// clause de pertinence '_no_grid && !gridAvailable' dans BCL est donc une
|
||
// ceinture DEFENSIVE (elle couvre un futur reordonnancement de branches) ;
|
||
// la propriete observable epinglee est : AUCUNE tentative sur la batterie.
|
||
label: "Verrou '_no_grid', reseau toujours absent → arret en attente du reseau, aucun retry sur la batterie",
|
||
time: MIDDAY,
|
||
overrides: {
|
||
evcs_vehicle_connected: 'on', evcs_device_connected: 'on',
|
||
evcs_charging_switch: 'off', control_latch: 'off', charge_permission_now: 'off',
|
||
victron_evcs_strategy: 'force_grid_now',
|
||
last_block_reason: 'grid_battery_guard_no_grid',
|
||
blocked_until: '2026-08-08 13:19:30',
|
||
last_stop_at: '2026-08-08 12:59:30',
|
||
prev_surplus_state: 'stop_grid_battery_guard',
|
||
grid_state: 'off', victron_acin1_available: '0', grid_power_w: '0',
|
||
soc_percent: '55', pv_power_w: '300', pv_power_10m_avg_w: '400',
|
||
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||
battery_current_a: '-4',
|
||
},
|
||
expect: {
|
||
state: 'wait_grid_source',
|
||
desiredCharging: false,
|
||
blockReason: 'grid_unavailable',
|
||
},
|
||
},
|
||
];
|