Refactor configuration handling (#12)

This commit is contained in:
andig 2020-03-22 17:58:44 +01:00 • committed by GitHub
parent 9a87f0d059
commit 6bf4b28902
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 147 deletions

View file

@ -13,6 +13,25 @@ type Charger struct {
maxCurrentS provider.IntSetter
}
type config struct {
Status, Enable, Enabled, MaxCurrent *provider.Config
}
// NewChargerFromConfig creates a new configurable charger
func NewChargerFromConfig(log *api.Logger, other map[string]interface{}) api.Charger {
var cc config
decodeOther(log, other, &cc)
charger := NewCharger(
provider.NewStringGetterFromConfig(cc.Status),
provider.NewBoolGetterFromConfig(cc.Enabled),
provider.NewBoolSetterFromConfig("enable", cc.Enable),
provider.NewIntSetterFromConfig("current", cc.MaxCurrent),
)
return charger
}
// NewCharger creates a new charger
func NewCharger(
statusG provider.StringGetter,

24
charger/decoder.go Normal file
View file

@ -0,0 +1,24 @@
package charger
import (
"github.com/andig/evcc/api"
"github.com/mitchellh/mapstructure"
)
func decodeOther(log *api.Logger, other map[string]interface{}, cc interface{}) {
decoderConfig := &mapstructure.DecoderConfig{
Result: cc,
ErrorUnused: true,
WeaklyTypedInput: true,
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
log.FATAL.Fatal(err)
}
if err := decoder.Decode(other); err != nil {
log.FATAL.Fatal(err)
}
}

View file

@ -39,6 +39,18 @@ type Wallbe struct {
handler *modbus.TCPClientHandler
}
type wallbeConfig struct {
URI string
}
// NewWallbeFromConfig creates a Wallbe charger from generic config
func NewWallbeFromConfig(log *api.Logger, other map[string]interface{}) api.Charger {
var cc wallbeConfig
decodeOther(log, other, &cc)
return NewWallbe(cc.URI)
}
// NewWallbe creates a Wallbe charger
func NewWallbe(conn string) api.Charger {
if conn == "" {

View file

@ -4,6 +4,7 @@ import (
"time"
"github.com/andig/evcc/api"
"github.com/andig/evcc/provider"
"github.com/andig/evcc/push"
"github.com/andig/evcc/server"
)
@ -42,42 +43,25 @@ type influxConfig struct {
Interval time.Duration
}
type providerConfig struct {
Type string
Topic string
Cmd string
Multiplier float64
Timeout time.Duration
Cache time.Duration
}
type meterConfig struct {
Name string
Type string
Power *providerConfig
Energy *providerConfig
Power *provider.Config
Energy *provider.Config
}
type chargerConfig struct {
Name string
Type string
// wallbe charger
URI string
// composite charger
Status *providerConfig // Charger
MaxCurrent *providerConfig // ChargeController
Enable *providerConfig // Charger
Enabled *providerConfig // Charger
Name string
Type string
Other map[string]interface{} `mapstructure:",remain"`
}
type socConfig struct {
Name string
Title string
Type string
Title string
Capacity int64
Charge *providerConfig
Charge *provider.Config
}
type loadPointConfig struct {

View file

@ -1,105 +0,0 @@
package cmd
import (
"time"
"github.com/andig/evcc/provider"
)
const (
execTimeout = 5 * time.Second
mqttTimeout = 5 * time.Second
)
func stringGetter(pc *providerConfig) (res provider.StringGetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := provider.NewScriptProvider(pc.Timeout)
res = exec.StringGetter(pc.Cmd)
default:
log.FATAL.Printf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = provider.NewCacheGetter(res, pc.Cache).StringGetter
}
return
}
func boolGetter(pc *providerConfig) (res provider.BoolGetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := provider.NewScriptProvider(pc.Timeout)
res = exec.BoolGetter(pc.Cmd)
default:
log.FATAL.Printf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = provider.NewCacheGetter(res, pc.Cache).BoolGetter
}
return
}
func floatGetter(pc *providerConfig) (res provider.FloatGetter) {
switch pc.Type {
case "mqtt":
if pc.Timeout == 0 {
pc.Timeout = mqttTimeout
}
if pc.Multiplier == 0 {
pc.Multiplier = 1
}
res = mq.FloatGetter(pc.Topic, pc.Multiplier, pc.Timeout)
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := provider.NewScriptProvider(pc.Timeout)
res = exec.FloatGetter(pc.Cmd)
default:
log.FATAL.Printf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = provider.NewCacheGetter(res, pc.Cache).FloatGetter
}
return
}
func boolSetter(param string, pc *providerConfig) (res provider.BoolSetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := provider.NewScriptProvider(pc.Timeout)
res = exec.BoolSetter(param, pc.Cmd)
default:
log.FATAL.Printf("invalid setter type %s", pc.Type)
}
return
}
func intSetter(param string, pc *providerConfig) (res provider.IntSetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := provider.NewScriptProvider(pc.Timeout)
res = exec.IntSetter(param, pc.Cmd)
default:
log.FATAL.Printf("invalid setter type %s", pc.Type)
}
return
}

View file

@ -18,9 +18,6 @@ func init() {
rand.Seed(time.Now().UnixNano())
}
// MQTT singleton
var mq *provider.MqttClient
func clientID() string {
pid := rand.Int31()
return fmt.Sprintf("evcc-%d", pid)
@ -29,13 +26,13 @@ func clientID() string {
func configureMeters(conf config) (meters map[string]api.Meter) {
meters = make(map[string]api.Meter)
for _, mc := range conf.Meters {
m := core.NewMeter(floatGetter(mc.Power))
m := core.NewMeter(provider.NewFloatGetterFromConfig(mc.Power))
// decorate Meter with MeterEnergy
if mc.Energy != nil {
m = &wrapper.CompositeMeter{
Meter: m,
MeterEnergy: core.NewMeterEnergy(floatGetter(mc.Energy)),
MeterEnergy: core.NewMeterEnergy(provider.NewFloatGetterFromConfig(mc.Energy)),
}
}
meters[mc.Name] = m
@ -50,16 +47,9 @@ func configureChargers(conf config) (chargers map[string]api.Charger) {
switch cc.Type {
case "wallbe":
c = charger.NewWallbe(cc.URI)
c = charger.NewWallbeFromConfig(log, cc.Other)
case "default", "configurable":
c = charger.NewCharger(
stringGetter(cc.Status),
boolGetter(cc.Enabled),
boolSetter("enable", cc.Enable),
intSetter("current", cc.MaxCurrent),
)
c = charger.NewChargerFromConfig(log, cc.Other)
default:
log.FATAL.Fatalf("invalid charger type '%s'", cc.Type)
}
@ -72,7 +62,7 @@ func configureChargers(conf config) (chargers map[string]api.Charger) {
func configureSoCs(conf config) (socs map[string]api.SoC) {
socs = make(map[string]api.SoC)
for _, sc := range conf.SoCs {
soc := core.NewSoC(sc.Capacity, sc.Title, floatGetter(sc.Charge))
soc := core.NewSoC(sc.Capacity, sc.Title, provider.NewFloatGetterFromConfig(sc.Charge))
socs[sc.Name] = soc
}
return
@ -113,7 +103,7 @@ func configureLoadPoint(lp *core.LoadPoint, lpc loadPointConfig, subv *viper.Vip
func loadConfig(conf config, eventsChan chan push.Event) (loadPoints []*core.LoadPoint) {
if viper.Get("mqtt") != nil {
mq = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1)
provider.MQTT = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1)
}
meters := configureMeters(conf)

1
go.mod
View file

@ -19,6 +19,7 @@ require (
github.com/hashicorp/go-version v1.2.0 // indirect
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mitchellh/mapstructure v1.2.2
github.com/mjibson/esc v0.2.0
github.com/pelletier/go-toml v1.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect

2
go.sum
View file

@ -164,6 +164,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mjibson/esc v0.2.0 h1:k96hdaR9Z+nMcnDwNrOvhdBqtjyMrbVyxLpsRCdP2mA=
github.com/mjibson/esc v0.2.0/go.mod h1:9Hw9gxxfHulMF5OJKCyhYD7PzlSdhzXyaGEBRPH1OPs=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

121
provider/config.go Normal file
View file

@ -0,0 +1,121 @@
package provider
import (
"time"
)
const (
execTimeout = 5 * time.Second
mqttTimeout = 5 * time.Second
)
// Config contains the getter/setter configuration
type Config struct {
Type string
Topic string
Cmd string
Multiplier float64
Timeout time.Duration
Cache time.Duration
}
// MQTT singleton
var MQTT *MqttClient
// NewStringGetterFromConfig creates a StringGetter from config
func NewStringGetterFromConfig(pc *Config) (res StringGetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.StringGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCacheGetter(res, pc.Cache).StringGetter
}
return
}
// NewBoolGetterFromConfig creates a BoolGetter from config
func NewBoolGetterFromConfig(pc *Config) (res BoolGetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.BoolGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCacheGetter(res, pc.Cache).BoolGetter
}
return
}
// NewFloatGetterFromConfig creates a FloatGetter from config
func NewFloatGetterFromConfig(pc *Config) (res FloatGetter) {
switch pc.Type {
case "mqtt":
if pc.Timeout == 0 {
pc.Timeout = mqttTimeout
}
if pc.Multiplier == 0 {
pc.Multiplier = 1
}
res = MQTT.FloatGetter(pc.Topic, pc.Multiplier, pc.Timeout)
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.FloatGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCacheGetter(res, pc.Cache).FloatGetter
}
return
}
// NewBoolSetterFromConfig creates a BoolSetter from config
func NewBoolSetterFromConfig(param string, pc *Config) (res BoolSetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.BoolSetter(param, pc.Cmd)
default:
log.FATAL.Fatalf("invalid setter type %s", pc.Type)
}
return
}
// NewIntSetterFromConfig creates a IntSetter from config
func NewIntSetterFromConfig(param string, pc *Config) (res IntSetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.IntSetter(param, pc.Cmd)
default:
log.FATAL.Fatalf("invalid setter type %s", pc.Type)
}
return
}

View file

@ -2,7 +2,7 @@ package push
// EventTemplate is the push message template for an event
type EventTemplate struct {
title, msg string
Title, Msg string
}
// Hub subscribes to event notifications and sends them to client devices
@ -33,6 +33,6 @@ func (h *Hub) Run(events <-chan Event) {
break
}
go h.pushOver.Send(ev, definition.title, definition.msg)
go h.pushOver.Send(ev, definition.Title, definition.Msg)
}
}