Make meters configurable (#55)

This commit is contained in:
andig 2020-04-26 15:40:41 +02:00 • committed by GitHub
parent fb67ec99cc
commit a1bb0dc594
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 67 additions and 54 deletions

View file

@ -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)
}
}

View file

@ -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

View file

@ -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
}

View file

@ -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)

View file

@ -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 {

View file

@ -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().

View file

@ -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

21
meter/config.go Normal file
View file

@ -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
}

View file

@ -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
}
// NewMeter creates a new charger
func NewMeter(currentPowerG provider.FloatGetter) api.Meter {
// 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
}
// 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()