Repository: andig/evcc (go)

Issue:
PV Mode berücksichtigt Enable Threshold nicht
**Describe the bug**
Ich möchte gerne das Laden starten sobald ich 500W einspeise (Grid power = -500). Tatsächlich wird aber erst bei 1380W Einspeisung die Ladung gestartet

**To Reproduce**
Steps to reproduce the behavior:
1. PV Mode
2. Enable Threshold auf -500 setzen
```
loadpoints:
- title: main # name for logging
  mode: pv
  charger:  configurable
  enable: # pv mode enable behavior
    delay: 5s # threshold must be exceeded for this long, 2m
    threshold: -500 # minimum export power (W). If zero, export must exceeds minimum charge power to enable
  disable: # pv mode disable behavior
    delay: 5s # threshold must be exceeded for this long, 2m
    threshold: 3000 # maximum import power (W # MIN = 1380-enable(600) = 780)
  sensitivity: 5 # current raise/lower steps size (default 1A)
  guardduration: 1s # switch charger contactor not more often than this (default 10m)
  maxcurrent: 16 # maximum charge current (default 16A)
  phases: 1 # ev phases (default 3)
```
3. Warten bis mehr als 500W eingespeist werden

**Expected behavior**
PV Ladung startet nach Delay


Das Probel scheint bei
https://github.com/andig/evcc/blob/master/core/loadpoint.go
zu liegen da hier abgebrochen wird.

```
	// in PV mode disable charger if car not charging and minCurrent not possible
	if mode == api.ModePV && lp.status != api.StatusC {
		lp.pvTimer = time.Time{}

		if targetCurrent < lp.MinCurrent {
			lp.log.DEBUG.Printf("target<MinCurrent -> return")
			return 0
		}

		lp.log.DEBUG.Printf("MinCurrent -> return")
		return lp.MinCurrent
	}
```



Pull request description:
Honour thresholds in PV mode
Fix #433

Patch:
diff --git a/core/loadpoint.go b/core/loadpoint.go
index 919b7693..b619461e 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -609,17 +609,6 @@ func (lp *LoadPoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64) int64
 		return lp.MinCurrent
 	}
 
-	// in PV mode disable charger if car not charging and minCurrent not possible
-	if mode == api.ModePV && lp.status != api.StatusC {
-		lp.pvTimer = time.Time{}
-
-		if targetCurrent < lp.MinCurrent {
-			return 0
-		}
-
-		return lp.MinCurrent
-	}
-
 	// read only once to simplify testing
 	if mode == api.ModePV && lp.enabled && targetCurrent < lp.MinCurrent {
 		// kick off disable sequence


Test patch:
diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go
index 004d1c50..f46c3583 100644
--- a/core/loadpoint_test.go
+++ b/core/loadpoint_test.go
@@ -174,7 +174,7 @@ func TestUpdatePowerZero(t *testing.T) {
 	}
 }
 
-func TestPVHysteresisForStatusC(t *testing.T) {
+func TestPVHysteresis(t *testing.T) {
 	dt := time.Minute
 	type se struct {
 		site    float64
@@ -277,51 +277,54 @@ func TestPVHysteresisForStatusC(t *testing.T) {
 		}},
 	}
 
-	for _, tc := range tc {
-		t.Log(tc)
-
-		clck := clock.NewMock()
-		ctrl := gomock.NewController(t)
-		charger := mock.NewMockCharger(ctrl)
-
-		Voltage = 100
-		lp := &LoadPoint{
-			log:        util.NewLogger("foo"),
-			clock:      clck,
-			charger:    charger,
-			MinCurrent: minA,
-			MaxCurrent: maxA,
-			Phases:     10,
-			Enable: ThresholdConfig{
-				Threshold: tc.enable,
-				Delay:     dt,
-			},
-			Disable: ThresholdConfig{
-				Threshold: tc.disable,
-				Delay:     dt,
-			},
-		}
+	for _, status := range []api.ChargeStatus{api.StatusB, api.StatusC} {
+
+		for _, tc := range tc {
+			t.Log(tc)
+
+			clck := clock.NewMock()
+			ctrl := gomock.NewController(t)
+			charger := mock.NewMockCharger(ctrl)
+
+			Voltage = 100
+			lp := &LoadPoint{
+				log:        util.NewLogger("foo"),
+				clock:      clck,
+				charger:    charger,
+				MinCurrent: minA,
+				MaxCurrent: maxA,
+				Phases:     10,
+				Enable: ThresholdConfig{
+					Threshold: tc.enable,
+					Delay:     dt,
+				},
+				Disable: ThresholdConfig{
+					Threshold: tc.disable,
+					Delay:     dt,
+				},
+			}
 
-		// charging, otherwise PV mode logic is short-circuited
-		lp.status = api.StatusC
+			// charging, otherwise PV mode logic is short-circuited
+			lp.status = status
 
-		start := clck.Now()
+			start := clck.Now()
 
-		for step, se := range tc.series {
-			clck.Set(start.Add(se.delay))
+			for step, se := range tc.series {
+				clck.Set(start.Add(se.delay))
 
-			// maxCurrent will read actual current and enabled state in PV mode
-			// charger.EXPECT().Enabled().Return(tc.enabled, nil)
+				// maxCurrent will read actual current and enabled state in PV mode
+				// charger.EXPECT().Enabled().Return(tc.enabled, nil)
 
-			lp.enabled = tc.enabled
-			current := lp.pvMaxCurrent(api.ModePV, se.site)
+				lp.enabled = tc.enabled
+				current := lp.pvMaxCurrent(api.ModePV, se.site)
 
-			if current != se.current {
-				t.Errorf("step %d: wanted %d, got %d", step, se.current, current)
+				if current != se.current {
+					t.Errorf("step %d: wanted %d, got %d", step, se.current, current)
+				}
 			}
-		}
 
-		ctrl.Finish()
+			ctrl.Finish()
+		}
 	}
 }