ajout gestion batterie

This commit is contained in:
2026-05-09 20:57:55 +02:00
parent 1776d2776f
commit ae725602d0
5 changed files with 2696 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
// =====================================================
// CLOUD DEBT
// =====================================================
const batteryNetPowerW = parsed.batteryCurrentA * derived.batteryVoltageRef;
let cloudDebtNextKwh = params.cloudDebtPrevKwh;
if (sessionObservedActive) {
if (batteryNetPowerW < 0 && !isGridSource) {
cloudDebtNextKwh += (Math.abs(batteryNetPowerW) * cfg.sampleSeconds) / 3600000;
} else if (batteryNetPowerW > 0) {
cloudDebtNextKwh -= (batteryNetPowerW * cfg.sampleSeconds) / 3600000;
}
} else if (batteryNetPowerW > 0) {
cloudDebtNextKwh -= (batteryNetPowerW * cfg.sampleSeconds) / 3600000;
}
cloudDebtNextKwh = clamp(cloudDebtNextKwh, 0, params.cloudDebtMaxKwh * 4);
const cloudDebtMeaningful =
cloudDebtNextKwh > cfg.cloudDebtMeaningfulThresholdKwh;
const cloudEventDischargingNow =
sessionObservedActive &&
!isGridSource &&
parsed.batteryCurrentA < -Math.max(1, params.batteryNeutralBandA) &&
cloudDebtMeaningful;
const continuingCloudEvent =
cloudEventDischargingNow &&
history.cloudDebtStartedMsPrev !== null &&
params.cloudDebtPrevKwh > cfg.cloudDebtRecoveryThresholdKwh;
const enteringCloudDebt =
cloudEventDischargingNow && !continuingCloudEvent;
const cloudDebtActive = cloudEventDischargingNow;
const cloudDebtStartedMs =
cloudEventDischargingNow
? (continuingCloudEvent ? history.cloudDebtStartedMsPrev : nowMs)
: null;
const cloudDebtDurationMinutes =
cloudDebtActive && cloudDebtStartedMs !== null
? Math.max(0, (nowMs - cloudDebtStartedMs) / 60000)
: 0;
const effectiveCloudToleranceMinutes =
params.cloudToleranceMinutes +
(solarPotentialContext ? batteryTune.global.solarPotentialGraceMinutes : 0) +
(houseSpikeDetected ? batteryTune.global.houseSpikeGraceMinutes : 0);
const cloudToleranceOk =
cloudDebtNextKwh <= params.cloudDebtMaxKwh &&
(
!cloudDebtActive ||
cloudDebtDurationMinutes <= effectiveCloudToleranceMinutes
);
// =====================================================
// LEGACY / FORECAST MODES
// =====================================================
const batteryFullHarvestMode =
parsed.forecastModeEnabled &&
context.profile !== 'night_hc' &&
context.profile !== 'outside_window' &&
parsed.soc >= params.batteryFullSoc &&
parsed.todayRemainingKwh >= params.batteryFullForecastMinKwh &&
derived.projectedEndSocAfterCharge >= (derived.endDayGuardSoc - 2);
const forecastCommitMode =
parsed.forecastModeEnabled &&
context.profile !== 'night_hc' &&
context.profile !== 'outside_window' &&
parsed.soc >= params.forecastCommitSocFloor &&
parsed.todayRemainingKwh >= params.forecastCommitMinKwh &&
derived.projectedEndSocAfterCharge >= derived.endDayGuardSoc;
const batteryPriorityMode =
!batteryFullHarvestMode && !forecastCommitMode;
// =====================================================
// EVCS NIGHT ENGINE
// =====================================================
const nightSessionActive =
context.profile === 'night_hc' &&
parsed.evcsVehicleConnected;
const nightSessionStartSocPct =
nightSessionActive
? (
Number.isFinite(history.nightSessionStartSocPct)
? history.nightSessionStartSocPct
: parsed.soc
)
: null;
const nightSessionStartAt =
nightSessionActive
? (history.nightSessionStartAt || '')
: '';
const nightSessionStartMs =
nightSessionActive
? (history.nightSessionStartMs ?? null)
: null;
const nightSessionNeedsCapture =
nightSessionActive &&
(
!Number.isFinite(history.nightSessionStartSocPct) ||
!nightSessionStartAt ||
nightSessionStartMs === null
);
const nightSessionNeedsClear =
!nightSessionActive &&
(
Number.isFinite(history.nightSessionStartSocPct) ||
!!history.nightSessionStartAt
);
const batteryHardFloorPct =
params.evcsNightBatteryHardFloorPct;
const batteryMorningTargetPct =
parsed.todayRemainingKwh >= params.evcsNightStrongForecastKwh
? params.evcsNightTargetStrongForecastPct
: parsed.todayRemainingKwh >= params.evcsNightMediumForecastKwh
? params.evcsNightTargetMediumForecastPct
: params.evcsNightTargetWeakForecastPct;
const maxBatteryDischargePoints =
nightSessionActive && Number.isFinite(nightSessionStartSocPct)
? Math.max(0, nightSessionStartSocPct - batteryMorningTargetPct)
: 0;
const gridTakeoverTriggerPct =
Math.max(batteryHardFloorPct, batteryMorningTargetPct);
const batteryKwhMissingToTarget =
nightSessionActive
? Math.max(
0,
((batteryMorningTargetPct - parsed.soc) / 100) * cfg.batteryUsableCapacityKwh
)
: 0;
const batteryRecoveryMinutesNeeded =
nightSessionActive && params.evcsNightBatteryRecoveryPowerKw > 0
? (batteryKwhMissingToTarget / params.evcsNightBatteryRecoveryPowerKw) * 60
: 0;
const batteryRecoveryUrgent =
nightSessionActive &&
Number.isFinite(lifecycle.minutesUntilDeparture) &&
lifecycle.minutesUntilDeparture >= 0 &&
(
batteryRecoveryMinutesNeeded + params.evcsNightDepartureSafetyMin
>= lifecycle.minutesUntilDeparture
);
const batteryBelowHardFloor =
parsed.soc <= batteryHardFloorPct;
const batteryBelowMorningTarget =
parsed.soc < batteryMorningTargetPct;
const batteryAtOrBelowGridTakeoverTrigger =
parsed.soc <= gridTakeoverTriggerPct;
const batteryAssistAllowed =
nightSessionActive &&
!batteryBelowHardFloor &&
!batteryRecoveryUrgent &&
parsed.soc > gridTakeoverTriggerPct;
const nightGridTakeoverNeeded =
nightSessionActive &&
(
batteryBelowHardFloor ||
batteryAtOrBelowGridTakeoverTrigger ||
batteryRecoveryUrgent
);
const nightPreferredSourceMode =
!nightSessionActive
? 'blocked'
: batteryAssistAllowed
? 'battery_assist_night'
: (
batteryBelowHardFloor ||
batteryBelowMorningTarget ||
batteryRecoveryUrgent
)
? 'grid_forced'
: 'grid_hc_allowed';
const batteryRecoveryAllowedNow =
nightSessionActive &&
nightGridTakeoverNeeded &&
(
batteryBelowMorningTarget ||
batteryRecoveryUrgent
);
const batteryTargetReached =
nightSessionActive &&
parsed.soc >= batteryMorningTargetPct;
// =====================================================
// PROFILE-BASED THRESHOLDS
// =====================================================
const profileSocStartFloor =
context.profile === 'morning_solar' ? params.morningSocMin :
context.profile === 'midday_hc_or_solar' ? params.middaySocMin :
context.profile === 'evening_high_soc' ? params.eveningSocStartMin :
params.socStartMin;
const profileSocHoldFloor =
context.profile === 'morning_solar' ? Math.max(params.socStopMin, params.morningSocMin - 10) :
context.profile === 'midday_hc_or_solar' ? Math.max(params.socStopMin, params.middaySocMin - 15) :
context.profile === 'evening_high_soc' ? params.eveningSocStopMin :
params.socStopMin;
const profileSpecificDischargeFloorA =
context.profile === 'morning_solar' ? params.morningBatteryMaxDischargeA :
context.profile === 'midday_hc_or_solar' ? params.middayBatteryMaxDischargeA :
context.profile === 'evening_high_soc' ? params.eveningBatteryMaxDischargeA :
params.batteryDischargeSoftA;
const profileBatteryDischargeFloorA =
Math.max(profileSpecificDischargeFloorA, batteryDischargeSoftDynamicA);