diff --git a/charger/charger.go b/charger/charger.go index 343c699c4..e3d72fb03 100644 --- a/charger/charger.go +++ b/charger/charger.go @@ -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, diff --git a/charger/decoder.go b/charger/decoder.go new file mode 100644 index 000000000..3b4c3c3a2 --- /dev/null +++ b/charger/decoder.go @@ -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) + } +} diff --git a/charger/wallbe.go b/charger/wallbe.go index 9925c7fee..5db6ae4bd 100644 --- a/charger/wallbe.go +++ b/charger/wallbe.go @@ -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 == "" { diff --git a/cmd/config.go b/cmd/config.go index ba82a0807..56b5ea9dd 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -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 { diff --git a/cmd/config_providers.go b/cmd/config_providers.go deleted file mode 100644 index 03aaeaf09..000000000 --- a/cmd/config_providers.go +++ /dev/null @@ -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 -} diff --git a/cmd/setup.go b/cmd/setup.go index 2771cb63a..94915edcd 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -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) diff --git a/go.mod b/go.mod index 63ca2e5f5..d14a6856f 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index a86fbbb33..85bb231f4 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/provider/config.go b/provider/config.go new file mode 100644 index 000000000..b6781f044 --- /dev/null +++ b/provider/config.go @@ -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 +} diff --git a/push/push.go b/push/push.go index 6ae165457..e88fcb09d 100644 --- a/push/push.go +++ b/push/push.go @@ -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) } }