Loadpoint: sync charger phases on vehicle connect (#32962)

This commit is contained in:
andig 2026-08-19 08:50:24 +02:00 • committed by GitHub
parent c745672437
commit 72b7e27057
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 50 additions and 29 deletions

View file

@ -561,6 +561,11 @@ func (lp *Loadpoint) evVehicleConnectHandler() {
// soc update reset // soc update reset
lp.socUpdated = time.Time{} lp.socUpdated = time.Time{}
// charger may have reconfigured phases internally while disconnected
if err := lp.syncChargerPhases(); err != nil {
lp.log.ERROR.Println(err)
}
// set default or start detection // set default or start detection
if !lp.chargerHasFeature(api.IntegratedDevice) { if !lp.chargerHasFeature(api.IntegratedDevice) {
lp.vehicleDefaultOrDetect() lp.vehicleDefaultOrDetect()
@ -884,35 +889,9 @@ func (lp *Loadpoint) syncCharger() error {
} }
// sync phases // sync phases
_, isPs := api.Cap[api.PhaseSwitcher](lp.charger) if shouldBeConsistent {
if phases := lp.GetPhases(); isPs && shouldBeConsistent && phases > 0 { if err := lp.syncChargerPhases(); err != nil {
// fallback to active phases from measured phases return err
chargerPhases := lp.measuredPhases
if chargerPhases == 2 {
chargerPhases = 3
}
pg, isPg := api.Cap[api.PhaseGetter](lp.charger)
if isPg {
if chargerPhases, err = pg.GetPhases(); err == nil {
if chargerPhases > 0 && chargerPhases != phases {
lp.log.WARN.Printf("charger logic error: phases mismatch (got %d, expected %d)", chargerPhases, phases)
lp.SetPhases(chargerPhases)
}
} else {
if errors.Is(err, api.ErrNotAvailable) {
return nil
}
return fmt.Errorf("charger get phases: %w", err)
}
}
// use measured phase currents for active phases as fallback if charger does not provide phases
if !isPg || errors.Is(err, api.ErrNotAvailable) {
if chargerPhases > phases {
lp.log.WARN.Printf("charger logic error: phases mismatch (got %d measured, expected %d)", chargerPhases, phases)
lp.SetPhases(chargerPhases)
}
} }
} }

View file

@ -1,6 +1,9 @@
package core package core
import ( import (
"errors"
"fmt"
"github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/core/keys" "github.com/evcc-io/evcc/core/keys"
) )
@ -161,3 +164,42 @@ func (lp *Loadpoint) getChargerPhysicalPhases() int {
func (lp *Loadpoint) hasPhaseSwitching() bool { func (lp *Loadpoint) hasPhaseSwitching() bool {
return api.HasCap[api.PhaseSwitcher](lp.charger) return api.HasCap[api.PhaseSwitcher](lp.charger)
} }
// syncChargerPhases synchronizes the assumed phase state with the charger's actual state.
// Chargers may reconfigure phases internally, i.e. when the vehicle is (dis)connected.
func (lp *Loadpoint) syncChargerPhases() error {
phases := lp.GetPhases()
if !lp.hasPhaseSwitching() || phases <= 0 {
return nil
}
if pg, ok := api.Cap[api.PhaseGetter](lp.charger); ok {
chargerPhases, err := pg.GetPhases()
if err != nil {
if errors.Is(err, api.ErrNotAvailable) {
return nil
}
return fmt.Errorf("charger get phases: %w", err)
}
if chargerPhases > 0 && chargerPhases != phases {
lp.log.WARN.Printf("charger logic error: phases mismatch (got %d, expected %d)", chargerPhases, phases)
lp.SetPhases(chargerPhases)
}
return nil
}
// use measured phase currents for active phases as fallback if charger does not provide phases
chargerPhases := lp.GetMeasuredPhases()
if chargerPhases == 2 {
chargerPhases = 3
}
if chargerPhases > phases {
lp.log.WARN.Printf("charger logic error: phases mismatch (got %d measured, expected %d)", chargerPhases, phases)
lp.SetPhases(chargerPhases)
}
return nil
}