ajout gestion batterie
This commit is contained in:
233
BATTERY/BATTERY_CLOUD_LOCKSP2.js
Normal file
233
BATTERY/BATTERY_CLOUD_LOCKSP2.js
Normal 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);
|
||||
Reference in New Issue
Block a user