Repository: evcc-io/evcc
Pull request #11346 (2023-12-30 12:45:18)

HuaweiAC: use active phases api
Depends on https://github.com/evcc-io/evcc/pull/11345


Patch:
diff --git a/charger/huawei-ac.go b/charger/huawei-ac.go
index d41b1d65..def40557 100644
--- a/charger/huawei-ac.go
+++ b/charger/huawei-ac.go
@@ -84,13 +84,9 @@ func NewHuaweiACCharger(uri string, slaveID uint8) (api.Charger, error) {
 }
 
 func (wb *HuaweiACCharger) setCurrent(current float64) error {
-	var phases int
-	// get (expectedly) active phases from loadpoint
+	phases := 3
 	if wb.lp != nil {
-		phases = wb.lp.GetPhases()
-	}
-	if phases == 0 {
-		phases = 3
+		phases = wb.lp.ActivePhases()
 	}
 
 	b := make([]byte, 4)
diff --git a/core/loadpoint.go b/core/loadpoint.go
index fbe24e6d..7b302e5b 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -534,7 +534,7 @@ func (lp *Loadpoint) evChargeCurrentHandler(current float64) {
 // If physical charge meter is present this handler is not used.
 // The actual value is published by the evChargeCurrentHandler
 func (lp *Loadpoint) evChargeCurrentWrappedMeterHandler(current float64) {
-	power := current * float64(lp.activePhases()) * Voltage
+	power := current * float64(lp.ActivePhases()) * Voltage
 
 	// if disabled we cannot be charging
 	if !lp.enabled || !lp.charging() {
@@ -582,7 +582,7 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
 
 	lp.setConfiguredPhases(lp.ConfiguredPhases)
 	lp.publish(keys.PhasesEnabled, lp.phases)
-	lp.publish(keys.PhasesActive, lp.activePhases())
+	lp.publish(keys.PhasesActive, lp.ActivePhases())
 	lp.publishTimer(phaseTimer, 0, timerInactive)
 	lp.publishTimer(pvTimer, 0, timerInactive)
 	lp.publishTimer(guardTimer, 0, timerInactive)
@@ -1052,7 +1052,7 @@ func (lp *Loadpoint) pvScalePhases(sitePower, minCurrent, maxCurrent float64) bo
 	}
 
 	var waiting bool
-	activePhases := lp.activePhases()
+	activePhases := lp.ActivePhases()
 	availablePower := lp.chargePower - sitePower
 	scalable := (sitePower > 0 || !lp.enabled) && activePhases > 1 && lp.ConfiguredPhases < 3
 
@@ -1156,7 +1156,7 @@ func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batter
 
 	// calculate target charge current from delta power and actual current
 	effectiveCurrent := lp.effectiveCurrent()
-	activePhases := lp.activePhases()
+	activePhases := lp.ActivePhases()
 	deltaCurrent := powerToCurrent(-sitePower, activePhases)
 	targetCurrent := max(effectiveCurrent+deltaCurrent, 0)
 
diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go
index d3d480fc..9f245a06 100644
--- a/core/loadpoint/api.go
+++ b/core/loadpoint/api.go
@@ -49,6 +49,8 @@ type API interface {
 	GetPhases() int
 	// SetPhases sets the enabled phases
 	SetPhases(int) error
+	// ActivePhases returns the active phases for the current vehicle
+	ActivePhases() int
 
 	// GetLimitSoc returns the session limit soc
 	GetLimitSoc() int
diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go
index 091a6593..8138ff1a 100644
--- a/core/loadpoint/mock.go
+++ b/core/loadpoint/mock.go
@@ -35,6 +35,20 @@ func (m *MockAPI) EXPECT() *MockAPIMockRecorder {
 	return m.recorder
 }
 
+// ActivePhases mocks base method.
+func (m *MockAPI) ActivePhases() int {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ActivePhases")
+	ret0, _ := ret[0].(int)
+	return ret0
+}
+
+// ActivePhases indicates an expected call of ActivePhases.
+func (mr *MockAPIMockRecorder) ActivePhases() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivePhases", reflect.TypeOf((*MockAPI)(nil).ActivePhases))
+}
+
 // EffectiveMaxPower mocks base method.
 func (m *MockAPI) EffectiveMaxPower() float64 {
 	m.ctrl.T.Helper()
diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go
index 61cc6d65..e82b8801 100644
--- a/core/loadpoint_phases.go
+++ b/core/loadpoint_phases.go
@@ -44,7 +44,7 @@ func (lp *Loadpoint) resetMeasuredPhases() {
 	lp.measuredPhases = 0
 	lp.Unlock()
 
-	lp.publish(keys.PhasesActive, lp.activePhases())
+	lp.publish(keys.PhasesActive, lp.ActivePhases())
 }
 
 // getMeasuredPhases provides synchronized access to measuredPhases
@@ -64,9 +64,9 @@ func expect(phases int) int {
 	return unknownPhases
 }
 
-// activePhases returns the number of expectedly active phases for the meter.
+// ActivePhases returns the number of expectedly active phases for the meter.
 // If unknown for 1p3p chargers during startup it will assume 3p.
-func (lp *Loadpoint) activePhases() int {
+func (lp *Loadpoint) ActivePhases() int {
 	physical := lp.GetPhases()
 	vehicle := lp.getVehiclePhases()
 	measured := lp.getMeasuredPhases()
diff --git a/core/loadpoint_vehicle.go b/core/loadpoint_vehicle.go
index 94af1d3c..673c18f9 100644
--- a/core/loadpoint_vehicle.go
+++ b/core/loadpoint_vehicle.go
@@ -156,7 +156,7 @@ func (lp *Loadpoint) setActiveVehicle(v api.Vehicle) {
 	}
 
 	// re-publish vehicle settings
-	lp.publish(keys.PhasesActive, lp.activePhases())
+	lp.publish(keys.PhasesActive, lp.ActivePhases())
 	lp.unpublishVehicle()
 
 	// publish effective values


Test patch:
diff --git a/core/loadpoint_phases_test.go b/core/loadpoint_phases_test.go
index 52fa6cdd..2c92c068 100644
--- a/core/loadpoint_phases_test.go
+++ b/core/loadpoint_phases_test.go
@@ -125,7 +125,7 @@ func TestMaxActivePhases(t *testing.T) {
 func testScale(t *testing.T, lp *Loadpoint, sitePower float64, direction string, tc testCase) {
 	t.Helper()
 
-	act := lp.activePhases()
+	act := lp.ActivePhases()
 	max := lp.maxActivePhases()
 
 	testDirection := direction[0:1] // (d)own or (u)p
@@ -221,7 +221,7 @@ func TestPvScalePhases(t *testing.T) {
 			t.Error("wrong phases", lp.phases, tc.physical)
 		}
 
-		if phs := lp.activePhases(); phs != tc.actExpected {
+		if phs := lp.ActivePhases(); phs != tc.actExpected {
 			t.Errorf("expected active %d, got %d", tc.actExpected, phs)
 		}
 		if phs := lp.maxActivePhases(); phs != tc.maxExpected {