Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore battery charging above inverter AC rating #3015

Merged
merged 1 commit into from
Mar 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions core/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"github.com/avast/retry-go/v3"
"github.com/evcc-io/evcc/util"
)

var (
Expand All @@ -25,13 +26,14 @@ func powerToCurrent(power float64, phases int) float64 {

// sitePower returns the available delta power that the charger might additionally consume
// negative value: available power (grid export), positive value: grid import
func sitePower(grid, battery, residual float64) float64 {
func sitePower(log *util.Logger, maxGrid, grid, battery, residual float64) float64 {
// For hybrid inverters, battery can be charged from DC power in excess of
// inverter AC rating. This must be offset by the grid consumption when calculating
// available site power.
// (https://github.com/evcc-io/evcc/issues/2734)
if grid > 0 && battery < 0 && grid-battery > 50 {
// inverter AC rating. This battery charge must not be counted as available for AC consumption.
// https://github.com/evcc-io/evcc/issues/2734, https://github.com/evcc-io/evcc/issues/2986
if maxGrid > 0 && grid > maxGrid && battery < 0 {
log.TRACE.Printf("ignoring excess DC charging due to grid consumption: %.0fW > %.0fW", grid, maxGrid)
battery = 0
}

return grid + battery + residual
}
16 changes: 9 additions & 7 deletions core/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ type Site struct {
log *util.Logger

// configuration
Title string `mapstructure:"title"` // UI title
Voltage float64 `mapstructure:"voltage"` // Operating voltage. 230V for Germany.
ResidualPower float64 `mapstructure:"residualPower"` // PV meter only: household usage. Grid meter: household safety margin
Meters MetersConfig // Meter references
PrioritySoC float64 `mapstructure:"prioritySoC"` // prefer battery up to this SoC
BufferSoC float64 `mapstructure:"bufferSoC"` // ignore battery above this SoC
Title string `mapstructure:"title"` // UI title
Voltage float64 `mapstructure:"voltage"` // Operating voltage. 230V for Germany.
ResidualPower float64 `mapstructure:"residualPower"` // PV meter only: household usage. Grid meter: household safety margin
Meters MetersConfig // Meter references
PrioritySoC float64 `mapstructure:"prioritySoC"` // prefer battery up to this SoC
BufferSoC float64 `mapstructure:"bufferSoC"` // ignore battery above this SoC
MaxGridSupplyWhileBatteryCharging float64 `mapstructure:"maxGridSupplyWhileBatteryCharging"` // ignore battery charging if AC consumption is above this value

// meters
gridMeter api.Meter // Grid usage meter
Expand Down Expand Up @@ -376,7 +377,8 @@ func (site *Site) sitePower(totalChargePower float64) (float64, error) {
site.batteryBuffered = batteryPower > 0 && site.BufferSoC > 0 && socs > site.BufferSoC
}

sitePower := sitePower(site.gridPower, batteryPower, site.ResidualPower)
sitePower := sitePower(site.log, site.MaxGridSupplyWhileBatteryCharging, site.gridPower, batteryPower, site.ResidualPower)

site.log.DEBUG.Printf("site power: %.0fW", sitePower)

return sitePower, nil
Expand Down
26 changes: 15 additions & 11 deletions core/site_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,31 @@ package core

import (
"testing"

"github.com/evcc-io/evcc/util"
)

func TestSitePower(t *testing.T) {
tc := []struct {
grid, battery, site float64
maxGrid, grid, battery, site float64
}{
{0, 0, 0}, // silent night
{0, 1, 1}, // battery discharging
{0, -1, -1}, // battery charging -> negative result cannot occur in reality
{1, 0, 1}, // grid import
{1, 1, 2}, // grid import + battery discharging
{-1, 0, -1}, // grid export
{-1, -1, -2}, // grid export + battery charging
// {0, 0, 0, 0}, // silent night
// {0, 0, 1, 1}, // battery discharging
// {0, 0, -1, -1}, // battery charging -> negative result cannot occur in reality
// {0, 1, 0, 1}, // grid import
// {0, 1, 1, 2}, // grid import + battery discharging
// {0, -1, 0, -1}, // grid export
// {0, -1, -1, -2}, // grid export + battery charging
{0, 1, -1, 0}, // grid import + battery charging -> should not happen
{0.5, 1, -1, 1}, // grid import + DC battery charging
}

log := util.NewLogger("foo")

for _, tc := range tc {
res := sitePower(tc.grid, tc.battery, 0)
res := sitePower(log, tc.maxGrid, tc.grid, tc.battery, 0)
if res != tc.site {
t.Errorf("sitePower wanted %.f, got %.f", tc.site, res)
}
}
}

// TODO add test case for battery priority charging