P11: reset du collant 'plein' au changement de véhicule (charging_time)
- incident 12/08: JEEP chargée -> débranchée -> VW branchée bloquée par le collant night_recovery_done_idle de la JEEP - snapshot +evcs_charging_time, INPUT_PARSE parseChargingTimeToSec - WINDOWS: sessionFresh (<=90s) neutralise vehicleChargeCompleted/ dayVehicleFullIdle/departureChargeDone/evcsStatusCharged - golden 36->39, validé live 05:00:30 (VW débloquée: recovery_done -> wait_grid_takeover) - HANDOFF §23 + MANUEL reset collant
This commit is contained in:
45
HANDOFF.md
45
HANDOFF.md
@@ -1066,3 +1066,48 @@ branché → tentatives légitimes (nuit P7 / jour solaire / P9 départ)
|
|||||||
|
|
||||||
---
|
---
|
||||||
*Document mis à jour le : 09/08/2026 (~10:10) — P10 déployé et validé live, golden 36/36*
|
*Document mis à jour le : 09/08/2026 (~10:10) — P10 déployé et validé live, golden 36/36*
|
||||||
|
|
||||||
|
## 23. Session du 12/08/2026 — P11 : reset du collant au changement de véhicule
|
||||||
|
|
||||||
|
### Incident (nuit 12/08, captures utilisateur)
|
||||||
|
JEEP branchée 01:24, chargée en HC (cible 35% ADVISOR), fin de charge franche
|
||||||
|
→ état collant `night_recovery_done_idle`. Utilisateur débranche la JEEP et
|
||||||
|
branche la **VW** à sa place. L'état collant a SURVÉCU (le collant n'était
|
||||||
|
remis à zéro que par `vehicle_not_connected`, non vu de façon franche lors du
|
||||||
|
swap) → la VW était bloquée « charge véhicule terminée » alors que c'était
|
||||||
|
celle de la JEEP. La VW (déchargée) ne chargeait pas.
|
||||||
|
|
||||||
|
### Cause de conception
|
||||||
|
Tous les états collants "plein" (P8 night_*, P9 departure_*, P10 day_*) se
|
||||||
|
libéraient UNIQUEMENT au débranchement (vehicle_not_connected). Un remplacement
|
||||||
|
de véhicule rapide (ou un statut EVCS qui ne repasse pas franchement
|
||||||
|
"déconnecté") laissait le collant en place → nouveau véhicule bloqué.
|
||||||
|
|
||||||
|
### Fix P11 (déployé 12/08 05:00, golden 39/39, validé live à 05:00:30)
|
||||||
|
Signal fiable : `sensor.victron_evcs_charging_time` repart de 0 à CHAQUE
|
||||||
|
nouvelle connexion physique.
|
||||||
|
- Snapshot : +`evcs_charging_time` ; INPUT_PARSE : `parseChargingTimeToSec`
|
||||||
|
("1h 49m 53s"/"8m 27s"/"0s" → secondes), `parsed.evcsChargingTimeSec`.
|
||||||
|
- WINDOWS : `sessionFresh` = véhicule branché ET charging_time ≤ 90 s.
|
||||||
|
Neutralise `vehicleChargeCompleted`, `dayVehicleFullIdle`,
|
||||||
|
`departureChargeDone` ET `evcsStatusCharged` (statut 3 résiduel ignoré le
|
||||||
|
temps que la borne renégocie) → le système RETESTE le véhicule neuf.
|
||||||
|
- Export `sessionFresh` (traçabilité).
|
||||||
|
|
||||||
|
### Golden 36 → 39
|
||||||
|
p11-changement-vehicule-nuit (prev=night_recovery_done_idle + charging_time 15s
|
||||||
|
→ night_wait_grid_takeover, retest) · p11-changement-vehicule-jour
|
||||||
|
(day_vehicle_full_idle + 20s → solaire repart) · p11-session-etablie-reste-collant
|
||||||
|
(charging_time 2h05 → reste collant, pas de reset abusif).
|
||||||
|
|
||||||
|
### Validation live
|
||||||
|
05:00:00 PUT → 05:00:30 : la VW est passée de `night_recovery_done_idle`
|
||||||
|
(bloqué) à `night_wait_grid_takeover` / source « Réseau HC autorisé ».
|
||||||
|
|
||||||
|
### Récapitulatif des remises à zéro du collant "plein"
|
||||||
|
1. DÉBRANCHEMENT (vehicle_not_connected) ;
|
||||||
|
2. CHANGEMENT DE VÉHICULE / nouvelle connexion (charging_time ≤ 90 s, P11) ;
|
||||||
|
3. force_charge_now / force_grid_now (échappatoires manuelles).
|
||||||
|
|
||||||
|
---
|
||||||
|
*Document mis à jour le : 12/08/2026 (~05:05) — P11 déployé et validé live, golden 39/39*
|
||||||
|
|||||||
@@ -460,6 +460,11 @@ de G2 — full_or_paused / not_requesting_power / charge_complete / no_demand) :
|
|||||||
tentative (même solaire) jusqu'au débranchement ; héritages bidirectionnels
|
tentative (même solaire) jusqu'au débranchement ; héritages bidirectionnels
|
||||||
jour↔nuit ; échappatoires force_charge_now / force_grid_now
|
jour↔nuit ; échappatoires force_charge_now / force_grid_now
|
||||||
|
|
||||||
|
**Reset du collant "plein" (P11, 12/08/2026)** : au CHANGEMENT DE VÉHICULE,
|
||||||
|
`sensor.victron_evcs_charging_time` repart de 0 → `sessionFresh` (≤ 90 s)
|
||||||
|
neutralise TOUS les collants (night/departure/day) → le nouveau véhicule est
|
||||||
|
retesté. Remises à zéro : débranchement, session fraîche (P11), ou force_*.
|
||||||
|
|
||||||
#### Incidents de référence
|
#### Incidents de référence
|
||||||
- **Nuit 07-08/08 (JEEP)** : SOC bloqué pile à la cible = point dégénéré →
|
- **Nuit 07-08/08 (JEEP)** : SOC bloqué pile à la cible = point dégénéré →
|
||||||
rebond EDF ~10 min + flapping ACM ; EDF maintenu 25 min après débranchement.
|
rebond EDF ~10 min + flapping ACM ; EDF maintenu 25 min après débranchement.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"evcs_command_current_a": states("number.victron_evcs_set_current_command"),
|
"evcs_command_current_a": states("number.victron_evcs_set_current_command"),
|
||||||
"evcs_current_a": states("sensor.victron_evcs_current"),
|
"evcs_current_a": states("sensor.victron_evcs_current"),
|
||||||
"evcs_ac_power_w": states("sensor.victron_evcs_ac_power"),
|
"evcs_ac_power_w": states("sensor.victron_evcs_ac_power"),
|
||||||
|
"evcs_charging_time": states("sensor.victron_evcs_charging_time"),
|
||||||
"evcs_charging_switch": states("switch.victron_evcs_charging"),
|
"evcs_charging_switch": states("switch.victron_evcs_charging"),
|
||||||
"evcs_auto_start_switch": states("switch.victron_evcs_auto_start"),
|
"evcs_auto_start_switch": states("switch.victron_evcs_auto_start"),
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,20 @@ function cleanText(v, fallback = '') {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// P11 (12/08/2026) : durée de charge EVCS "1h 49m 53s" / "8m 27s" / "0s"
|
||||||
|
// → secondes. Repart de 0 à chaque NOUVELLE connexion physique d'un véhicule
|
||||||
|
// (débranchement JEEP → branchement VW) → signal de reset du collant "plein".
|
||||||
|
function parseChargingTimeToSec(s) {
|
||||||
|
const t = (s ?? '').toString().toLowerCase();
|
||||||
|
if (!t) return null;
|
||||||
|
let sec = 0;
|
||||||
|
let matched = false;
|
||||||
|
const h = t.match(/(\d+)\s*h/); if (h) { sec += Number(h[1]) * 3600; matched = true; }
|
||||||
|
const m = t.match(/(\d+)\s*m/); if (m) { sec += Number(m[1]) * 60; matched = true; }
|
||||||
|
const s2 = t.match(/(\d+)\s*s/); if (s2){ sec += Number(s2[1]); matched = true; }
|
||||||
|
return matched ? sec : null;
|
||||||
|
}
|
||||||
|
|
||||||
// Advisor IA : input_text.evcs_advisor_last_run au format "DD/MM HH:mm"
|
// Advisor IA : input_text.evcs_advisor_last_run au format "DD/MM HH:mm"
|
||||||
// (pas d'année). On suppose l'année courante ; si le résultat est dans le
|
// (pas d'année). On suppose l'année courante ; si le résultat est dans le
|
||||||
// futur (> 1 jour), c'est un passage d'année → on retire un an.
|
// futur (> 1 jour), c'est un passage d'année → on retire un an.
|
||||||
@@ -257,6 +271,7 @@ const parsed = {
|
|||||||
evcsCurrentRealA: Math.max(0, num(input.evcs_current_a, 0)),
|
evcsCurrentRealA: Math.max(0, num(input.evcs_current_a, 0)),
|
||||||
|
|
||||||
evcsAcPowerW: Math.max(0, num(input.evcs_ac_power_w, 0)),
|
evcsAcPowerW: Math.max(0, num(input.evcs_ac_power_w, 0)),
|
||||||
|
evcsChargingTimeSec: parseChargingTimeToSec(input.evcs_charging_time),
|
||||||
evcsChargingSwitchOn: low(input.evcs_charging_switch || 'off') === 'on',
|
evcsChargingSwitchOn: low(input.evcs_charging_switch || 'off') === 'on',
|
||||||
evcsAutoStartSwitchOn: low(input.evcs_auto_start_switch || 'off') === 'on',
|
evcsAutoStartSwitchOn: low(input.evcs_auto_start_switch || 'off') === 'on',
|
||||||
|
|
||||||
|
|||||||
@@ -345,6 +345,19 @@ const nightTakeoverPrev =
|
|||||||
// débranchement (state vehicle_not_connected) ou à la fin de la fenêtre nuit.
|
// débranchement (state vehicle_not_connected) ou à la fin de la fenêtre nuit.
|
||||||
const prevSurplusState = (input.prev_surplus_state ?? '').toString().trim().toLowerCase();
|
const prevSurplusState = (input.prev_surplus_state ?? '').toString().trim().toLowerCase();
|
||||||
|
|
||||||
|
// P11 (12/08/2026) : RESET du collant "véhicule plein" au CHANGEMENT DE
|
||||||
|
// VÉHICULE. Incident 12/08 : JEEP chargée → night_recovery_done_idle collant ;
|
||||||
|
// débranchement JEEP + branchement VW → l'état collant a survécu et bloquait
|
||||||
|
// la VW (« charge terminée » = celle de la JEEP, pas de la VW).
|
||||||
|
// Signal fiable : sensor.victron_evcs_charging_time repart de 0 à chaque
|
||||||
|
// nouvelle connexion physique. Sous ~90 s → session neuve → on neutralise le
|
||||||
|
// collant pour laisser le système RETESTER le véhicule fraîchement branché.
|
||||||
|
const P11_FRESH_SESSION_MAX_SEC = 90;
|
||||||
|
const sessionFresh =
|
||||||
|
parsed.evcsVehicleConnected &&
|
||||||
|
Number.isFinite(parsed.evcsChargingTimeSec) &&
|
||||||
|
parsed.evcsChargingTimeSec <= P11_FRESH_SESSION_MAX_SEC;
|
||||||
|
|
||||||
// Fix D (08/08/2026) : le statut natif EVCS "charged" (3) vaut aussi fin de
|
// Fix D (08/08/2026) : le statut natif EVCS "charged" (3) vaut aussi fin de
|
||||||
// charge, sans attendre le VehicleDemandFilter (désarmé si la session a été
|
// charge, sans attendre le VehicleDemandFilter (désarmé si la session a été
|
||||||
// stoppée par une garde, ex battery_current_hard_stop → détection tardive
|
// stoppée par une garde, ex battery_current_hard_stop → détection tardive
|
||||||
@@ -353,12 +366,16 @@ const prevSurplusState = (input.prev_surplus_state ?? '').toString().trim().toLo
|
|||||||
// sur un glitch de statut pendant une vraie charge. NB : le statut retombe
|
// sur un glitch de statut pendant une vraie charge. NB : le statut retombe
|
||||||
// (3→1/6) dès l'ouverture du relais → la persistance est assurée par l'état
|
// (3→1/6) dès l'ouverture du relais → la persistance est assurée par l'état
|
||||||
// collant écrit par DECISION pendant la session (voir Fix E DECISION).
|
// collant écrit par DECISION pendant la session (voir Fix E DECISION).
|
||||||
|
// P11 : sur une session neuve, on ignore aussi un éventuel statut 3 résiduel
|
||||||
|
// (le temps que la borne renégocie avec le nouveau véhicule).
|
||||||
const evcsStatusCharged =
|
const evcsStatusCharged =
|
||||||
|
!sessionFresh &&
|
||||||
Number.parseFloat(parsed.evcsStatusRaw) === 3 &&
|
Number.parseFloat(parsed.evcsStatusRaw) === 3 &&
|
||||||
Math.max(0, parsed.evcsAcPowerW || 0) <= 300;
|
Math.max(0, parsed.evcsAcPowerW || 0) <= 300;
|
||||||
|
|
||||||
const vehicleChargeCompleted =
|
const vehicleChargeCompleted =
|
||||||
nightSessionActive &&
|
nightSessionActive &&
|
||||||
|
!sessionFresh &&
|
||||||
(
|
(
|
||||||
prevSurplusState === 'vehicle_full_or_paused' ||
|
prevSurplusState === 'vehicle_full_or_paused' ||
|
||||||
prevSurplusState === 'night_session_done_idle' ||
|
prevSurplusState === 'night_session_done_idle' ||
|
||||||
@@ -415,6 +432,7 @@ const departureDonePrev =
|
|||||||
|
|
||||||
const departureChargeDone =
|
const departureChargeDone =
|
||||||
departureWindowActive &&
|
departureWindowActive &&
|
||||||
|
!sessionFresh &&
|
||||||
(departureDonePrev || evcsStatusCharged);
|
(departureDonePrev || evcsStatusCharged);
|
||||||
|
|
||||||
const departureUrgentActive =
|
const departureUrgentActive =
|
||||||
@@ -446,6 +464,7 @@ const dayVehicleFullPrev =
|
|||||||
|
|
||||||
const dayVehicleFullIdle =
|
const dayVehicleFullIdle =
|
||||||
parsed.evcsVehicleConnected &&
|
parsed.evcsVehicleConnected &&
|
||||||
|
!sessionFresh &&
|
||||||
!inNightHcWindow &&
|
!inNightHcWindow &&
|
||||||
!isStopMode &&
|
!isStopMode &&
|
||||||
!forceGridRequested &&
|
!forceGridRequested &&
|
||||||
@@ -851,6 +870,9 @@ return [{
|
|||||||
|
|
||||||
// P10
|
// P10
|
||||||
dayVehicleFullIdle,
|
dayVehicleFullIdle,
|
||||||
|
|
||||||
|
// P11
|
||||||
|
sessionFresh,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -391,6 +391,82 @@ export const scenarios = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ===== 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) =================
|
// ================= FIN DE CHARGE NUIT (P8) =================
|
||||||
{
|
{
|
||||||
id: 'night-vw-pleine-transition',
|
id: 'night-vw-pleine-transition',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user