feat: add EcoWithMinCurrent + EcoMinWithTargetTime charging modes

- charginginfo.h: 2 new ChargingMode enum values
- smartchargingmanager.h: EcoMinChargingCurrent = 6A constant
- smartchargingmanager.cpp: 5 changes
  A: fallback EcoMinWithTargetTime → EcoWithMinCurrent (not Eco)
  B: EcoWithMinCurrent ignored in spot market without target
  C: EcoMinWithTargetTime treated as EcoWithTargetTime for deadline
  D: planSurplusCharging: target time check for EcoMin modes
  E: adjustEvChargers: floor at max(6A, minValue) when no surplus/spot/time
This commit is contained in:
Patrick Schurig 2026-04-05 08:12:47 +02:00
parent d76e7e61d5
commit 7588473a60
3 changed files with 57 additions and 12 deletions

View File

@ -564,10 +564,17 @@ void SmartChargingManager::prepareInformation(const QDateTime &currentDateTime)
QDateTime endDateTime = m_chargingInfos.value(evCharger->id()).nextEndTime(currentDateTime);
if (!endDateTime.isValid() && m_chargingInfos.value(evCharger->id()).chargingMode() != ChargingInfo::ChargingModeNormal) {
qCDebug(dcNymeaEnergy()).nospace() << "No valid target time set for " << evCharger->name();
if (m_chargingInfos.value(evCharger->id()).chargingMode() == ChargingInfo::ChargingModeEcoWithTargetTime) {
qCDebug(dcNymeaEnergy()) << "Resetting to ECO mode without time.";
const ChargingInfo::ChargingMode currentMode = m_chargingInfos.value(evCharger->id()).chargingMode();
ChargingInfo::ChargingMode fallbackMode;
if (currentMode == ChargingInfo::ChargingModeEcoMinWithTargetTime) {
qCDebug(dcNymeaEnergy()) << "Resetting to ECO with min current mode.";
fallbackMode = ChargingInfo::ChargingModeEcoWithMinCurrent;
} else {
if (currentMode == ChargingInfo::ChargingModeEcoWithTargetTime)
qCDebug(dcNymeaEnergy()) << "Resetting to ECO mode without time.";
fallbackMode = ChargingInfo::ChargingModeEco;
}
m_chargingInfos[evCharger->id()].setChargingMode(ChargingInfo::ChargingModeEco);
m_chargingInfos[evCharger->id()].setChargingMode(fallbackMode);
storeChargingInfo(m_chargingInfos.value(evCharger->id()));
emit chargingInfoChanged(m_chargingInfos.value(evCharger->id()));
}
@ -754,7 +761,9 @@ void SmartChargingManager::planSpotMarketCharging(const QDateTime &currentDateTi
continue;
}
if (m_chargingInfos.value(evCharger->id()).chargingMode() == ChargingInfo::ChargingModeEco && m_chargingInfos.value(evCharger->id()).dailySpotMarketPercentage() == 0) {
if ((m_chargingInfos.value(evCharger->id()).chargingMode() == ChargingInfo::ChargingModeEco
|| m_chargingInfos.value(evCharger->id()).chargingMode() == ChargingInfo::ChargingModeEcoWithMinCurrent)
&& m_chargingInfos.value(evCharger->id()).dailySpotMarketPercentage() == 0) {
qCDebug(dcNymeaEnergy()) << evCharger->name() << "has no target time set nor any daily percentage to charge with spot market. Ignoring...";
schedulesChanged |= (m_chargingSchedules[evCharger].removeAllIssuer(ChargingAction::ChargingActionIssuerSpotMarketCharging) > 0);
m_processInfos[evCharger].chargingUntilTargetTimeUsingSpotmarket = false;
@ -774,15 +783,21 @@ void SmartChargingManager::planSpotMarketCharging(const QDateTime &currentDateTi
const ChargingInfo info = m_chargingInfos.value(evCharger->id());
if (info.chargingMode() == ChargingInfo::ChargingModeEcoWithTargetTime) {
if (info.chargingMode() == ChargingInfo::ChargingModeEcoWithTargetTime
|| info.chargingMode() == ChargingInfo::ChargingModeEcoMinWithTargetTime) {
QDateTime endDateTime = info.nextEndTime(currentDateTime);
if (!endDateTime.isValid()) {
qCDebug(dcNymeaEnergy()).nospace() << "No valid target time set for " << evCharger->name();
if (info.chargingMode() == ChargingInfo::ChargingModeEcoWithTargetTime) {
ChargingInfo::ChargingMode fallbackMode;
if (info.chargingMode() == ChargingInfo::ChargingModeEcoMinWithTargetTime) {
qCDebug(dcNymeaEnergy()) << "Resetting to ECO with min current mode.";
fallbackMode = ChargingInfo::ChargingModeEcoWithMinCurrent;
} else {
qCDebug(dcNymeaEnergy()) << "Resetting to ECO mode without time.";
fallbackMode = ChargingInfo::ChargingModeEco;
}
m_chargingInfos[evCharger->id()].setChargingMode(ChargingInfo::ChargingModeEco);
m_chargingInfos[evCharger->id()].setChargingMode(fallbackMode);
storeChargingInfo(m_chargingInfos.value(evCharger->id()));
emit chargingInfoChanged(m_chargingInfos.value(evCharger->id()));
continue;
@ -1038,7 +1053,8 @@ void SmartChargingManager::planSurplusCharging(const QDateTime &currentDateTime)
qCDebug(dcNymeaEnergy()) << "**** Evaluating" << evCharger->name();
ChargingInfo info = m_chargingInfos.value(evCharger->id());
if (info.chargingMode() != ChargingInfo::ChargingModeEcoWithTargetTime) {
if (info.chargingMode() != ChargingInfo::ChargingModeEcoWithTargetTime
&& info.chargingMode() != ChargingInfo::ChargingModeEcoMinWithTargetTime) {
qCDebug(dcNymeaEnergy()) << evCharger->name() << "does not use a target time.";
m_chargingSchedules[evCharger].removeAllIssuer(ChargingAction::ChargingActionIssuerTimeRequirement);
continue;
@ -1047,10 +1063,15 @@ void SmartChargingManager::planSurplusCharging(const QDateTime &currentDateTime)
QDateTime endDateTime = info.nextEndTime(currentDateTime);
if (!endDateTime.isValid()) {
qCDebug(dcNymeaEnergy()).nospace() << "No valid target time set for " << evCharger->name();
if (info.chargingMode() == ChargingInfo::ChargingModeEcoWithTargetTime) {
ChargingInfo::ChargingMode fallbackMode;
if (info.chargingMode() == ChargingInfo::ChargingModeEcoMinWithTargetTime) {
qCDebug(dcNymeaEnergy()) << "Resetting to ECO with min current mode.";
fallbackMode = ChargingInfo::ChargingModeEcoWithMinCurrent;
} else {
qCDebug(dcNymeaEnergy()) << "Resetting to ECO mode without time.";
fallbackMode = ChargingInfo::ChargingModeEco;
}
m_chargingInfos[evCharger->id()].setChargingMode(ChargingInfo::ChargingModeEco);
m_chargingInfos[evCharger->id()].setChargingMode(fallbackMode);
storeChargingInfo(m_chargingInfos.value(evCharger->id()));
emit chargingInfoChanged(m_chargingInfos.value(evCharger->id()));
continue;
@ -1345,7 +1366,26 @@ void SmartChargingManager::adjustEvChargers(const QDateTime &currentDateTime)
continue;
}
// 4. Else ... idle, switch off
// 4. Min current fallback (EcoWithMinCurrent / EcoMinWithTargetTime)
{
const ChargingInfo::ChargingMode mode = m_chargingInfos.value(evCharger->thing()->id()).chargingMode();
if (mode == ChargingInfo::ChargingModeEcoWithMinCurrent
|| mode == ChargingInfo::ChargingModeEcoMinWithTargetTime) {
uint minCurrent = qMax(EcoMinChargingCurrent, evCharger->maxChargingCurrentMinValue());
qCDebug(dcNymeaEnergy()).nospace() << "No surplus available for " << evCharger->name()
<< " — applying min current " << minCurrent << " A on " << processInfo.effectivePhaseCount << " phase(s).";
ChargingAction minAction(true, minCurrent, processInfo.effectivePhaseCount,
ChargingAction::ChargingActionIssuerSurplusCharging);
executeChargingAction(evCharger, minAction, currentDateTime);
if (m_chargingInfos[evCharger->thing()->id()].chargingState() != ChargingInfo::ChargingStateSurplusCharging) {
m_chargingInfos[evCharger->thing()->id()].setChargingState(ChargingInfo::ChargingStateSurplusCharging);
emit chargingInfoChanged(m_chargingInfos[evCharger->thing()->id()]);
}
continue;
}
}
// 5. Else ... idle, switch off
qCDebug(dcNymeaEnergy()).nospace() << "Setting " << evCharger->name() << " to power OFF. Idle state.";
evCharger->setChargingEnabled(false, currentDateTime);

View File

@ -45,6 +45,9 @@
class EvCharger;
class RootMeter;
// Minimum charging current (A) applied when EcoWithMinCurrent is active and no surplus is available
static constexpr uint EcoMinChargingCurrent = 6;
class SmartChargingManager : public QObject
{
Q_OBJECT

View File

@ -48,7 +48,9 @@ public:
enum ChargingMode {
ChargingModeNormal = 0, // implicit default
ChargingModeEco,
ChargingModeEcoWithTargetTime
ChargingModeEcoWithTargetTime,
ChargingModeEcoWithMinCurrent, // Eco + guaranteed minimum current (6 A) when no surplus
ChargingModeEcoMinWithTargetTime // Eco + minimum current + target time deadline
};
Q_ENUM(ChargingMode)