157 lines
3.8 KiB
Go
157 lines
3.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/andig/evcc/api"
|
|
"github.com/andig/evcc/charger"
|
|
"github.com/andig/evcc/core"
|
|
"github.com/andig/evcc/push"
|
|
"github.com/andig/evcc/vehicle"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().UnixNano())
|
|
}
|
|
|
|
func configureMessengers(conf messagingConfig) chan push.Event {
|
|
notificationChan := make(chan push.Event, 1)
|
|
notificationHub := push.NewHub(conf.Events)
|
|
|
|
for _, service := range conf.Services {
|
|
impl := push.NewMessengerFromConfig(service.Type, service.Other)
|
|
notificationHub.Add(impl)
|
|
}
|
|
|
|
go notificationHub.Run(notificationChan)
|
|
|
|
return notificationChan
|
|
}
|
|
|
|
func clientID() string {
|
|
pid := rand.Int31()
|
|
return fmt.Sprintf("evcc-%d", pid)
|
|
}
|
|
|
|
func configureMeters(conf config) (meters map[string]api.Meter) {
|
|
meters = make(map[string]api.Meter)
|
|
for _, cc := range conf.Meters {
|
|
meters[cc.Name] = core.NewMeterFromConfig(log, cc.Other)
|
|
}
|
|
return
|
|
}
|
|
|
|
func configureChargers(conf config) (chargers map[string]api.Charger) {
|
|
chargers = make(map[string]api.Charger)
|
|
for _, cc := range conf.Chargers {
|
|
chargers[cc.Name] = charger.NewFromConfig(log, cc.Type, cc.Other)
|
|
}
|
|
return
|
|
}
|
|
|
|
func configureVehicles(conf config) (vehicles map[string]api.Vehicle) {
|
|
vehicles = make(map[string]api.Vehicle)
|
|
for _, cc := range conf.Vehicles {
|
|
vehicles[cc.Name] = vehicle.NewFromConfig(log, cc.Type, cc.Other)
|
|
}
|
|
return
|
|
}
|
|
|
|
// TODO rewrite based on https://github.com/spf13/viper/pull/863
|
|
func configureLoadPoint(lp *core.LoadPoint, lpc loadPointConfig, subv *viper.Viper) {
|
|
// for _, key := range []string{"charger", "gridmeter", "pvmeter", "chargemeter", "soc"} {
|
|
// delete(kv, key)
|
|
// }
|
|
|
|
// config := &mapstructure.DecoderConfig{
|
|
// WeaklyTypedInput: true,
|
|
// DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
// Result: lp,
|
|
// }
|
|
|
|
// decoder, err := mapstructure.NewDecoder(config)
|
|
// if err != nil {
|
|
// log.FATAL.Fatalf("configuring loadpoints failed: %v", err)
|
|
// }
|
|
|
|
// if err := decoder.Decode(kv); err != nil {
|
|
// log.FATAL.Fatalf("configuring loadpoints failed: %v", err)
|
|
// }
|
|
|
|
// we can ignore the error here as UnmarshalExact has been called before
|
|
_ = subv.UnmarshalExact(lp)
|
|
|
|
if lpc.Mode != "" {
|
|
// workaround for golangs yaml off=0 conversion
|
|
if lpc.Mode == "0" {
|
|
lpc.Mode = api.ModeOff
|
|
}
|
|
lp.Mode = lpc.Mode // don't use SetMode here as that will block on channel send
|
|
}
|
|
}
|
|
|
|
func loadConfig(conf config, eventsChan chan push.Event) (loadPoints []*core.LoadPoint) {
|
|
meters := configureMeters(conf)
|
|
chargers := configureChargers(conf)
|
|
vehicles := configureVehicles(conf)
|
|
|
|
for idx, lpc := range conf.LoadPoints {
|
|
// configure loadpoint
|
|
lp := core.NewLoadPoint()
|
|
subv := viper.SubSlice("loadpoints")[idx]
|
|
configureLoadPoint(lp, lpc, subv)
|
|
|
|
// assign charger
|
|
if charger, ok := chargers[lpc.Charger]; ok {
|
|
lp.Charger = charger
|
|
} else {
|
|
log.FATAL.Fatalf("invalid charger '%s'", lpc.Charger)
|
|
}
|
|
|
|
// assign meters
|
|
for _, m := range []struct {
|
|
key string
|
|
meter *api.Meter
|
|
}{
|
|
{lpc.GridMeter, &lp.GridMeter},
|
|
{lpc.ChargeMeter, &lp.ChargeMeter},
|
|
{lpc.PVMeter, &lp.PVMeter},
|
|
} {
|
|
if m.key != "" {
|
|
if impl, ok := meters[m.key]; ok {
|
|
*m.meter = impl
|
|
} else {
|
|
log.FATAL.Fatalf("invalid meter '%s'", m.key)
|
|
}
|
|
}
|
|
}
|
|
|
|
// assign socs
|
|
if lpc.Vehicle != "" {
|
|
if impl, ok := vehicles[lpc.Vehicle]; ok {
|
|
lp.Vehicle = impl
|
|
} else {
|
|
log.FATAL.Fatalf("invalid vehicle '%s'", lpc.Vehicle)
|
|
}
|
|
}
|
|
|
|
loadPoints = append(loadPoints, lp)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func loadConfigFile(cfgFile string) (conf config) {
|
|
if cfgFile != "" {
|
|
log.INFO.Println("using config file", cfgFile)
|
|
if err := viper.UnmarshalExact(&conf); err != nil {
|
|
log.FATAL.Fatalf("config: failed parsing config file %s: %v", cfgFile, err)
|
|
}
|
|
} else {
|
|
log.FATAL.Fatal("missing evcc config")
|
|
}
|
|
return
|
|
}
|