Repository: evcc-io/evcc
Pull request #5973 (2023-01-31 22:10:28)

Substitute minSoc with minEnergy when soc not available
Fix https://github.com/evcc-io/evcc/issues/5972


Patch:
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 2d359c64..45742e3b 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -60,7 +60,7 @@ jobs:
         uses: docker/setup-buildx-action@v2
 
       - name: Publish
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         with:
           context: .
           platforms: linux/amd64,linux/arm64,linux/arm/v6
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7f038d87..9540c312 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -38,7 +38,7 @@ jobs:
             evcc/evcc
 
       - name: Publish
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         with:
           context: .
           platforms: linux/amd64,linux/arm64,linux/arm/v6
diff --git a/core/loadpoint.go b/core/loadpoint.go
index 97323691..4056db0a 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -734,11 +734,18 @@ func (lp *Loadpoint) targetSocReached() bool {
 }
 
 // minSocNotReached checks if minimum is configured and not reached.
-// If vehicle is not configured this will always return true
+// If vehicle is not configured this will always return false
 func (lp *Loadpoint) minSocNotReached() bool {
-	return lp.vehicle != nil &&
-		lp.Soc.min > 0 &&
-		lp.vehicleSoc < float64(lp.Soc.min)
+	if lp.vehicle == nil || lp.Soc.min == 0 {
+		return false
+	}
+
+	if lp.vehicleSoc != 0 {
+		return lp.vehicleSoc < float64(lp.Soc.min)
+	}
+
+	minEnergy := lp.vehicle.Capacity() * float64(lp.Soc.min) / 100 / soc.ChargeEfficiency
+	return minEnergy > 0 && lp.getChargedEnergy() < minEnergy
 }
 
 // climateActive checks if vehicle has active climate request


Test patch:
diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go
index 4fe17c42..f8c15d3e 100644
--- a/core/loadpoint_test.go
+++ b/core/loadpoint_test.go
@@ -12,6 +12,7 @@ import (
 	"github.com/evcc-io/evcc/push"
 	"github.com/evcc-io/evcc/util"
 	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
 )
 
 const (
@@ -738,36 +739,41 @@ func TestMinSoc(t *testing.T) {
 	vhc := mock.NewMockVehicle(ctrl)
 
 	tc := []struct {
-		vehicle api.Vehicle
+		vehicle *mock.MockVehicle
 		min     int
 		soc     float64
+		energy  float64
 		res     bool
 	}{
-		{nil, 0, 0, false},    // never reached without vehicle
-		{nil, 0, 10, false},   // never reached without vehicle
-		{nil, 80, 0, false},   // never reached without vehicle
-		{nil, 80, 80, false},  // never reached without vehicle
-		{nil, 80, 100, false}, // never reached without vehicle
-		{vhc, 0, 0, false},    // min disabled
-		{vhc, 0, 10, false},   // min disabled
-		{vhc, 80, 0, true},    // min not reached
-		{vhc, 80, 80, false},  // min reached
-		{vhc, 80, 100, false}, // min reached
+		{nil, 0, 0, 0, false},    // never reached without vehicle
+		{nil, 0, 10, 0, false},   // never reached without vehicle
+		{nil, 80, 0, 0, false},   // never reached without vehicle
+		{nil, 80, 80, 0, false},  // never reached without vehicle
+		{nil, 80, 100, 0, false}, // never reached without vehicle
+		{vhc, 0, 0, 0, false},    // min disabled
+		{vhc, 0, 10, 0, false},   // min disabled
+		{vhc, 80, 0, 0, true},    // min not reached
+		{vhc, 80, 10, 0, true},   // min not reached
+		{vhc, 80, 0, 8.0, true},  // min energy not reached
+		{vhc, 80, 0, 9.0, false}, // min energy reached
+		{vhc, 80, 80, 0, false},  // min reached
+		{vhc, 80, 100, 0, false}, // min reached
 	}
 
 	for _, tc := range tc {
-		t.Logf("%+v", tc)
-
 		lp := &Loadpoint{
-			vehicle: tc.vehicle,
 			Soc: SocConfig{
 				min: tc.min,
 			},
-			vehicleSoc: tc.soc,
+			vehicleSoc:    tc.soc,
+			chargedEnergy: tc.energy,
 		}
 
-		if res := lp.minSocNotReached(); tc.res != res {
-			t.Errorf("expected %v, got %v", tc.res, res)
+		if v := tc.vehicle; v != nil {
+			lp.vehicle = tc.vehicle // avoid assigning nil to interface
+			v.EXPECT().Capacity().Return(10.0).MaxTimes(1)
 		}
+
+		assert.Equal(t, tc.res, lp.minSocNotReached(), tc)
 	}
 }