From dde26c79403f852f9587330388dfcdde49b23fa2 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 15 Apr 2020 13:30:34 +0200 Subject: [PATCH] Add charger, meter and vehicle commands (#38) --- cmd/charger.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/dump.go | 43 ++++++++++++++++++++++++++++++++ cmd/meter.go | 54 +++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 66 +++++++++++++++++++++++++------------------------- cmd/setup.go | 17 +++++++++---- cmd/vehicle.go | 60 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 38 deletions(-) create mode 100644 cmd/charger.go create mode 100644 cmd/dump.go create mode 100644 cmd/meter.go create mode 100644 cmd/vehicle.go diff --git a/cmd/charger.go b/cmd/charger.go new file mode 100644 index 000000000..6c36ff12a --- /dev/null +++ b/cmd/charger.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "fmt" + + "github.com/andig/evcc/provider" + "github.com/andig/evcc/server" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// chargerCmd represents the charger command +var chargerCmd = &cobra.Command{ + Use: "charger [name]", + Short: "Query configured chargers", + Run: runCharger, +} + +func init() { + cobra.OnInitialize(initConfig) + rootCmd.AddCommand(chargerCmd) + configureCommand(chargerCmd) +} + +func runCharger(cmd *cobra.Command, args []string) { + level, _ := cmd.PersistentFlags().GetString("log") + configureLogging(level) + log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit) + + // load config + conf := loadConfigFile(cfgFile) + + // setup mqtt + if viper.Get("mqtt") != nil { + provider.MQTT = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1) + } + + chargers := configureChargers(conf) + + for name, v := range chargers { + if len(args) == 1 { + if target := args[0]; name != target { + if _, ok := chargers[target]; !ok { + log.FATAL.Fatalf("charger not found: %s", target) + } + continue + } + } else if len(chargers) != 1 { + fmt.Println(name) + } + + if status, err := v.Status(); err != nil { + fmt.Printf("Status: %v\n", err) + } else { + fmt.Printf("Status: %s\n", status) + } + + if enabled, err := v.Enabled(); err != nil { + fmt.Printf("Enabled: %v\n", err) + } else { + fmt.Printf("Enabled: %s\n", truefalse[enabled]) + } + + dumpAPIs(v) + } +} diff --git a/cmd/dump.go b/cmd/dump.go new file mode 100644 index 000000000..30b5debff --- /dev/null +++ b/cmd/dump.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "fmt" + + "github.com/andig/evcc/api" +) + +var truefalse = map[bool]string{false: "false", true: "true"} + +func dumpAPIs(v interface{}) { + if v, ok := v.(api.Meter); ok { + if power, err := v.CurrentPower(); err != nil { + fmt.Printf("Power: %v\n", err) + } else { + fmt.Printf("Power: %.0fW\n", power) + } + } + + if v, ok := v.(api.MeterEnergy); ok { + if energy, err := v.TotalEnergy(); err != nil { + fmt.Printf("Energy: %v\n", err) + } else { + fmt.Printf("Energy: %.0fkWh\n", energy) + } + } + + if v, ok := v.(api.ChargeRater); ok { + if energy, err := v.ChargedEnergy(); err != nil { + fmt.Printf("Charged: %v\n", err) + } else { + fmt.Printf("Charged: %.0fkWh\n", energy) + } + } + + if v, ok := v.(api.ChargeTimer); ok { + if duration, err := v.ChargingTime(); err != nil { + fmt.Printf("Duration: %v\n", err) + } else { + fmt.Printf("Duration: %v\n", duration) + } + } +} diff --git a/cmd/meter.go b/cmd/meter.go new file mode 100644 index 000000000..6f930fada --- /dev/null +++ b/cmd/meter.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "fmt" + + "github.com/andig/evcc/provider" + "github.com/andig/evcc/server" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// meterCmd represents the meter command +var meterCmd = &cobra.Command{ + Use: "meter [name]", + Short: "Query configured meters", + Run: runMeter, +} + +func init() { + cobra.OnInitialize(initConfig) + rootCmd.AddCommand(meterCmd) + configureCommand(meterCmd) +} + +func runMeter(cmd *cobra.Command, args []string) { + level, _ := cmd.PersistentFlags().GetString("log") + configureLogging(level) + log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit) + + // load config + conf := loadConfigFile(cfgFile) + + // setup mqtt + if viper.Get("mqtt") != nil { + provider.MQTT = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1) + } + + meters := configureMeters(conf) + + for name, v := range meters { + if len(args) == 1 { + if target := args[0]; name != target { + if _, ok := meters[target]; !ok { + log.FATAL.Fatalf("meter not found: %s", target) + } + continue + } + } else if len(meters) != 1 { + fmt.Println(name) + } + + dumpAPIs(v) + } +} diff --git a/cmd/root.go b/cmd/root.go index 88f523a95..78c680460 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,6 +7,7 @@ import ( "github.com/andig/evcc/api" "github.com/andig/evcc/core" + "github.com/andig/evcc/provider" "github.com/andig/evcc/server" "github.com/spf13/cobra" @@ -26,46 +27,49 @@ var rootCmd = &cobra.Command{ Run: run, } -func bind(flag string) { - if err := viper.BindPFlag(flag, rootCmd.PersistentFlags().Lookup(flag)); err != nil { +func bind(cmd *cobra.Command, flag string) { + if err := viper.BindPFlag(flag, cmd.PersistentFlags().Lookup(flag)); err != nil { panic(err) } } +func configureCommand(cmd *cobra.Command) { + cmd.PersistentFlags().StringP( + "log", "l", + "error", + "Log level (fatal, error, warn, info, debug, trace)", + ) + bind(cmd, "log") + + cmd.PersistentFlags().StringVarP(&cfgFile, + "config", "c", + "", + "Config file (default \"~/evcc.yaml\" or \"/etc/evcc.yaml\")", + ) + cmd.PersistentFlags().BoolP( + "help", "h", + false, + "Help for "+cmd.Name(), + ) +} + func init() { cobra.OnInitialize(initConfig) + configureCommand(rootCmd) rootCmd.PersistentFlags().StringP( "uri", "u", "0.0.0.0:7070", "Listen address", ) - bind("uri") - - rootCmd.PersistentFlags().StringP( - "log", "l", - "info", - "Log level (fatal, error, warn, info, debug, trace)", - ) - bind("log") + bind(rootCmd, "uri") rootCmd.PersistentFlags().DurationP( "interval", "i", 10*time.Second, "Update interval", ) - bind("interval") - - rootCmd.PersistentFlags().StringVarP(&cfgFile, - "config", "c", - "", - "Config file (default \"~/evcc.yaml\" or \"/etc/evcc.yaml\")", - ) - rootCmd.PersistentFlags().BoolP( - "help", "h", - false, - "Help for "+rootCmd.Name(), - ) + bind(rootCmd, "interval") } // initConfig reads in config file and ENV variables if set @@ -158,17 +162,8 @@ func run(cmd *cobra.Command, args []string) { configureLogging(level) log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit) - var 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") - } - - // re-configure after reading config file + // load config and re-configure logging after reading config file + conf := loadConfigFile(cfgFile) configureLogging(conf.Log) go checkVersion() @@ -179,6 +174,11 @@ func run(cmd *cobra.Command, args []string) { // setup messaging notificationChan := configureMessengers(conf.Messaging) + // setup mqtt + if viper.Get("mqtt") != nil { + provider.MQTT = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1) + } + // setup loadpoints loadPoints := loadConfig(conf, notificationChan) diff --git a/cmd/setup.go b/cmd/setup.go index d416d2344..2c78c629f 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -8,7 +8,6 @@ import ( "github.com/andig/evcc/api" "github.com/andig/evcc/charger" "github.com/andig/evcc/core" - "github.com/andig/evcc/provider" "github.com/andig/evcc/push" "github.com/andig/evcc/vehicle" "github.com/spf13/viper" @@ -95,10 +94,6 @@ 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 { - provider.MQTT = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1) - } - meters := configureMeters(conf) chargers := configureChargers(conf) vehicles := configureVehicles(conf) @@ -148,3 +143,15 @@ func loadConfig(conf config, eventsChan chan push.Event) (loadPoints []*core.Loa 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 +} diff --git a/cmd/vehicle.go b/cmd/vehicle.go new file mode 100644 index 000000000..a7b6526c3 --- /dev/null +++ b/cmd/vehicle.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "fmt" + + "github.com/andig/evcc/provider" + "github.com/andig/evcc/server" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// vehicleCmd represents the vehicle command +var vehicleCmd = &cobra.Command{ + Use: "vehicle [name]", + Short: "Query configured vehicles", + Run: runVehicle, +} + +func init() { + cobra.OnInitialize(initConfig) + rootCmd.AddCommand(vehicleCmd) + configureCommand(vehicleCmd) +} + +func runVehicle(cmd *cobra.Command, args []string) { + level, _ := cmd.PersistentFlags().GetString("log") + configureLogging(level) + log.INFO.Printf("evcc %s (%s)", server.Version, server.Commit) + + // load config + conf := loadConfigFile(cfgFile) + + // setup mqtt + if viper.Get("mqtt") != nil { + provider.MQTT = provider.NewMqttClient(conf.Mqtt.Broker, conf.Mqtt.User, conf.Mqtt.Password, clientID(), 1) + } + + vehicles := configureVehicles(conf) + + for name, v := range vehicles { + if len(args) == 1 { + if target := args[0]; name != target { + if _, ok := vehicles[target]; !ok { + log.FATAL.Fatalf("charger not found: %s", target) + } + continue + } + } else if len(vehicles) != 1 { + fmt.Println(name) + } + + if soc, err := v.ChargeState(); err != nil { + fmt.Printf("State: %v\n", err) + } else { + fmt.Printf("State: %.0f%%\n", soc) + } + + dumpAPIs(v) + } +}