Voiture en solaire : arret avant le temps mini si la batterie paie, tolerance nuage bornee par le SOC requis prudent

This commit is contained in:
Tallagrand Thierry
2026-10-12 23:44:59 +02:00
parent ce18162977
commit 6a42cce5e5
6 changed files with 305 additions and 6 deletions

View File

@@ -2388,5 +2388,15 @@ batterie a 32 A et JEEP (branchee 13:01, 52 %) qui ne demarre pas. SOC 47 % a 12
SPARK. Sans relance : l'alerte reste en base, la revue de 07:30 la reprend (« regarde les alertes d'hier »). Le
proprietaire ne corrige rien lui-meme dans HA sur la seule foi d'une alerte.
### 12/10 — La VW payee par la batterie maison : temps mini et tolerance nuage bornes
- Constat (proprietaire, via SPARK) : VW branchee 14:08 -> 16:59, 3,6 kWh dont **~1,9 kWh pris a la batterie** (51 min sur 70 sous -3 A ; 16:16-16:44 batterie a -40/-74 A, maison 5,5-6,2 kW, PV 2,2-4,1 kW). Bilan : 99 % requis (prudent) pour la nuit. Mon instantane de 16:56 (« batterie +19 A ») tombait dans un creux et l'avait masque.
- Cause principale (execution n8n 1464717, 16:38:30) : **`min_run_minutes` = 29 min en production** tenait la VW a 14 A alors que les conditions de maintien etaient perdues et le depassement souple durait depuis 20 min (tolerance de la bande 60-80 % : 6 min). Ensuite, la tolerance nuage (dette jusqu'a 1,2 kWh, plafond fixe).
- Correctifs (accord du proprietaire, deployes 12/10 23:43) :
1. `DECISION_ENGINEV2` : en solaire, un depassement souple qui a epuise sa tolerance arrete la charge MEME avant le temps mini (`stop_battery_pays_vehicle`). Les 29 min ne sont pas touches (ils servent aussi a Fix H, credibilite de « charged »).
2. `BATTERY_CLOUD_LOCKS` : dette nuage toleree = min(plafond, energie au-dessus du SOC requis PRUDENT du bilan) ; bilan perime -> plafond historique. Sortie `cloud.effectiveCloudDebtMaxKwh` (0 en reel au deploiement : SOC 39 pour 67).
- Golden 149/149 : 3 scenarios sur le snapshot REEL complet (`tests/golden/fixtures/vw-1210-1638.json`, entree de INPUT_PARSE) ; 3 mutations qui mordent ; acm 26/26, hysteresis 24/24.
- ⚠️ **Certificat HTTPS de n8n.airtria.pro expire le 12/10 a 15:52 UTC** (Let's Encrypt, non renouvele) : MCP n8n et `n8n-push` par defaut en echec. Deploiement fait par l'acces interne : `N8N_URL=http://10.10.10.17:5678` + `N8N_API_KEY` (lu dans opencode.json). Le n8n vivant est sur le .17 (`n8n_ha_proxy-n8n-1`) ; celui du .22 (`n8n_ha_a_revoir`) refuse la cle. Renouvellement a faire par le proprietaire (reverse proxy non documente ici).
- Kilometrage : niveaux deduits de l'autonomie marques `photo_autonomie:<km>/<ref>` (SPARK) ; `capacite_estimee.niveau_estime` ; VW 12/10 14 -> 55 % (estime), 3,60 kWh, **8,78 kWh**.
---
*Document mis a jour le : 10/10/2026 (~16:00) — conseiller relecteur de la journee (revue calculee + pistes) ; nuit : voiture qui profite d'EDF ferme pour la batterie, nuit qui prepare le soir les jours annonces faibles ; 08/10/2026 (~00:15) — jour faible : charge maximale en HC de midi (bilan 100 %, ACM plafond + solaire) ; 07/10/2026 (~22:45) — reprise de 16 h verifiee, EDF en HP a 18:03, bilan : reprise du soir et prevision recalee sur la matinee ; bilan HC de midi, reprise du soir apres coupure en pleine chauffe, mesure ECS sur 10 min ; recharge reseau HC : courant calcule (12 kVA) et consigne non tenue ; HC de midi : AUTO ne chauffe pas (BOOST), alerte v2, Jeep (contact, relecture, zone), recharge batterie bridee a 30 A par l'ACM ; jour faible = EDF les deux fenetres HC pour le chauffe-eau, alerte BOOST, Victron Mobile refait ; 1re decision jour faible en reel, mesure ECS reparee, douches 06:00/20:00, Grafana chauffe-eau, tableaux ECS ; chauffe-eau en HC de nuit les jours faibles (decision du bilan, workflow ECS sous controle du depot, VRM, V2.3 desactive) ; garde batterie reseau : voiture bridee a 6 A au lieu d'un arret ; garde-fou concierge 2 h ; allers-retours brefs de la borne retrouves dans l'historique HA (echange VW -> JEEP de 20 s), production 14,5 kWh contre 22 prevus, defaut garde batterie en mode reseau a trancher ; voiture a midi : la batterie n'est plus comptee comme du solaire (EDF automatique les jours de depart si le surplus ne couvre pas) ; nuit 05->06 : JEEP chargee sur EDF en complete (6,32 kWh), budget 0 du a la prevision, Windy/ECMWF contre VRM, a comparer ce soir ; concierge aligne sur la preuve fin de trajet ; a verifier : nuit 05->06 et mardi faible, comparaison VRM/Forecast.Solar ; JEEP identifiee aussi par sa fin de trajet, prevision de la nuit 05->06 ; identification JEEP reussie en reel, correction humaine qui prime (refus supprime), session du 04/10 coupee ; EVCS Master en quatre onglets ; Jeep lue par Uconnect et identifiee seule, capacite JEEP 10 kWh, concierge variante (a) ; releve par photo (SPARK), hass-uconnect en attente ; ecart semaine/week-end mesure (pas de profil week-end la nuit) ; nuit du budget : budget borne au plancher prudent, sans EDF = voiture bloquee meme EDF ferme (batterie seule) ; Energie : sauts corriges ; tableau Energie complete (Sud/Ouest, batterie par integration, borne ; aucun redemarrage HA) ; kilometrage des vehicules (km/kWh, capacite, releves de reference, agent cable 15/15) ; a verifier le 04/10 au matin : nuit du budget JEEP ; concierge : vehicule non confirme demande, heure de saisie ; Grafana vehicules en tableau ; Grafana vehicules : ZERO et Invite ajoutes, session 16:46 reetiquetee ZERO ; prevision SOC 10 h ~35 % ; concierge fiable : niveau note par le script, une ligne, dedoublonnage, simulation par drapeau ; agent : message incompris = question, bourse sur demande explicite (banc final 6/6) ; a verifier : nuit du samedi 03 au dimanche 04/10 (budget < besoin JEEP), cron orange-cloture lundi 05/10 apres 17:40, comparaison VRM / Forecast.Solar lundi 05/10*

View File

@@ -313,8 +313,28 @@ const effectiveCloudToleranceMinutes =
(solarPotentialContext ? batteryTune.global.solarPotentialGraceMinutes : 0) +
(houseSpikeDetected ? batteryTune.global.houseSpikeGraceMinutes : 0);
// TOLERANCE NUAGE BORNEE PAR LE BILAN (12/10/2026, accord du proprietaire).
// La dette nuage est l'energie que la batterie maison avance a la voiture en
// pariant sur le retour du soleil. Plafond fixe (1,2 kWh) = defaut de
// conception : le 12/10 a 16:16, SOC 76 % pour 99 % requis (profil prudent)
// pour passer la nuit, la VW a pris ~1,9 kWh a la batterie. La voiture est un
// delestage : elle n'avance que ce que la batterie a AU-DESSUS du SOC requis
// prudent du bilan (meme borne que le budget voiture de nuit, 04/10).
// Bilan perime ou illisible -> plafond historique inchange.
const cloudBalanceAgeMin =
params.balanceLastRunMs !== null && params.balanceLastRunMs !== undefined
? (nowMs - params.balanceLastRunMs) / 60000
: null;
const cloudBalanceUsable =
cloudBalanceAgeMin !== null && cloudBalanceAgeMin >= -5 && cloudBalanceAgeMin <= 30 &&
params.balanceStatusOk && Number.isFinite(params.balanceRequiredSocPrudentPct);
const cloudDebtBalanceCapKwh = cloudBalanceUsable
? Math.max(0, parsed.soc - params.balanceRequiredSocPrudentPct) / 100 * cfg.batteryUsableCapacityKwh
: Infinity;
const effectiveCloudDebtMaxKwh = Math.min(params.cloudDebtMaxKwh, cloudDebtBalanceCapKwh);
const cloudToleranceOk =
cloudDebtNextKwh <= params.cloudDebtMaxKwh &&
cloudDebtNextKwh <= effectiveCloudDebtMaxKwh &&
(
!cloudDebtActive ||
cloudDebtDurationMinutes <= effectiveCloudToleranceMinutes
@@ -1125,6 +1145,7 @@ return [{
cloudDebtStartedMs,
cloudDebtDurationMinutes,
effectiveCloudToleranceMinutes,
effectiveCloudDebtMaxKwh,
cloudToleranceOk,
},

View File

@@ -837,11 +837,23 @@ else if (cloud.sessionObservedActive) {
}
else if (!effectiveHoldConditionsOk) {
if (lifecycle.minRunElapsed) {
// 12/10/2026 : le temps mini de marche (29 min en production) protege
// contre le cliquetis, PAS contre la batterie. Le 12/10 de 16:16 a 16:44,
// la VW a ete tenue a 14 A sur une batterie a -40/-74 A, en depassement
// souple depuis 20 min (tolerance de la bande : 6 min). En solaire, un
// depassement souple qui a epuise sa tolerance arrete la charge meme
// avant le temps mini : la voiture est un delestage.
const batteryPaysVehicle =
prev.batteryPolicy?.batterySoftDischargeHoldOk === false &&
!manualLocalHoldOverride &&
(activeSourceMode === 'solar_only' || activeSourceMode === 'solar_potential');
if (lifecycle.minRunElapsed || batteryPaysVehicle) {
setOff({
stateName: 'stop_conditions_lost',
stateName: batteryPaysVehicle && !lifecycle.minRunElapsed ? 'stop_battery_pays_vehicle' : 'stop_conditions_lost',
decisionName: 'stop',
reasonText: 'Conditions de maintien perdues',
reasonText: batteryPaysVehicle && !lifecycle.minRunElapsed
? 'Arret avant le temps mini : la batterie maison paie la voiture'
: 'Conditions de maintien perdues',
dynamic: false,
mustStopFlag: true,
blockReason: 'hold_conditions_lost',

View File

@@ -0,0 +1,213 @@
{
"ac_loads_max_safe_w": "9000.0",
"acin1_last_changed_ts": 1791782643.84412,
"afternoon_battery_max_discharge_a": "unknown",
"afternoon_probe_pv_avg_min_w": "1000.0",
"afternoon_probe_soc_min": "85.0",
"allow_grid_departure_urgent": "off",
"allow_grid_hc_midday": "on",
"allow_grid_hc_night": "on",
"balance_ecs_nuit": "off",
"balance_energy_to_cover_kwh": "0.0",
"balance_grid_energy_needed_kwh": "0.0",
"balance_last_run": "12/10 16:35",
"balance_night_vehicle_budget_kwh": "0.0",
"balance_required_soc": "22.0",
"balance_required_soc_prudent": "99.0",
"balance_status": "ok",
"balance_vehicle_grid_needed": "off",
"base_load_remaining_kwh": "2.2",
"batt_charge_reserve_25_40_a": "110.0",
"batt_charge_reserve_40_60_a": "70.0",
"batt_charge_reserve_60_80_a": "40.0",
"batt_charge_reserve_80_90_a": "10.0",
"batt_charge_reserve_gt90_a": "5.0",
"batt_charge_reserve_lt25_a": "150.0",
"batt_discharge_hard_25_40_a": "-15.0",
"batt_discharge_hard_40_60_a": "-30.0",
"batt_discharge_hard_60_80_a": "-50.0",
"batt_discharge_hard_80_90_a": "-80.0",
"batt_discharge_hard_gt90_a": "-100.0",
"batt_discharge_hard_lt25_a": "-8.0",
"batt_discharge_soft_25_40_a": "-5.0",
"batt_discharge_soft_40_60_a": "-15.0",
"batt_discharge_soft_60_80_a": "-20.0",
"batt_discharge_soft_80_90_a": "-40.0",
"batt_discharge_soft_gt90_a": "-50.0",
"batt_discharge_soft_lt25_a": "-3.0",
"batt_hard_tol_25_40_sec": "5.0",
"batt_hard_tol_40_60_sec": "10.0",
"batt_hard_tol_60_80_sec": "15.0",
"batt_hard_tol_80_90_sec": "20.0",
"batt_hard_tol_gt90_sec": "20.0",
"batt_hard_tol_lt25_sec": "2.0",
"batt_recovery_25_40_min": "12.0",
"batt_recovery_40_60_min": "10.0",
"batt_recovery_60_80_min": "8.0",
"batt_recovery_80_90_min": "6.0",
"batt_recovery_gt90_min": "6.0",
"batt_recovery_lt25_min": "15.0",
"batt_soft_tol_25_40_min": "2.0",
"batt_soft_tol_40_60_min": "4.0",
"batt_soft_tol_60_80_min": "6.0",
"batt_soft_tol_80_90_min": "10.0",
"batt_soft_tol_gt90_min": "12.0",
"batt_soft_tol_lt25_min": "1.0",
"battery_current_a": "-73.9000015258789",
"battery_discharge_hard_a": "-25.0",
"battery_discharge_soft_a": "0.0",
"battery_full_forecast_min_kwh": "4.5",
"battery_full_soc": "90.0",
"battery_neutral_band_a": "0.0",
"battery_reserve_up_a": "8.0",
"battery_soft_breach_at": "2026-10-12 16:18:30",
"blocked_until": "2026-10-12 16:38:00",
"bms_max_charge_current_a": "250.0",
"bms_max_discharge_current_a": "250.0",
"budget_remaining_kwh": "-9.55",
"charge_permission_now": "on",
"cloud_debt_kwh": "0.99",
"cloud_debt_max_kwh": "1.2",
"cloud_debt_started_at": "2026-10-12 16:21:00",
"cloud_tolerance_minutes": "20.0",
"control_latch": "on",
"controller_enabled": "on",
"current_change_cooldown_seconds": "30.0",
"current_max_auto_a": "22.0",
"current_min_a": "6.0",
"current_step_down_a": "2.0",
"current_step_up_a": "1.0",
"day_start_time": "08:30:00",
"day_stop_time": "20:15:00",
"departure_day_ok": "on",
"departure_priority_enabled": "on",
"departure_time": "08:50:00",
"departure_time_jeep": "06:30:00",
"departure_time_zero": "08:50:00",
"dvcc_max_charge_current_a": "250",
"end_of_day_soc_guard": "85.0",
"end_of_day_soc_target": "85.0",
"evcs_ac_power_w": "3363.0",
"evcs_auto_start_switch": "off",
"evcs_charging_switch": "on",
"evcs_charging_time": "51m 46s",
"evcs_command_current_a": "14",
"evcs_current_a": "14.0",
"evcs_delivery_noise_ema": "0.003",
"evcs_delivery_ratio_ema": "0.999",
"evcs_device_connected": "on",
"evcs_mode_fallback": "unknown",
"evcs_mode_sensor": "Manuel",
"evcs_night_session_start_at": "",
"evcs_night_session_start_soc": "",
"evcs_no_demand_filter_eval_at": "2026-10-12 16:38:00",
"evcs_no_demand_score_ema": "0.001",
"evcs_no_demand_semantic_active": "off",
"evcs_no_demand_since_at": "2026-08-23 15:29:30",
"evcs_priority_manual_mode": "normal",
"evcs_priority_manual_override": "off",
"evcs_priority_manual_reason": "unknown",
"evcs_priority_manual_until": "2026-04-14 00:00:00",
"evcs_session_energy_kwh": "2.7",
"evcs_set_current_a": "14",
"evcs_status_raw": "2",
"evcs_total_energy_kwh": "4123.2",
"evcs_vehicle": "VW",
"evcs_vehicle_connected": "on",
"evcs_vehicle_connected_ts": 1791806899.563081,
"evening_battery_max_discharge_a": "-30.0",
"evening_soc_start_min": "85.0",
"evening_soc_stop_min": "80.0",
"evening_start_time": "17:00:00",
"evening_stop_time": "20:30:00",
"flexible_safety_margin_kwh": "0.9",
"force_mode_auto_revert_minutes": "120.0",
"forecast_commit_min_kwh": "10.0",
"forecast_commit_soc_floor": "35.0",
"forecast_mode_enabled": "off",
"grid_battery_hard_a": "-20.0",
"grid_battery_soft_a": "-10.0",
"grid_current_max_a": "20.0",
"grid_power_w": "0",
"grid_setpoint_idle_w": "unknown",
"grid_setpoint_w": "700",
"grid_settle_seconds": "unknown",
"grid_state": "off",
"grid_switch_last_changed_ts": 1791782643.740837,
"hard_stop_enabled": "on",
"hc_midday_start_time": "12:24:00",
"hc_midday_stop_time": "14:24:00",
"hc_night_start_time": "01:24:00",
"hc_night_stop_time": "07:24:00",
"house_ac_headroom_w": "5000.0",
"house_ac_power_prev_w": "5446.0",
"house_ac_power_w": "5446.0",
"house_spike_grace_minutes": "10.0",
"last_block_reason": "none",
"last_current_change_at": "2026-10-12 16:23:30",
"last_signature": "hold_min_run|profile=outside_window|source=solar_only|chg=1|perm=1|l=1|cur=14|block=none|stop=0|prio=0|night=0|ntrg=22|nurg=0|vdf=0|vdfs=0.001",
"last_start_at": "2026-10-12 16:15:30",
"last_stop_at": "2026-10-12 14:37:30",
"midday_battery_max_discharge_a": "-40.0",
"midday_probe_pv_avg_min_w": "0.0",
"midday_soc_min": "75.0",
"min_run_minutes": "29.0",
"min_stop_minutes": "0.0",
"modules_en_service": "13.0",
"morning_battery_max_discharge_a": "-20.0",
"morning_probe_pv_avg_min_w": "1500.0",
"morning_probe_pv_trend_min_w": "-2000.0",
"morning_soc_min": "35.0",
"morning_start_time": "07:30:00",
"morning_stop_time": "11:00:00",
"mppt_operation_modes": "mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active",
"night_battery_charge_enabled": "on",
"night_battery_done_prev": "off",
"night_budget_prev": "",
"night_charge_mode": "sans_edf",
"night_grid_session_committed_prev": "off",
"night_takeover_prev": "off",
"notifications_enabled": "on",
"operating_mode_helper": "outside_window",
"overload_hold_seconds": "30.0",
"overload_step_down_a": "2.0",
"overload_stop_after_seconds": "120.0",
"prev_surplus_state": "hold_min_run",
"probe_current_a": "6.0",
"probe_enabled": "on",
"probe_minutes": "3.0",
"probe_retry_minutes": "45.0",
"probe_started_at": "2026-03-31 00:00:00",
"projected_end_soc_after_charge": "53.2",
"pv_hold_min_w": "500.0",
"pv_power_10m_avg_w": "2733",
"pv_power_w": "2170.201974890708",
"pv_start_min_w": "700.0",
"pv_trend_w": "unknown",
"required_energy_kwh": "10.5",
"reset_borne": "off",
"soc_comfort_target": "85.0",
"soc_percent": "76.0",
"soc_start_min": "80.0",
"soc_stop_min": "29.0",
"solar_potential_grace_minutes": "12.0",
"step_down_cooldown_seconds": "60.0",
"step_up_cooldown_seconds": "30.0",
"target_current_auto_a": "14.0",
"today_remaining_kwh": "6.748",
"tomorrow_kwh": "36.166",
"vebus_ac_out_l1_current_a": "24.540000915527344",
"vebus_ac_out_l1_power_w": "5448",
"vebus_ac_out_l1_voltage_v": "229.75999450683594",
"vebus_dc_current_a": "-119.5",
"vebus_dc_power_w": "-5443.0",
"vebus_dc_voltage_v": "49.41999816894531",
"vebus_l1_low_battery": "off",
"vebus_l1_overload": "off",
"vebus_low_battery": "off",
"vebus_main_state": "4",
"vebus_overload": "off",
"vebus_state": "9",
"victron_acin1_available": "0",
"victron_evcs_strategy": "auto_solar_plus"
}

View File

@@ -1,3 +1,4 @@
import { readFileSync } from 'node:fs';
// 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
@@ -52,6 +53,10 @@ const VEHICLE_NIGHT = {
};
const BUDGET_NUIT = { ...VEHICLE_NIGHT, ...BUDGET_NUIT_BASE };
// 12/10/2026 16:38:30 — releve reel COMPLET (entree de INPUT_PARSE, execution
// n8n 1464717) : VW en charge solaire tenue sur la batterie.
const VW_1210 = JSON.parse(readFileSync(new URL('./fixtures/vw-1210-1638.json', import.meta.url), 'utf8'));
export const scenarios = [
// ================= NUIT HC (P7 + historique) =================
@@ -3558,4 +3563,42 @@ export const scenarios = [
blockReason: 'grid_unavailable',
},
},
// 12/10/2026 — la VW payee par la batterie maison. Releve reel de 16:38:30
// (execution n8n 1464717) : charge solaire redemarree a 16:15:30, maison
// 5,4 kW pour 2,2 kW de PV, batterie a -73,9 A, conditions de maintien
// perdues, depassement souple depuis 16:18:30 (tolerance de la bande 60-80 % :
// 6 min). Le temps mini de marche (29 min en production) tenait quand meme
// la voiture a 14 A : ~1,9 kWh pris a la batterie, bilan a 99 % requis.
{
id: 'vw-1210-temps-mini-ne-fait-pas-payer-la-batterie',
label: 'RELEVE 12/10 16:38 — batterie a -74 A depuis 20 min, temps mini non ecoule : la voiture S ARRETE',
time: '2026-10-12T16:38:30+02:00',
overrides: { ...VW_1210 },
expect: { desiredCharging: false, state: 'stop_battery_pays_vehicle' },
},
{
id: 'vw-1210-tolerance-nuage-bornee-par-le-bilan',
label: '12/10 — temps mini ecoule, dette 0,5 kWh, SOC 76 pour 99 % requis : AUCUNE tolerance nuage',
time: '2026-10-12T16:38:30+02:00',
overrides: {
...VW_1210, last_start_at: '2026-10-12 15:30:00', battery_soft_breach_at: '2026-10-12 16:37:30',
battery_current_a: '-20', cloud_debt_kwh: '0.5', cloud_debt_started_at: '2026-10-12 16:36:00',
prev_surplus_state: 'charging_hold_cloud_tolerance',
},
// Ancien code : hausse du courant (15 A) sur une batterie qui debite. Desormais la
// dette nuage n'est plus toleree : reduction, puis arret par la garde souple.
expect: { state: 'charging_step_down_cloud_debt', currentA: 12 },
},
{
id: 'vw-1210-tolerance-nuage-intacte-batterie-au-dessus-du-besoin',
label: 'temoin — meme passage nuageux, SOC 98 pour 70 % requis : la tolerance nuage tient la charge',
time: '2026-10-12T16:38:30+02:00',
overrides: {
...VW_1210, last_start_at: '2026-10-12 15:30:00', battery_soft_breach_at: '2026-10-12 16:37:30',
battery_current_a: '-20', cloud_debt_kwh: '0.5', cloud_debt_started_at: '2026-10-12 16:36:00',
prev_surplus_state: 'charging_hold_cloud_tolerance',
soc_percent: '98', balance_required_soc_prudent: '70.0',
},
expect: { state: 'charging_step_up_battery', desiredCharging: true },
},
];

File diff suppressed because one or more lines are too long