// 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'; 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. { 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-rearm-sous-bande', label: 'Nuit HC 05:30, recovery done mais SOC 32 ≤ cible−2 → ré-engagement takeover (recharge batterie)', 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', state: 'night_session_done_idle', decision: 'hold', desiredCharging: false, currentA: 6, takeoverNeeded: true, edfOnAction: false, 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', label: 'Après-midi hors fenêtre, SOC 95, PV bridé 300 W (< hold 500) → provocation PV (solar_potential)', time: AFTERNOON, overrides: { evcs_vehicle_connected: 'on', evcs_device_connected: 'on', soc_percent: '95', pv_power_w: '300', pv_power_10m_avg_w: '300', pv_trend_w: '0', battery_current_a: '2', house_ac_power_w: '600', vebus_ac_out_l1_power_w: '600', }, 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', }, }, // ================= 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', }, }, ];