feat: Add afternoon_probe for outside-window solar charging

- Add afternoonProbeEligible in WINDOW_SOURCEV2.js
- Enable hybrid_probe sourceMode in outside_window when probe conditions met
- Add afternoon probe parameters in INPUTPARSE (inputparse.js + inputparseP2.js)
- Update probeStartOk in BATTERY_CLOUD_LOCKS to accept afternoon probe
- Update blockStillRelevant for probe_failed to include outside_window

Fixes the issue where strong afternoon sun with SOC>85%
was blocked by high cloudDebt and no probe mechanism.
This commit is contained in:
Tallagrand Thierry
2026-06-12 02:45:24 +02:00
parent 0d91819577
commit af87a76a65
5 changed files with 23 additions and 3 deletions

View File

@@ -594,7 +594,7 @@ const probeModeActive = context.sourceMode === 'hybrid_probe';
const probeStartOk = const probeStartOk =
probeModeActive && probeModeActive &&
commonSafetyStartOk && commonSafetyStartOk &&
(context.morningProbeEligible || context.middayProbeEligible); (context.morningProbeEligible || context.middayProbeEligible || context.afternoonProbeEligible);
const probeHoldGuardOk = const probeHoldGuardOk =
probeModeActive && probeModeActive &&

View File

@@ -329,6 +329,13 @@ const middayProbeEligible =
parsed.soc >= params.middaySocMin && parsed.soc >= params.middaySocMin &&
parsed.batteryCurrentA >= params.middayBatteryMaxDischargeA; parsed.batteryCurrentA >= params.middayBatteryMaxDischargeA;
const afternoonProbeEligible =
profile === 'outside_window' &&
params.probeEnabled &&
parsed.soc >= params.afternoonProbeSocMin &&
parsed.batteryCurrentA >= params.afternoonBatteryMaxDischargeA &&
parsed.pvPowerAvg10mW >= params.afternoonProbePvAvgMinW;
const cloudDebtOkForPotential = params.cloudDebtPrevKwh <= params.cloudDebtMaxKwh; const cloudDebtOkForPotential = params.cloudDebtPrevKwh <= params.cloudDebtMaxKwh;
const batteryOkForPotential = parsed.batteryCurrentA >= -Math.max(1, params.batteryNeutralBandA); const batteryOkForPotential = parsed.batteryCurrentA >= -Math.max(1, params.batteryNeutralBandA);
@@ -460,6 +467,10 @@ else if (profile === 'outside_window') {
sourceMode = 'solar_potential'; sourceMode = 'solar_potential';
} }
else if (afternoonProbeEligible) {
sourceMode = 'hybrid_probe';
}
else { else {
// IMPORTANT: // IMPORTANT:
// hors fenêtre = pas de réseau programmé. // hors fenêtre = pas de réseau programmé.
@@ -537,6 +548,7 @@ return [{
manualChargeLocalRequested, manualChargeLocalRequested,
morningProbeEligible, morningProbeEligible,
middayProbeEligible, middayProbeEligible,
afternoonProbeEligible,
isHighBatteryForPotential, isHighBatteryForPotential,
strongSolarSignal, strongSolarSignal,
effectivePvStartMinW, effectivePvStartMinW,

View File

@@ -10,7 +10,7 @@ const nextAllowedWindowStartMs =
const blockActive = blockedUntilMs !== null && blockedUntilMs > nowMs + 1000; const blockActive = blockedUntilMs !== null && blockedUntilMs > nowMs + 1000;
const blockStillRelevant = const blockStillRelevant =
lastBlockReason === 'outside_window' ? profile === 'outside_window' : lastBlockReason === 'outside_window' ? profile === 'outside_window' :
lastBlockReason === 'probe_failed' ? (profile === 'morning_solar' || profile === 'midday_hc_or_solar') : lastBlockReason === 'probe_failed' ? (profile === 'morning_solar' || profile === 'midday_hc_or_solar' || profile === 'outside_window') :
lastBlockReason === 'battery_protection_stop' ? ( lastBlockReason === 'battery_protection_stop' ? (
batteryCurrentA < -batteryNeutralBandA || anyVebusLowBattery || vebusDcVoltageSoftLow batteryCurrentA < -batteryNeutralBandA || anyVebusLowBattery || vebusDcVoltageSoftLow
) : ) :
@@ -133,7 +133,7 @@ const gridHoldOk =
const probeStartOk = const probeStartOk =
sourceMode === 'hybrid_probe' && sourceMode === 'hybrid_probe' &&
commonSafetyStartOk && commonSafetyStartOk &&
(morningProbeEligible || middayProbeEligible); (morningProbeEligible || middayProbeEligible || afternoonProbeEligible);
const probeHoldGuardOk = const probeHoldGuardOk =
commonSafetyHoldOk && commonSafetyHoldOk &&
batteryCurrentA >= profileBatteryDischargeFloorA; batteryCurrentA >= profileBatteryDischargeFloorA;

View File

@@ -346,6 +346,10 @@ const params = {
morningProbePvTrendMinW: num(input.morning_probe_pv_trend_min_w, 0), morningProbePvTrendMinW: num(input.morning_probe_pv_trend_min_w, 0),
middayProbePvAvgMinW: Math.max(0, num(input.midday_probe_pv_avg_min_w, 1200)), middayProbePvAvgMinW: Math.max(0, num(input.midday_probe_pv_avg_min_w, 1200)),
afternoonProbePvAvgMinW: Math.max(0, num(input.afternoon_probe_pv_avg_min_w, 1000)),
afternoonProbeSocMin: clamp(num(input.afternoon_probe_soc_min, 85), 0, 100),
afternoonBatteryMaxDischargeA: num(input.afternoon_battery_max_discharge_a, -20),
morningSocMin: clamp(num(input.morning_soc_min, 35), 0, 100), morningSocMin: clamp(num(input.morning_soc_min, 35), 0, 100),
middaySocMin: clamp(num(input.midday_soc_min, 75), 0, 100), middaySocMin: clamp(num(input.midday_soc_min, 75), 0, 100),
eveningSocStartMin: clamp(num(input.evening_soc_start_min, 85), 0, 100), eveningSocStartMin: clamp(num(input.evening_soc_start_min, 85), 0, 100),

View File

@@ -85,6 +85,10 @@ const params = {
morningProbePvTrendMinW: num(input.morning_probe_pv_trend_min_w, 0), morningProbePvTrendMinW: num(input.morning_probe_pv_trend_min_w, 0),
middayProbePvAvgMinW: Math.max(0, num(input.midday_probe_pv_avg_min_w, 1200)), middayProbePvAvgMinW: Math.max(0, num(input.midday_probe_pv_avg_min_w, 1200)),
afternoonProbePvAvgMinW: Math.max(0, num(input.afternoon_probe_pv_avg_min_w, 1000)),
afternoonProbeSocMin: clamp(num(input.afternoon_probe_soc_min, 85), 0, 100),
afternoonBatteryMaxDischargeA: num(input.afternoon_battery_max_discharge_a, -20),
morningSocMin: clamp(num(input.morning_soc_min, 35), 0, 100), morningSocMin: clamp(num(input.morning_soc_min, 35), 0, 100),
middaySocMin: clamp(num(input.midday_soc_min, 75), 0, 100), middaySocMin: clamp(num(input.midday_soc_min, 75), 0, 100),
eveningSocStartMin: clamp(num(input.evening_soc_start_min, 85), 0, 100), eveningSocStartMin: clamp(num(input.evening_soc_start_min, 85), 0, 100),