Optimizer: add min/max soc limits (#23429)

This commit is contained in:
andig 2025-09-06 13:41:49 +02:00 • committed by GitHub
parent dda8b56029
commit e8277796ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 7096 additions and 577 deletions

View file

@ -49,9 +49,14 @@ type MaxACPowerGetter interface {
MaxACPower() float64
}
// BatteryMaxPowerGetter provides max AC charge- and discharge power in W
type BatteryMaxPowerGetter interface {
GetMaxChargeDischargePower() (float64, float64)
// BatteryPowerLimiter provides max AC charge- and discharge power in W
type BatteryPowerLimiter interface {
GetPowerLimits() (charge, discharge float64)
}
// BatterySocLimiter provides min/max battery soc in %
type BatterySocLimiter interface {
GetSocLimits() (min, max float64)
}
// ChargeState provides current charging status

View file

@ -47,9 +47,10 @@ var a struct {
api.Battery
api.BatteryCapacity
api.SocLimiter // vehicles only
api.BatteryController
api.SocLimiter
api.BatteryMaxPowerGetter
api.BatterySocLimiter
api.BatteryPowerLimiter
}
func typ(i any) string {
@ -57,10 +58,10 @@ func typ(i any) string {
}
var dependents = map[string][]string{
typ(&a.Meter): {typ(&a.MeterEnergy), typ(&a.PhaseCurrents), typ(&a.PhaseVoltages), typ(&a.PhasePowers), typ(&a.MaxACPowerGetter)},
typ(&a.Meter): {typ(&a.MeterEnergy), typ(&a.PhaseCurrents), typ(&a.PhaseVoltages), typ(&a.MaxACPowerGetter)},
typ(&a.PhaseCurrents): {typ(&a.PhasePowers)}, // phase powers are only used to determine currents sign
typ(&a.PhaseSwitcher): {typ(&a.PhaseGetter)},
typ(&a.Battery): {typ(&a.BatteryCapacity), typ(&a.BatteryController), typ(&a.SocLimiter), typ(&a.BatteryMaxPowerGetter)},
typ(&a.Battery): {typ(&a.BatteryCapacity), typ(&a.SocLimiter), typ(&a.BatteryController), typ(&a.BatterySocLimiter), typ(&a.BatteryPowerLimiter)},
}
// hasIntersection returns if the slices intersect

View file

@ -231,12 +231,18 @@ func (site *Site) optimizerUpdate(battery []measurement) error {
bat.ChargeFromGrid = true
}
if m, ok := instance.(api.BatteryMaxPowerGetter); ok {
charge, discharge := m.GetMaxChargeDischargePower()
if m, ok := instance.(api.BatteryPowerLimiter); ok {
charge, discharge := m.GetPowerLimits()
bat.CMax = float32(charge)
bat.DMax = float32(discharge)
}
if m, ok := instance.(api.BatterySocLimiter); ok {
min, max := m.GetSocLimits()
bat.SMin = float32(*b.Capacity * float64(min) * 10) // Wh
bat.SMax = float32(*b.Capacity * float64(max) * 10) // Wh
}
req.Batteries = append(req.Batteries, bat)
details.BatteryDetails = append(details.BatteryDetails, batteryDetail{

View file

@ -22,7 +22,7 @@ func init() {
registry.Add("lgess15", NewLgEss15FromConfig)
}
//go:generate go tool decorate -f decorateLgEss -b *LgEss -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.BatteryCapacity,Capacity,func() float64"
//go:generate go tool decorate -f decorateLgEss -b *LgEss -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatterySocLimiter,GetSocLimits,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error"
func NewLgEss8FromConfig(other map[string]interface{}) (api.Meter, error) {
return NewLgEssFromConfig(other, lgpcs.LgEss8)
@ -79,15 +79,18 @@ func NewLgEss(uri, usage, registration, password string, cache time.Duration, ca
// decorate battery
var batterySoc func() (float64, error)
var batterySocLimiter func() (float64, float64)
var setBatteryMode func(api.BatteryMode) error
if usage == "battery" {
batterySoc = m.batterySoc
batterySocLimiter = batterySocLimits.Decorator()
if version, err := conn.GetFirmwareVersion(); err == nil && version >= 7430 {
setBatteryMode = m.batteryMode(batterySocLimits)
}
}
return decorateLgEss(m, totalEnergy, batterySoc, setBatteryMode, capacity), nil
return decorateLgEss(m, totalEnergy, capacity, batterySoc, batterySocLimiter, setBatteryMode), nil
}
// CurrentPower implements the api.Meter interface

View file

@ -6,7 +6,7 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery func() (float64, error), batteryController func(api.BatteryMode) error, batteryCapacity func() float64) api.Meter {
func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), batteryCapacity func() float64, battery func() (float64, error), batterySocLimiter func() (float64, float64), batteryController func(api.BatteryMode) error) api.Meter {
switch {
case battery == nil && meterEnergy == nil:
return base
@ -22,7 +22,7 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
@ -33,7 +33,7 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && meterEnergy != nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
@ -48,41 +48,7 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
api.BatteryController
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
api.BatteryController
api.MeterEnergy
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
MeterEnergy: &decorateLgEssMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && meterEnergy == nil:
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
@ -97,7 +63,7 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && meterEnergy != nil:
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
@ -116,7 +82,117 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
api.BatterySocLimiter
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
api.BatterySocLimiter
api.MeterEnergy
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateLgEssMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
api.BatteryCapacity
api.BatterySocLimiter
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateLgEssBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
api.BatteryCapacity
api.BatterySocLimiter
api.MeterEnergy
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateLgEssBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateLgEssMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
api.BatteryController
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
api.BatteryController
api.MeterEnergy
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
MeterEnergy: &decorateLgEssMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
@ -135,7 +211,7 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && meterEnergy != nil:
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
@ -157,6 +233,98 @@ func decorateLgEss(base *LgEss, meterEnergy func() (float64, error), battery fun
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
api.BatteryController
api.BatterySocLimiter
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
api.BatteryController
api.BatterySocLimiter
api.MeterEnergy
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateLgEssMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*LgEss
api.Battery
api.BatteryCapacity
api.BatteryController
api.BatterySocLimiter
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateLgEssBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*LgEss
api.Battery
api.BatteryCapacity
api.BatteryController
api.BatterySocLimiter
api.MeterEnergy
}{
LgEss: base,
Battery: &decorateLgEssBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateLgEssBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatteryController: &decorateLgEssBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateLgEssBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateLgEssMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
@ -186,6 +354,14 @@ func (impl *decorateLgEssBatteryControllerImpl) SetBatteryMode(p0 api.BatteryMod
return impl.batteryController(p0)
}
type decorateLgEssBatterySocLimiterImpl struct {
batterySocLimiter func() (float64, float64)
}
func (impl *decorateLgEssBatterySocLimiterImpl) GetSocLimits() (float64, float64) {
return impl.batterySocLimiter()
}
type decorateLgEssMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}

View file

@ -14,7 +14,7 @@ func init() {
registry.AddCtx(api.Custom, NewConfigurableFromConfig)
}
//go:generate go tool decorate -f decorateMeter -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.PhasePowers,Powers,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.BatteryMaxPowerGetter,GetMaxChargeDischargePower,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.MaxACPowerGetter,MaxACPower,func() float64"
//go:generate go tool decorate -f decorateMeter -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.PhasePowers,Powers,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.BatterySocLimiter,GetSocLimits,func() (float64, float64)" -t "api.BatteryPowerLimiter,GetPowerLimits,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.MaxACPowerGetter,MaxACPower,func() float64"
// NewConfigurableFromConfig creates api.Meter from config
func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) {
@ -26,12 +26,12 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
pvMaxACPower `mapstructure:",squash"`
// battery
batteryCapacity `mapstructure:",squash"`
batterySocLimits `mapstructure:",squash"`
batteryChargeDischargePower `mapstructure:",squash"`
Soc *plugin.Config // optional
LimitSoc *plugin.Config // optional
BatteryMode *plugin.Config // optional
batteryCapacity `mapstructure:",squash"`
batterySocLimits `mapstructure:",squash"`
batteryPowerLimits `mapstructure:",squash"`
Soc *plugin.Config // optional
LimitSoc *plugin.Config // optional
BatteryMode *plugin.Config // optional
}{
batterySocLimits: batterySocLimits{
MinSoc: 20,
@ -85,7 +85,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
res := m.Decorate(
energyG, currentsG, voltagesG, powersG,
socG, cc.batteryCapacity.Decorator(), cc.batteryChargeDischargePower.Decorator(), batModeS,
socG, cc.batteryCapacity.Decorator(), cc.batterySocLimits.Decorator(), cc.batteryPowerLimits.Decorator(), batModeS,
cc.pvMaxACPower.Decorator(),
)
@ -111,13 +111,13 @@ func (m *Meter) Decorate(
currents, voltages, powers func() (float64, float64, float64, error),
batterySoc func() (float64, error),
batteryCapacity func() float64,
batteryChargeDischargePower func() (float64, float64),
batterySocLimits, batteryPowerLimits func() (float64, float64),
setBatteryMode func(api.BatteryMode) error,
maxACPower func() float64,
) api.Meter {
return decorateMeter(m,
totalEnergy, currents, voltages, powers,
batterySoc, batteryCapacity, batteryChargeDischargePower, setBatteryMode,
batterySoc, batteryCapacity, batterySocLimits, batteryPowerLimits, setBatteryMode,
maxACPower,
)
}

View file

@ -70,7 +70,7 @@ func NewMovingAverageFromConfig(ctx context.Context, other map[string]interface{
powers = m.Powers
}
return meter.Decorate(totalEnergy, currents, voltages, powers, batterySoc, cc.Meter.batteryCapacity.Decorator(), nil, nil, nil), nil
return meter.Decorate(totalEnergy, currents, voltages, powers, batterySoc, cc.Meter.batteryCapacity.Decorator(), nil, nil, nil, nil), nil
}
type MovingAverage struct {

File diff suppressed because it is too large Load diff

View file

@ -139,7 +139,7 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
return nil, err
}
res := m.Decorate(nil, currents, nil, nil, soc, capacity, nil, nil, nil)
res := m.Decorate(nil, currents, nil, nil, soc, capacity, nil, nil, nil, nil)
return res, nil
}

View file

@ -31,7 +31,7 @@ func init() {
registry.Add("powerwall", NewPowerWallFromConfig)
}
//go:generate go tool decorate -f decoratePowerWall -b *PowerWall -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error"
//go:generate go tool decorate -f decoratePowerWall -b *PowerWall -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.BatterySocLimiter,GetSocLimits,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error"
// NewPowerWallFromConfig creates a PowerWall Powerwall Meter from generic config
func NewPowerWallFromConfig(other map[string]interface{}) (api.Meter, error) {
@ -143,10 +143,13 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, refres
}
// decorate battery
var batterySoc func() (float64, error)
var batteryCapacity func() float64
var batterySoc func() (float64, error)
var batterySocLimiter func() (float64, float64)
if usage == "battery" {
batterySoc = m.batterySoc
batterySocLimiter = batterySocLimits.Decorator()
res, err := m.client.GetSystemStatus()
if err != nil {
@ -173,7 +176,7 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, refres
})
}
return decoratePowerWall(m, totalEnergy, batterySoc, batteryCapacity, batModeS), nil
return decoratePowerWall(m, totalEnergy, batterySoc, batteryCapacity, batterySocLimiter, batModeS), nil
}
var _ api.Meter = (*PowerWall)(nil)

View file

@ -6,7 +6,7 @@ import (
"github.com/evcc-io/evcc/api"
)
func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), battery func() (float64, error), batteryCapacity func() float64, batteryController func(api.BatteryMode) error) api.Meter {
func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), battery func() (float64, error), batteryCapacity func() float64, batterySocLimiter func() (float64, float64), batteryController func(api.BatteryMode) error) api.Meter {
switch {
case battery == nil && meterEnergy == nil:
return base
@ -22,7 +22,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
@ -33,7 +33,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && meterEnergy != nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
@ -48,7 +48,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && meterEnergy == nil:
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
@ -63,7 +63,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && meterEnergy != nil:
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
@ -82,7 +82,83 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
api.BatterySocLimiter
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
api.BatterySocLimiter
api.MeterEnergy
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decoratePowerWallMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
api.BatteryCapacity
api.BatterySocLimiter
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatteryCapacity: &decoratePowerWallBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
api.BatteryCapacity
api.BatterySocLimiter
api.MeterEnergy
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatteryCapacity: &decoratePowerWallBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decoratePowerWallMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
@ -97,7 +173,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && meterEnergy != nil:
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
@ -116,7 +192,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && meterEnergy == nil:
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
@ -135,7 +211,7 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && meterEnergy != nil:
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
@ -157,6 +233,98 @@ func decoratePowerWall(base *PowerWall, meterEnergy func() (float64, error), bat
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
api.BatteryController
api.BatterySocLimiter
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatteryController: &decoratePowerWallBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
api.BatteryController
api.BatterySocLimiter
api.MeterEnergy
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatteryController: &decoratePowerWallBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decoratePowerWallMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*PowerWall
api.Battery
api.BatteryCapacity
api.BatteryController
api.BatterySocLimiter
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatteryCapacity: &decoratePowerWallBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatteryController: &decoratePowerWallBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*PowerWall
api.Battery
api.BatteryCapacity
api.BatteryController
api.BatterySocLimiter
api.MeterEnergy
}{
PowerWall: base,
Battery: &decoratePowerWallBatteryImpl{
battery: battery,
},
BatteryCapacity: &decoratePowerWallBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatteryController: &decoratePowerWallBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decoratePowerWallBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decoratePowerWallMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
@ -186,6 +354,14 @@ func (impl *decoratePowerWallBatteryControllerImpl) SetBatteryMode(p0 api.Batter
return impl.batteryController(p0)
}
type decoratePowerWallBatterySocLimiterImpl struct {
batterySocLimiter func() (float64, float64)
}
func (impl *decoratePowerWallBatterySocLimiterImpl) GetSocLimits() (float64, float64) {
return impl.batterySocLimiter()
}
type decoratePowerWallMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}

View file

@ -33,7 +33,7 @@ func init() {
registry.AddCtx("rct", NewRCTFromConfig)
}
//go:generate go tool decorate -f decorateRCT -b *RCT -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.BatteryCapacity,Capacity,func() float64"
//go:generate go tool decorate -f decorateRCT -b *RCT -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatterySocLimiter,GetSocLimits,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.BatteryCapacity,Capacity,func() float64"
// NewRCTFromConfig creates an RCT from generic config
func NewRCTFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) {
@ -101,9 +101,12 @@ func NewRCT(ctx context.Context, uri, usage string, batterySocLimits batterySocL
// decorate api.Battery
var batterySoc func() (float64, error)
var batterySocLimiter func() (float64, float64)
var batteryMode func(api.BatteryMode) error
if usage == "battery" {
batterySoc = m.batterySoc
batterySocLimiter = batterySocLimits.Decorator()
batteryMode = func(mode api.BatteryMode) error {
if mode != api.BatteryNormal {
@ -154,7 +157,7 @@ func NewRCT(ctx context.Context, uri, usage string, batterySocLimits batterySocL
}
}
return decorateRCT(m, totalEnergy, batterySoc, batteryMode, capacity), nil
return decorateRCT(m, totalEnergy, batterySoc, batterySocLimiter, batteryMode, capacity), nil
}
func (m *RCT) floatVal(f float32) []byte {

View file

@ -6,7 +6,7 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func() (float64, error), batteryController func(api.BatteryMode) error, batteryCapacity func() float64) api.Meter {
func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func() (float64, error), batterySocLimiter func() (float64, float64), batteryController func(api.BatteryMode) error, batteryCapacity func() float64) api.Meter {
switch {
case battery == nil && meterEnergy == nil:
return base
@ -22,7 +22,7 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
@ -33,7 +33,7 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && meterEnergy != nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
@ -48,7 +48,41 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
api.BatterySocLimiter
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity == nil && batteryController == nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
api.BatterySocLimiter
api.MeterEnergy
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateRCTMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
@ -63,7 +97,7 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && meterEnergy != nil:
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
@ -82,7 +116,49 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && meterEnergy == nil:
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
api.BatteryController
api.BatterySocLimiter
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatteryController: &decorateRCTBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity == nil && batteryController != nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
api.BatteryController
api.BatterySocLimiter
api.MeterEnergy
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatteryController: &decorateRCTBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateRCTMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
@ -97,7 +173,7 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && meterEnergy != nil:
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
@ -116,7 +192,49 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && meterEnergy == nil:
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
api.BatteryCapacity
api.BatterySocLimiter
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateRCTBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity != nil && batteryController == nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
api.BatteryCapacity
api.BatterySocLimiter
api.MeterEnergy
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateRCTBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateRCTMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter == nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
@ -135,7 +253,7 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && meterEnergy != nil:
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter == nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
@ -157,6 +275,56 @@ func decorateRCT(base *RCT, meterEnergy func() (float64, error), battery func()
meterEnergy: meterEnergy,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter != nil && meterEnergy == nil:
return &struct {
*RCT
api.Battery
api.BatteryCapacity
api.BatteryController
api.BatterySocLimiter
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateRCTBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatteryController: &decorateRCTBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
}
case battery != nil && batteryCapacity != nil && batteryController != nil && batterySocLimiter != nil && meterEnergy != nil:
return &struct {
*RCT
api.Battery
api.BatteryCapacity
api.BatteryController
api.BatterySocLimiter
api.MeterEnergy
}{
RCT: base,
Battery: &decorateRCTBatteryImpl{
battery: battery,
},
BatteryCapacity: &decorateRCTBatteryCapacityImpl{
batteryCapacity: batteryCapacity,
},
BatteryController: &decorateRCTBatteryControllerImpl{
batteryController: batteryController,
},
BatterySocLimiter: &decorateRCTBatterySocLimiterImpl{
batterySocLimiter: batterySocLimiter,
},
MeterEnergy: &decorateRCTMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
@ -186,6 +354,14 @@ func (impl *decorateRCTBatteryControllerImpl) SetBatteryMode(p0 api.BatteryMode)
return impl.batteryController(p0)
}
type decorateRCTBatterySocLimiterImpl struct {
batterySocLimiter func() (float64, float64)
}
func (impl *decorateRCTBatterySocLimiterImpl) GetSocLimits() (float64, float64) {
return impl.batterySocLimiter()
}
type decorateRCTMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}

View file

@ -8,7 +8,7 @@ type batteryCapacity struct {
// var _ api.BatteryCapacity = (*batteryCapacity)(nil)
// Decorator returns the capacity decorator
// Decorator returns an api.BatteryCapacity decorator
func (m *batteryCapacity) Decorator() func() float64 {
if m.Capacity == 0 {
return nil
@ -18,15 +18,15 @@ func (m *batteryCapacity) Decorator() func() float64 {
}
}
type batteryChargeDischargePower struct {
type batteryPowerLimits struct {
MaxChargePower float64
MaxDischargePower float64
}
// var _ api.BatteryMaxPowerGetter = (*batteryChargeDischargePower)(nil)
// var _ api.BatteryPowerLimiter = (*batteryPowerLimits)(nil)
// Decorator returns the max charge/discharge power decorator
func (m *batteryChargeDischargePower) Decorator() func() (float64, float64) {
// Decorator returns an api.BatteryPowerLimiter decorator
func (m *batteryPowerLimits) Decorator() func() (float64, float64) {
if m.MaxChargePower == 0 || m.MaxDischargePower == 0 {
return nil
}
@ -39,6 +39,16 @@ type batterySocLimits struct {
MinSoc, MaxSoc float64
}
// Decorator returns an api.BatterySocLimiter decorator
func (m *batterySocLimits) Decorator() func() (float64, float64) {
if m.MinSoc == 0 && m.MaxSoc == 0 {
return nil
}
return func() (float64, float64) {
return m.MinSoc, m.MaxSoc
}
}
// LimitController returns an api.BatteryController decorator
func (m *batterySocLimits) LimitController(socG func() (float64, error), limitSocS func(float64) error) func(api.BatteryMode) error {
return func(mode api.BatteryMode) error {