Phases: disable instead of scaling down to 1p without sufficient surplus (#32991)

This commit is contained in:
andig 2026-08-19 21:14:57 +02:00 • committed by GitHub
parent 41319dfa39
commit 748d36d130
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 23 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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()