Support multiple batteries (#1702)

This commit is contained in:
premultiply 2021-10-07 22:18:23 +02:00 • committed by GitHub
parent 8a76456dbe
commit dbba9103af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 39 deletions

View file

@ -38,9 +38,9 @@ type Site struct {
BufferSoC float64 `mapstructure:"bufferSoC"` // ignore battery above this SoC
// meters
gridMeter api.Meter // Grid usage meter
pvMeters []api.Meter // PV generation meters
batteryMeter api.Meter // Battery charging meter
gridMeter api.Meter // Grid usage meter
pvMeters []api.Meter // PV generation meters
batteryMeters []api.Meter // Battery charging meters
tariff api.Tariff // Tariff
loadpoints []*LoadPoint // Loadpoints
@ -54,10 +54,11 @@ type Site struct {
// MetersConfig contains the loadpoint's meter configuration
type MetersConfig struct {
GridMeterRef string `mapstructure:"grid"` // Grid usage meter
PVMeterRef string `mapstructure:"pv"` // PV meter
PVMetersRef []string `mapstructure:"pvs"` // Multiple PV meters
BatteryMeterRef string `mapstructure:"battery"` // Battery charging meter
GridMeterRef string `mapstructure:"grid"` // Grid usage meter
PVMeterRef string `mapstructure:"pv"` // PV meter
PVMetersRef []string `mapstructure:"pvs"` // Multiple PV meters
BatteryMeterRef string `mapstructure:"battery"` // Battery charging meter
BatteryMetersRef []string `mapstructure:"batterys"` // Multiple Battery charging meters
}
// NewSiteFromConfig creates a new site
@ -96,8 +97,19 @@ func NewSiteFromConfig(
site.pvMeters = append(site.pvMeters, pv)
}
// multiple batteries
for _, ref := range site.Meters.BatteryMetersRef {
battery := cp.Meter(ref)
site.batteryMeters = append(site.batteryMeters, battery)
}
// single battery
if site.Meters.BatteryMeterRef != "" {
site.batteryMeter = cp.Meter(site.Meters.BatteryMeterRef)
if len(site.batteryMeters) > 0 {
return nil, errors.New("cannot have battery and batteries both")
}
battery := cp.Meter(site.Meters.BatteryMeterRef)
site.batteryMeters = append(site.batteryMeters, battery)
}
// configure meter from references
@ -150,7 +162,7 @@ func (site *Site) DumpConfig() {
site.log.INFO.Printf(" meters: grid %s pv %s battery %s",
presence[site.gridMeter != nil],
presence[len(site.pvMeters) > 0],
presence[site.batteryMeter != nil],
presence[len(site.batteryMeters) > 0],
)
site.publish("gridConfigured", site.gridMeter != nil)
@ -165,16 +177,18 @@ func (site *Site) DumpConfig() {
}
}
site.publish("batteryConfigured", site.batteryMeter != nil)
if site.batteryMeter != nil {
_, ok := site.batteryMeter.(api.Battery)
site.log.INFO.Println(
meterCapabilities("battery", site.batteryMeter),
fmt.Sprintf("soc %s", presence[ok]),
)
site.publish("batteryConfigured", len(site.batteryMeters) > 0)
if len(site.batteryMeters) > 0 {
for i, battery := range site.batteryMeters {
_, ok := battery.(api.Battery)
site.log.INFO.Println(
meterCapabilities(fmt.Sprintf("battery %d", i), battery),
fmt.Sprintf("soc %s", presence[ok]),
)
if ok {
site.publish("prioritySoC", site.PrioritySoC)
if ok {
site.publish("prioritySoC", site.PrioritySoC)
}
}
}
@ -281,8 +295,24 @@ func (site *Site) updateMeters() error {
}
err := retryMeter("grid", site.gridMeter, &site.gridPower)
if err == nil {
err = retryMeter("battery", site.batteryMeter, &site.batteryPower)
if len(site.batteryMeters) > 0 {
site.batteryPower = 0
for id, meter := range site.batteryMeters {
var power float64
err := retry.Do(site.updateMeter(meter, &power), retryOptions...)
if err == nil {
site.batteryPower += power
} else {
err = fmt.Errorf("updating battery meter %d: %v", id, err)
site.log.ERROR.Println(err)
}
}
site.log.DEBUG.Printf("battery power: %.0fW", site.batteryPower)
site.publish("batteryPower", site.batteryPower)
}
// currents
@ -315,26 +345,32 @@ func (site *Site) sitePower() (float64, error) {
// honour battery priority
batteryPower := site.batteryPower
if battery, ok := site.batteryMeter.(api.Battery); ok {
soc, err := battery.SoC()
if err != nil {
site.log.ERROR.Printf("updating battery soc: %v", err)
} else {
site.log.DEBUG.Printf("battery soc: %.0f%%", soc)
site.publish("batterySoC", math.Trunc(soc))
site.Lock()
defer site.Unlock()
// if battery is charging below prioritySoC give it priority
if soc < site.PrioritySoC && batteryPower < 0 {
site.log.DEBUG.Printf("giving priority to battery charging at soc: %.0f", soc)
batteryPower = 0
if len(site.batteryMeters) > 0 {
var socs float64
for id, battery := range site.batteryMeters {
soc, err := battery.(api.Battery).SoC()
if err != nil {
err = fmt.Errorf("updating battery soc %d: %v", id, err)
site.log.ERROR.Println(err)
} else {
site.log.DEBUG.Printf("battery soc %d: %.0f%%", id, soc)
socs += soc / float64(len(site.batteryMeters))
}
// if battery is discharging above bufferSoC ignore it
site.batteryBuffered = batteryPower > 0 && site.BufferSoC > 0 && soc > site.BufferSoC
}
site.publish("batterySoC", math.Trunc(socs))
site.Lock()
defer site.Unlock()
// if battery is charging below prioritySoC give it priority
if socs < site.PrioritySoC && batteryPower < 0 {
site.log.DEBUG.Printf("giving priority to battery charging at soc: %.0f", socs)
batteryPower = 0
}
// if battery is discharging above bufferSoC ignore it
site.batteryBuffered = batteryPower > 0 && site.BufferSoC > 0 && socs > site.BufferSoC
}
sitePower := sitePower(site.gridPower, batteryPower, site.ResidualPower)

View file

@ -3,7 +3,6 @@ package core
import (
"errors"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/core/site"
)
@ -21,7 +20,7 @@ func (site *Site) SetPrioritySoC(soc float64) error {
site.Lock()
defer site.Unlock()
if _, ok := site.batteryMeter.(api.Battery); !ok {
if len(site.batteryMeters) == 0 {
return errors.New("battery not configured")
}