Battery control: prevent grid charging if load management restrictions apply (#23920)
This commit is contained in:
parent
55fc2e40c5
commit
3c2f0dfad6
4 changed files with 44 additions and 18 deletions
|
|
@ -3,6 +3,7 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util/config"
|
||||
)
|
||||
|
||||
|
|
@ -53,3 +54,22 @@ func deviceProperties[T any](dev config.Device[T]) config.Properties {
|
|||
}
|
||||
return config.Properties{}
|
||||
}
|
||||
|
||||
// deviceTitleOrName returns device title or name
|
||||
func deviceTitleOrName[T any](dev config.Device[T]) string {
|
||||
if d, ok := dev.(config.ConfigurableDevice[T]); ok {
|
||||
if title := d.Properties().Title; title != "" {
|
||||
return title
|
||||
}
|
||||
}
|
||||
return dev.Config().Name
|
||||
}
|
||||
|
||||
// circuitMaxPower returns a circuits power limit
|
||||
func circuitMaxPower(circuit api.Circuit) float64 {
|
||||
if circuit == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return circuit.GetMaxPower()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1236,7 +1236,7 @@ func (lp *Loadpoint) fastCharging() error {
|
|||
maxPower1p := Voltage * lp.effectiveMaxCurrent()
|
||||
|
||||
// load management limit active
|
||||
if circuitMaxPower := lp.circuitMaxPower(); circuitMaxPower > 0 && circuitMaxPower < 1.1*maxPower1p {
|
||||
if circuitMaxPower := circuitMaxPower(lp.circuit); circuitMaxPower > 0 && circuitMaxPower < 1.1*maxPower1p {
|
||||
phases = 1
|
||||
lp.log.DEBUG.Printf("fast charging: scaled to 1p to match %.0fW max circuit power", circuitMaxPower)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,21 +217,13 @@ func (lp *Loadpoint) EffectiveMinPower() float64 {
|
|||
return Voltage * lp.effectiveMinCurrent() * float64(lp.minActivePhases())
|
||||
}
|
||||
|
||||
func (lp *Loadpoint) circuitMaxPower() float64 {
|
||||
if lp.circuit == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return lp.circuit.GetMaxPower()
|
||||
}
|
||||
|
||||
// EffectiveMaxPower returns the effective max power taking vehicle capabilities,
|
||||
// phase scaling and load management power limits into account
|
||||
func (lp *Loadpoint) EffectiveMaxPower() float64 {
|
||||
lp.RLock()
|
||||
defer lp.RUnlock()
|
||||
|
||||
if circuitMaxPower := lp.circuitMaxPower(); circuitMaxPower > 0 {
|
||||
if circuitMaxPower := circuitMaxPower(lp.circuit); circuitMaxPower > 0 {
|
||||
return min(lp.effectiveMaxPower(), circuitMaxPower)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/core/keys"
|
||||
"github.com/evcc-io/evcc/core/loadpoint"
|
||||
"github.com/evcc-io/evcc/util/config"
|
||||
)
|
||||
|
||||
func batteryModeModified(mode api.BatteryMode) bool {
|
||||
|
|
@ -94,7 +95,9 @@ func (site *Site) requiredBatteryMode(batteryGridChargeActive bool, rate api.Rat
|
|||
}
|
||||
|
||||
// batteryMaxSocReached checks is battery has exceed max soc limit
|
||||
func batteryMaxSocReached(meter api.Meter) (bool, error) {
|
||||
func (site *Site) batteryMaxSocReached(dev config.Device[api.Meter]) (bool, error) {
|
||||
meter := dev.Instance()
|
||||
|
||||
batLimiter, ok := meter.(api.BatterySocLimiter)
|
||||
if !ok {
|
||||
return false, nil
|
||||
|
|
@ -110,8 +113,12 @@ func batteryMaxSocReached(meter api.Meter) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
_, max := batLimiter.GetSocLimits()
|
||||
return soc >= max, nil
|
||||
if _, max := batLimiter.GetSocLimits(); max > 0 && max < 100 && soc >= max {
|
||||
site.log.DEBUG.Printf("battery %s: limit soc reached (%.0f > %.0f)", deviceTitleOrName(dev), soc, max)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// applyBatteryMode applies the mode to each battery
|
||||
|
|
@ -121,6 +128,15 @@ func batteryMaxSocReached(meter api.Meter) (bool, error) {
|
|||
// The current soc is validated against max soc.
|
||||
// In case max soc is reached, hold mode is applied.
|
||||
func (site *Site) applyBatteryMode(mode api.BatteryMode) error {
|
||||
isCharge := mode == api.BatteryCharge || mode == api.BatteryUnknown && site.batteryMode == api.BatteryCharge
|
||||
|
||||
// put battery into hold mode when charging and load management limit active
|
||||
if circuitMaxPower := circuitMaxPower(site.circuit); isCharge && circuitMaxPower > 0 {
|
||||
// TODO do this only once
|
||||
site.log.DEBUG.Printf("battery mode: load management active at %.0fW", circuitMaxPower)
|
||||
mode = api.BatteryHold
|
||||
}
|
||||
|
||||
for _, dev := range site.batteryMeters {
|
||||
meter := dev.Instance()
|
||||
|
||||
|
|
@ -129,18 +145,16 @@ func (site *Site) applyBatteryMode(mode api.BatteryMode) error {
|
|||
continue
|
||||
}
|
||||
|
||||
isCharge := mode == api.BatteryCharge || mode == api.BatteryUnknown && site.batteryMode == api.BatteryCharge
|
||||
|
||||
// validate max soc
|
||||
if isCharge {
|
||||
ok, err := batteryMaxSocReached(meter)
|
||||
if isCharge && mode != api.BatteryHold {
|
||||
ok, err := site.batteryMaxSocReached(dev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// put battery into hold mode when soc limit reached
|
||||
if ok {
|
||||
// TODO do this once only
|
||||
// TODO do this only once
|
||||
mode = api.BatteryHold
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue