Repository: evcc-io/evcc
Pull request #3015 (2022-03-27 09:44:52)

Ignore battery charging above inverter AC rating
Fix https://github.com/evcc-io/evcc/issues/2986

premultiply: Mmmh. Ist das nicht vielleicht zufällig auch ein SunSpec-Wert den man abfragen könnte?
premultiply: Statt `MaxACOnBattery` `HybridPMax`?
andig: > Statt `MaxACOnBattery` `HybridPMax`?

Der Parameter so wie er jetzt ist bezieht sich auf Grid Consumption, nicht auf PV Generation. 
andig: /rebase

Patch:
diff --git a/core/helper.go b/core/helper.go
index ed31f703..e27f40e1 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 509aca3f..8adfca12 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


Test patch:
diff --git a/core/site_test.go b/core/site_test.go
index bbbda955..51b7f896 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