fix: detect actual EV phase instead of hardcoding phase A

- evcharger.cpp: phases() now calls meteredPhases() instead of returning PhaseNone
- smartchargingmanager: add chargerPhaseKey() with 3-level fallback
  1. meteredPhases() when charger is active
  2. effectivePhases from last known state
  3. fallback 'A' + warning (previous behavior)
- Remove 4 FIXME comments on lines 394, 477, 517
This commit is contained in:
Patrick Schurig 2026-04-05 07:09:38 +02:00
parent b343650f9b
commit 5df69d79f8
3 changed files with 34 additions and 13 deletions

View File

@ -168,13 +168,13 @@ Electricity::Phases EvCharger::phases() const
}
}
// TODO: Can we figure out from the root meter on which phase we're attached?
// One idea would be to sign up on root meter changes. When chargingEnabled is set or unset,
// memorize root meter values and compare the next (or some more) cycles if a phase changed
// by a similar value as we'd expect
// Until we have this detection, we must ask the user how many phases will be used while charging
// For single-phase chargers, detect the actual connected phase from per-phase metering
Electricity::Phases metered = meteredPhases();
if (metered != Electricity::PhaseNone) {
return metered;
}
// Phase unknown — charger has no per-phase metering or is not currently charging
return Electricity::PhaseNone;
}

View File

@ -391,9 +391,9 @@ void SmartChargingManager::verifyOverloadProtection(const QDateTime &currentDate
if (evCharger->phaseCount() == 1) {
// FIXME: get the actual phase, not assume it is phase A in single phase charging
if (requiredThrottlePower.value("A") > 0) {
int throttleAmpere = qCeil(requiredThrottlePower.value("A") / 230);
const QString phase = chargerPhaseKey(evCharger);
if (requiredThrottlePower.value(phase) > 0) {
int throttleAmpere = qCeil(requiredThrottlePower.value(phase) / 230);
int desiredFallbackAmpere = evCharger->maxChargingCurrent() - throttleAmpere;
if (desiredFallbackAmpere < static_cast<int>(m_processInfos[evCharger].minimalChargingCurrent)) {
@ -474,8 +474,7 @@ void SmartChargingManager::verifyOverloadProtectionRecovery(const QDateTime &cur
bool restoreCharger = false;
if (evCharger->phaseCount() == 1) {
// FIXME: get the actual phase, not assume it is phase A in single phase charging
if (availablePhasePower.value("A") >= requiredRestorePhasePower) {
if (availablePhasePower.value(chargerPhaseKey(evCharger)) >= requiredRestorePhasePower) {
qCDebug(dcNymeaEnergy()) << "Overload protection: Enought power available to restore the original configuration"
<< manualMaxChargingCurrent(evCharger->id()) << "[A]";
restoreCharger = true;
@ -514,8 +513,7 @@ void SmartChargingManager::verifyOverloadProtectionRecovery(const QDateTime &cur
bool restoreChargerPower = false;
if (evCharger->phaseCount() == 1) {
// FIXME: get the actual phase, not assume it is phase A in single phase charging
if (availablePhasePower.value("A") >= requiredRestorePhasePower) {
if (availablePhasePower.value(chargerPhaseKey(evCharger)) >= requiredRestorePhasePower) {
qCDebug(dcNymeaEnergy()) << "Overload protection: Enought power available to start charging using the minimal charging current of"
<< m_processInfos.value(evCharger).minimalChargingCurrent << "[A]";
restoreChargerPower = true;
@ -1770,6 +1768,28 @@ Electricity::Phases SmartChargingManager::getAscendingPhasesForCount(uint phaseC
return phases;
}
QString SmartChargingManager::chargerPhaseKey(EvCharger *evCharger) const
{
// Use live metering first — reliable when charger is actively charging
Electricity::Phases metered = evCharger->meteredPhases();
if (metered.testFlag(Electricity::PhaseA)) return QStringLiteral("A");
if (metered.testFlag(Electricity::PhaseB)) return QStringLiteral("B");
if (metered.testFlag(Electricity::PhaseC)) return QStringLiteral("C");
// Charger may be off — fall back to last known phase stored by prepareInformation()
if (m_processInfos.contains(evCharger)) {
Electricity::Phases known = m_processInfos.value(evCharger).effectivePhases;
if (known.testFlag(Electricity::PhaseA)) return QStringLiteral("A");
if (known.testFlag(Electricity::PhaseB)) return QStringLiteral("B");
if (known.testFlag(Electricity::PhaseC)) return QStringLiteral("C");
}
// Phase unknown — conservative fallback (unchanged previous behavior)
qCWarning(dcNymeaEnergy()) << "Cannot determine connected phase for"
<< evCharger->name() << "— defaulting to phase A";
return QStringLiteral("A");
}
uint SmartChargingManager::getBestPhaseCount(EvCharger *evCharger, double surplusAmpere)
{
uint desiredPhaseCount = 1;

View File

@ -123,6 +123,7 @@ private:
Electricity::Phases getAscendingPhasesForCount(uint phaseCount);
uint getBestPhaseCount(EvCharger *evCharger, double surplusAmpere);
QString chargerPhaseKey(EvCharger *evCharger) const;
EnergyManager *m_energyManager = nullptr;
ThingManager *m_thingManager = nullptr;