Support multiple batteries (#1702)
This commit is contained in:
parent
8a76456dbe
commit
dbba9103af
2 changed files with 74 additions and 39 deletions
110
core/site.go
110
core/site.go
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue