From d8a6efc35a6e70be4bb3b086b678a10a5046fe30 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 27 Mar 2022 17:42:02 +0200 Subject: [PATCH] Ignore battery charging above inverter AC rating (#3015) --- core/helper.go | 12 +++++++----- core/site.go | 16 +++++++++------- core/site_test.go | 26 +++++++++++++++----------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/core/helper.go b/core/helper.go index ed31f703b0..e27f40e1dd 100644 --- a/core/helper.go +++ b/core/helper.go @@ -2,6 +2,7 @@ package core import ( "github.com/avast/retry-go/v3" + "github.com/evcc-io/evcc/util" ) var ( @@ -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 } diff --git a/core/site.go b/core/site.go index 509aca3f9a..8adfca1240 100644 --- a/core/site.go +++ b/core/site.go @@ -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 @@ -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 diff --git a/core/site_test.go b/core/site_test.go index bbbda95565..51b7f896ed 100644 --- a/core/site_test.go +++ b/core/site_test.go @@ -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