Repository: andig/evcc
Pull request #55 (2020-04-26 13:32:08)

Make meters configurable


Patch:
diff --git a/cmd/config.go b/cmd/config.go
index f78aba34..ae40111c 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -6,6 +6,7 @@ import (
 	"github.com/andig/evcc/api"
 	"github.com/andig/evcc/charger"
 	"github.com/andig/evcc/core"
+	"github.com/andig/evcc/meter"
 	"github.com/andig/evcc/push"
 	"github.com/andig/evcc/server"
 	"github.com/andig/evcc/vehicle"
@@ -19,7 +20,7 @@ type config struct {
 	Influx     influxConfig
 	Menu       []server.MenuConfig
 	Messaging  messagingConfig
-	Meters     []namedConfig
+	Meters     []qualifiedConfig
 	Chargers   []qualifiedConfig
 	Vehicles   []qualifiedConfig
 	LoadPoints []core.Config
@@ -30,11 +31,6 @@ type qualifiedConfig struct {
 	Other      map[string]interface{} `mapstructure:",remain"`
 }
 
-type namedConfig struct {
-	Name  string
-	Other map[string]interface{} `mapstructure:",remain"`
-}
-
 type typedConfig struct {
 	Type  string
 	Other map[string]interface{} `mapstructure:",remain"`
@@ -102,7 +98,7 @@ func (c *ConfigProvider) configure(conf config) {
 func (c *ConfigProvider) configureMeters(conf config) {
 	c.meters = make(map[string]api.Meter)
 	for _, cc := range conf.Meters {
-		c.meters[cc.Name] = core.NewMeterFromConfig(log, cc.Other)
+		c.meters[cc.Name] = meter.NewFromConfig(log, cc.Type, cc.Other)
 	}
 }
 
diff --git a/core/api.go b/core/api.go
index bd24dcc0..5d07f153 100644
--- a/core/api.go
+++ b/core/api.go
@@ -154,7 +154,7 @@ func (lp *LoadPoint) publishSoC() {
 	if lp.connected() {
 		f, err := lp.vehicle.ChargeState()
 		if err == nil {
-			log.DEBUG.Printf("%s vehicle charge: %.1f%%", lp.Name, f)
+			log.DEBUG.Printf("%s vehicle soc: %.1f%%", lp.Name, f)
 			lp.publish("socCharge", f)
 			lp.publish("chargeEstimate", lp.remainingChargeDuration(f))
 			return
diff --git a/core/config.go b/core/config.go
deleted file mode 100644
index 44eb5bd0..00000000
--- a/core/config.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package core
-
-import (
-	"github.com/andig/evcc/api"
-	"github.com/andig/evcc/core/wrapper"
-	"github.com/andig/evcc/provider"
-)
-
-// NewMeterFromConfig creates api.Meter from config
-func NewMeterFromConfig(log *api.Logger, other map[string]interface{}) api.Meter {
-	cc := struct {
-		Power  provider.Config
-		Energy *provider.Config // optional
-	}{}
-	api.DecodeOther(log, other, &cc)
-
-	m := NewMeter(provider.NewFloatGetterFromConfig(log, cc.Power))
-
-	// decorate Meter with MeterEnergy
-	if cc.Energy != nil {
-		m = &wrapper.CompositeMeter{
-			Meter:       m,
-			MeterEnergy: NewMeterEnergy(provider.NewFloatGetterFromConfig(log, *cc.Energy)),
-		}
-	}
-
-	return m
-}
diff --git a/core/wrapper/chargemeter.go b/core/wrapper/chargemeter.go
index 1da95efd..d905a6d4 100644
--- a/core/wrapper/chargemeter.go
+++ b/core/wrapper/chargemeter.go
@@ -2,16 +2,8 @@ package wrapper
 
 import (
 	"sync"
-
-	"github.com/andig/evcc/api"
 )
 
-// CompositeMeter decorates a Meter with MeterEnergy.
-type CompositeMeter struct {
-	api.Meter
-	api.MeterEnergy
-}
-
 // ChargeMeter is a replacement for a physical charge meter.
 // It uses the charger's actual or max current to calculate power consumption.
 type ChargeMeter struct {
diff --git a/evcc.dist.yaml b/evcc.dist.yaml
index 6faa3bb9..ee27ebd3 100644
--- a/evcc.dist.yaml
+++ b/evcc.dist.yaml
@@ -44,16 +44,19 @@ messaging:
 # meters
 meters:
 - name: grid
+  type: default
   power: # power reading
     type: mqtt # use mqtt
     topic: mbmd/sdm1-1/Power # mqtt topic
     timeout: 10s # don't use older values
 - name: pv
+  type: default
   power:
     type: mqtt
     topic: mbmd/sdm1-2/Power
     timeout: 10s # don't use older values
 - name: charge
+  type: default
   power:
     type: script # use script
     cmd: /bin/sh -c "echo 0" # actual command
diff --git a/meter/config.go b/meter/config.go
new file mode 100644
index 00000000..cb9af6da
--- /dev/null
+++ b/meter/config.go
@@ -0,0 +1,21 @@
+package meter
+
+import (
+	"strings"
+
+	"github.com/andig/evcc/api"
+)
+
+// NewFromConfig creates meter from configuration
+func NewFromConfig(log *api.Logger, typ string, other map[string]interface{}) api.Meter {
+	var c api.Meter
+
+	switch strings.ToLower(typ) {
+	case "default", "configurable":
+		c = NewConfigurableFromConfig(log, other)
+	default:
+		log.FATAL.Fatalf("invalid meter type '%s'", typ)
+	}
+
+	return c
+}
diff --git a/core/meter.go b/meter/meter.go
similarity index 52%
rename from core/meter.go
rename to meter/meter.go
index d01a69ba..cad10614 100644
--- a/core/meter.go
+++ b/meter/meter.go
@@ -1,22 +1,49 @@
-package core
+package meter
 
 import (
 	"github.com/andig/evcc/api"
 	"github.com/andig/evcc/provider"
 )
 
-// Meter is an api.Meter implementation with configurable getters and setters.
-type Meter struct {
-	currentPowerG provider.FloatGetter
+// CompositeMeter decorates a Meter with MeterEnergy.
+type CompositeMeter struct {
+	api.Meter
+	api.MeterEnergy
+}
+
+// NewConfigurableFromConfig creates api.Meter from config
+func NewConfigurableFromConfig(log *api.Logger, other map[string]interface{}) api.Meter {
+	cc := struct {
+		Power  provider.Config
+		Energy *provider.Config // optional
+	}{}
+	api.DecodeOther(log, other, &cc)
+
+	m := NewConfigurable(provider.NewFloatGetterFromConfig(log, cc.Power))
+
+	// decorate Meter with MeterEnergy
+	if cc.Energy != nil {
+		m = &CompositeMeter{
+			Meter:       m,
+			MeterEnergy: NewMeterEnergy(provider.NewFloatGetterFromConfig(log, *cc.Energy)),
+		}
+	}
+
+	return m
 }
 
-// NewMeter creates a new charger
-func NewMeter(currentPowerG provider.FloatGetter) api.Meter {
+// NewConfigurable creates a new charger
+func NewConfigurable(currentPowerG provider.FloatGetter) api.Meter {
 	return &Meter{
 		currentPowerG: currentPowerG,
 	}
 }
 
+// Meter is an api.Meter implementation with configurable getters and setters.
+type Meter struct {
+	currentPowerG provider.FloatGetter
+}
+
 // CurrentPower implements the Meter.CurrentPower interface
 func (m *Meter) CurrentPower() (float64, error) {
 	return m.currentPowerG()


Test patch:
diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go
index 56d65e12..39059b70 100644
--- a/core/loadpoint_test.go
+++ b/core/loadpoint_test.go
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/andig/evcc/api"
+	"github.com/andig/evcc/meter"
 	"github.com/andig/evcc/mock"
 	"github.com/andig/evcc/provider"
 	"github.com/andig/evcc/push"
@@ -121,13 +122,13 @@ func TestMeterConfigurations(t *testing.T) {
 
 		var gm, pm, cm api.Meter
 		if tc.gm {
-			gm = NewMeter(fg)
+			gm = meter.NewConfigurable(fg)
 		}
 		if tc.cm {
-			cm = NewMeter(fg)
+			cm = meter.NewConfigurable(fg)
 		}
 		if tc.pm {
-			pm = NewMeter(fg)
+			pm = meter.NewConfigurable(fg)
 		}
 
 		ctrl := gomock.NewController(t)
diff --git a/core/wrapper/chargerater_test.go b/core/wrapper/chargerater_test.go
index b0e517a7..88c41b93 100644
--- a/core/wrapper/chargerater_test.go
+++ b/core/wrapper/chargerater_test.go
@@ -4,6 +4,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/andig/evcc/meter"
 	"github.com/andig/evcc/mock"
 	"github.com/benbjohnson/clock"
 	"github.com/golang/mock/gomock"
@@ -43,7 +44,7 @@ func TestWrappedMeter(t *testing.T) {
 
 	mm := mock.NewMockMeter(ctrl)
 	me := mock.NewMockMeterEnergy(ctrl)
-	cm := &CompositeMeter{mm, me}
+	cm := &meter.CompositeMeter{Meter: mm, MeterEnergy: me}
 
 	me.EXPECT().
 		TotalEnergy().