diff --git a/core/loadpoint.go b/core/loadpoint.go index 69d21af59..3223400aa 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -1449,8 +1449,15 @@ func (lp *Loadpoint) pvScalePhases(sitePower, minCurrent, maxCurrent float64) in lp.log.DEBUG.Printf("available power %.0fW < %.0fW min %dp threshold", availablePower, float64(activePhases)*Voltage*minCurrent, activePhases) } + // while charging, scaling down only helps if 1p is sustainable, otherwise it + // merely delays the pv disable timer by the phase timer duration + useful := !lp.enabled || !lp.charging() || powerToCurrent(availablePower, 1) >= minCurrent + if insufficient && !useful { + lp.log.DEBUG.Printf("available power %.0fW < %.0fW min 1p threshold, disabling instead of scaling down", availablePower, Voltage*minCurrent) + } + // scaling down also frees load management headroom for min power on activePhases - scalable = insufficient || !lp.circuitAllowsPhases(activePhases, minCurrent) + scalable = insufficient && useful || !lp.circuitAllowsPhases(activePhases, minCurrent) } // scale down phases diff --git a/core/loadpoint_phases_test.go b/core/loadpoint_phases_test.go index da713e4a7..cc723ea14 100644 --- a/core/loadpoint_phases_test.go +++ b/core/loadpoint_phases_test.go @@ -271,6 +271,7 @@ func TestPvScalePhases(t *testing.T) { // scale down min1p := 0.1 lp.phaseTimer = time.Time{} + lp.chargePower = float64(lp.ActivePhases()) * minA * Voltage // charging at min current plainCharger.EXPECT().Enable(false).Return(nil).MaxTimes(1) phaseCharger.EXPECT().Phases1p3p(1).Return(nil).MaxTimes(1) @@ -372,6 +373,17 @@ func TestPvScalePhasesTimer(t *testing.T) { lp.phaseTimer = elapsed }}, + // charging with insufficient power for 1p: disable instead of scaling down + {"3/3->1, insufficient for 1p, charging", 3, 3, 0.1, 3, 0, func(lp *Loadpoint) { + lp.phaseTimer = elapsed + lp.enabled = true + }}, + {"3/3->1, sufficient for 1p, charging", 3, 3, 3 * Voltage * minA / 2, 1, 1, func(lp *Loadpoint) { + lp.phaseTimer = elapsed + lp.enabled = true + lp.chargePower = 3 * Voltage * minA + }}, + // switch down from 3p/0p while not yet charging {"3/0->1, not enough power, not charging", 3, 0, 0, 1, 1, func(lp *Loadpoint) { lp.status = api.StatusB diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go index d427b0f1d..ccb160dcf 100644 --- a/core/loadpoint_test.go +++ b/core/loadpoint_test.go @@ -752,8 +752,9 @@ func TestPVHysteresisAfterPhaseSwitch(t *testing.T) { Disable: loadpoint.ThresholdConfig{ Delay: dt, }, - status: api.StatusC, - enabled: true, + status: api.StatusC, + enabled: true, + chargePower: 3 * Voltage * minA, // charging 3p at min current } start := clock.Now()