Le correctif precedent bloque l incident de 19:44, mais un excedent proche de zero est ambigu : c est la signature de la censure ET celle de l equilibre de fin de journee. Le releve du 03/09 contient les deux avec le meme excedent — 18:50 : +14 W, ressource refusee ; 19:41 : +1 W, plus rien a donner. Rejeu du 19:41 : batteryAssistSolarStartOk etait encore vrai. Trois minutes avant l incident, le systeme aurait commis la meme faute. Le plan annoncait de lire evcs_balance_pv_censored. Lecture de CENSORSHIP.js : ce drapeau est un OU de motifs dont l un est exactement le proxy qu on voulait remplacer (socPct >= 90 et batterie sans puits). Et il est reste ON jusqu au calcul de 19:50, soit cinq minutes APRES le demarrage de la charge — ce qui l a fait basculer n est pas la censure mais sun_elevation passant sous 10 degres. Il n aurait pas bloque l incident. Le signal juste, ce sont les chargeurs eux-memes : sept entites sensor.*_mppt_operation_mode, jamais lues par aucun workflow. mppt_active = je suis a mon point de puissance maximale, je donne tout ; voltage_current_limited ou off = je me retiens faute de preneur. Le 03/09 elles ont bascule a 19:20, soit 24 minutes avant pv_censored, sans aucun proxy et a la cadence de la boucle. Contre-verification le 01/09 a 14:22 : les trackers restaient limites PENDANT une charge de 3,4 kW sous 6,9 kW de PV, donc le signal ne s auto-annule pas a midi. Collecte sans aucun entity_id code en dur (selectattr sur mppt et operation_mode), expression testee sur /api/template puis gabarit complet re-rendu : 191 champs, JSON valide, aucun null. Lectures inexploitables ecartees, et repli sur le comportement anterieur quand il n en reste aucune. Garde de DEMARRAGE uniquement, structurellement : la lier au sourceMode ou au maintien ferait que la charge, en donnant un preneur au MPPT, detruirait sa propre condition d autorisation — le bistable de l abondance P6 corrige le 24/08. Golden 64 -> 66. Mutation dans trois directions : garde neutralisee -> 1 echec cible, provocation toujours refusee -> 3 echecs, lecture illisible traitee comme "a fond" -> 1 echec sur le scenario de repli. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
1676 lines
62 KiB
JavaScript
1676 lines
62 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.
|
||
{
|
||
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,
|
||
},
|
||
},
|
||
|
||
// ===== 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: 50,
|
||
},
|
||
},
|
||
{
|
||
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: 50,
|
||
},
|
||
},
|
||
];
|