Quatre US5000 arrivent aujourd'hui. Les bandes de politique batterie sont en amperes ABSOLUS (souple -50 A / dur -100 A au-dessus de 90 % de SOC, etc.) et decrivent le parc de 4 modules sur lequel elles sont en vigueur -- l'arret dur du 11/09 a mordu au bon endroit, 87 A pour une limite de 70. Les garder telles quelles briderait un parc trois fois plus gros au niveau de l'ancien. Deux lectures, aucune table reecrite : - ECHELLE : le parc DECLARE (`evcs_balance_modules_en_service`, en equivalents US2000C -- un US5000 en vaut DEUX), le meme helper que la capacite utile et que le bilan energetique. Une seule valeur a tenir a jour, et les trois consommateurs restent forcement d'accord. - PLAFOND : ce que le BMS declare accepter a l'instant (`maximum_allowed_discharge_current` / `_charge_current`, nouvellement lus dans le snapshot). On ne s'autorise jamais plus. ⚠️ On ne met PAS a l'echelle sur la limite BMS lue en direct : elle s'effondre quand une cellule monte en tension (04/09 : plafond de charge de 150 a 20 A en cinq secondes). Une regle de confort ne suit pas un signal aussi nerveux ; elle se plafonne dessus, c'est tout. Parc absent ou BMS illisible => echelle 1, plafond inactif, comportement inchange. C'est pourquoi les 95 scenarios existants passent sans modification. Golden 95 -> 98. Mutation : echelle neutralisee -> seul `parc-double-decharge-toleree` tombe ; plafond BMS ignore -> seul `parc-double-plafond-bms` tombe. Verifie en production : execution 05:28:30 en succes, le gabarit rend bms_max_discharge_current_a=100, bms_max_charge_current_a=100, modules_en_service=4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
2513 lines
97 KiB
JavaScript
2513 lines
97 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;
|
||
|
||
const VEHICLE_NIGHT = {
|
||
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',
|
||
};
|
||
|
||
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',
|
||
},
|
||
},
|
||
|
||
// ===== ADVISOR IA — premier câblage réel (09/08/2026, HANDOFF §20) =====
|
||
// Référence : night-soc-sous-cible (SOC 30 < cible règle 35 → grid_forced).
|
||
// Avec la cible advisor 22 APPLIQUÉE : SOC 30 ≥ 22 → grid_hc_allowed.
|
||
{
|
||
id: 'night-advisor-target-applique',
|
||
label: 'Nuit HC, SOC 30, advisor cible 22 appliquée (toggle ON, ok, frais) → hc_allowed au lieu de 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_hc_allowed',
|
||
state: 'night_wait_grid_takeover',
|
||
decision: 'hold',
|
||
desiredCharging: false,
|
||
currentA: 6,
|
||
takeoverNeeded: true,
|
||
edfOnAction: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
{
|
||
id: 'night-advisor-target-stale',
|
||
label: 'Nuit HC, SOC 30, advisor cible 22 mais recommandation > 120 min → fallback règles (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: '07/08 20:00',
|
||
},
|
||
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-advisor-toggle-off',
|
||
label: 'Nuit HC, SOC 30, advisor cible 22 mais toggle APPLIQUER OFF → règles inchangées (grid_forced)',
|
||
time: NIGHT,
|
||
overrides: {
|
||
...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on',
|
||
advisor_apply_night_target: 'off',
|
||
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',
|
||
},
|
||
},
|
||
|
||
// ===== P9 09/08/2026 : charge réseau avant départ (hors HC) + sélecteur véhicule =====
|
||
// VW départ 08:50 (helper existant), JEEP départ 06:30 (nouveau helper).
|
||
// À 08:00 (morning_solar, PV nul) : VW → fenêtre départ active (50 min) ;
|
||
// JEEP → départ passé depuis 90 min → rien.
|
||
{
|
||
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',
|
||
},
|
||
},
|
||
{
|
||
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',
|
||
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',
|
||
},
|
||
},
|
||
|
||
// ================= 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',
|
||
},
|
||
},
|
||
{
|
||
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',
|
||
},
|
||
},
|
||
|
||
{
|
||
// SENS UNIQUE : le bilan ne doit jamais ABAISSER la cible.
|
||
id: 'cible-bilan-ne-baisse-pas-la-cible',
|
||
label: "Nuit HC, SOC 30, le bilan ne demande que 25 % → le palier 35 % reste maître → le réseau est maintenu",
|
||
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',
|
||
state: 'night_session_done_idle',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
},
|
||
},
|
||
|
||
{
|
||
// 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',
|
||
},
|
||
expect: {
|
||
profile: 'night_hc',
|
||
takeoverNeeded: true,
|
||
takeoverHelperService: 'turn_on',
|
||
sessionCommittedService: 'turn_on',
|
||
gridSetpointW: 5437,
|
||
},
|
||
},
|
||
{
|
||
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',
|
||
},
|
||
},
|
||
];
|