Improve network configuration (#3155)
This commit is contained in:
parent
741cfce027
commit
1a4da3f7ca
16 changed files with 105 additions and 70 deletions
|
|
@ -38,8 +38,7 @@ func runCharger(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s", server.FormattedVersion())
|
||||
|
||||
// load config
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
if err := loadConfigFile(cfgFile, &conf); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -18,10 +20,12 @@ import (
|
|||
"github.com/evcc-io/evcc/vehicle"
|
||||
"github.com/evcc-io/evcc/vehicle/wrapper"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
URI string
|
||||
URI interface{} // TODO deprecated
|
||||
Network networkConfig
|
||||
Log string
|
||||
SponsorToken string
|
||||
Metrics bool
|
||||
|
|
@ -42,6 +46,20 @@ type config struct {
|
|||
LoadPoints []map[string]interface{}
|
||||
}
|
||||
|
||||
type networkConfig struct {
|
||||
Schema string
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
|
||||
func (c networkConfig) HostPort() string {
|
||||
return net.JoinHostPort(c.Host, strconv.Itoa(c.Port))
|
||||
}
|
||||
|
||||
func (c networkConfig) URI() string {
|
||||
return fmt.Sprintf("%s://%s", c.Schema, c.HostPort())
|
||||
}
|
||||
|
||||
type mqttConfig struct {
|
||||
mqtt.Config `mapstructure:",squash"`
|
||||
Topic string
|
||||
|
|
@ -218,9 +236,7 @@ func (cp *ConfigProvider) configureVehicles(conf config) error {
|
|||
}
|
||||
|
||||
// webControl handles routing for devices. For now only api.ProviderLogin related routes
|
||||
func (cp *ConfigProvider) webControl(httpd *server.HTTPd, paramC chan<- util.Param) {
|
||||
router := httpd.Router()
|
||||
|
||||
func (cp *ConfigProvider) webControl(conf networkConfig, router *mux.Router, paramC chan<- util.Param) {
|
||||
auth := router.PathPrefix("/oauth").Subrouter()
|
||||
auth.Use(handlers.CompressHandler)
|
||||
auth.Use(handlers.CORS(
|
||||
|
|
@ -233,9 +249,8 @@ func (cp *ConfigProvider) webControl(httpd *server.HTTPd, paramC chan<- util.Par
|
|||
// initialize
|
||||
cp.auth = util.NewAuthCollection(paramC)
|
||||
|
||||
// TODO make evccURI configurable, add warnings for any network/ localhost
|
||||
evccURI := fmt.Sprintf("http://%s", httpd.Addr)
|
||||
baseAuthURI := fmt.Sprintf("%s/oauth", evccURI)
|
||||
baseURI := conf.URI()
|
||||
baseAuthURI := fmt.Sprintf("%s/oauth", baseURI)
|
||||
|
||||
var id int
|
||||
for _, v := range cp.vehicles {
|
||||
|
|
@ -245,14 +260,10 @@ func (cp *ConfigProvider) webControl(httpd *server.HTTPd, paramC chan<- util.Par
|
|||
basePath := fmt.Sprintf("vehicles/%d", id)
|
||||
callbackURI := fmt.Sprintf("%s/%s/callback", baseAuthURI, basePath)
|
||||
|
||||
// replace interface designator with address
|
||||
// TODO fix when evccURI becomes configurable
|
||||
callbackURI = strings.ReplaceAll(callbackURI, "0.0.0.0", "localhost")
|
||||
|
||||
// register vehicle
|
||||
ap := cp.auth.Register(fmt.Sprintf("oauth/%s", basePath), v.Title())
|
||||
|
||||
provider.SetCallbackParams(evccURI, callbackURI, ap.Handler())
|
||||
provider.SetCallbackParams(baseURI, callbackURI, ap.Handler())
|
||||
|
||||
auth.
|
||||
Methods(http.MethodPost).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,18 @@
|
|||
uri: 0.0.0.0:7070 # uri for ui
|
||||
interval: 10s # control cycle interval
|
||||
# open evcc at http://evcc.local:7070
|
||||
network:
|
||||
schema: http
|
||||
host: evcc.local # .local suffix announces the hostname on MDNS
|
||||
port: 7070
|
||||
|
||||
log: info
|
||||
levels:
|
||||
cache: error
|
||||
|
||||
interval: 10s # control cycle interval
|
||||
{{- if ne (len .SponsorToken) 0 }}
|
||||
|
||||
sponsortoken: {{ .SponsorToken }}
|
||||
{{- end}}
|
||||
{{- if ne (len .Meters) 0 }}
|
||||
|
||||
meters:
|
||||
|
|
@ -74,7 +85,3 @@ hems:
|
|||
eebus:
|
||||
{{ .EEBUS | indent 2 }}
|
||||
{{- end }}
|
||||
{{- if ne (len .SponsorToken) 0 }}
|
||||
|
||||
sponsortoken: {{ .SponsorToken }}
|
||||
{{- end}}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package cmd
|
|||
|
||||
import (
|
||||
_ "embed" // for yaml
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
|
@ -11,7 +10,7 @@ import (
|
|||
//go:embed demo.yaml
|
||||
var demoYaml string
|
||||
|
||||
func demoConfig() (conf config) {
|
||||
func demoConfig(conf *config) {
|
||||
demo := map[string]interface{}{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(demoYaml), &demo); err != nil {
|
||||
|
|
@ -22,12 +21,9 @@ func demoConfig() (conf config) {
|
|||
viper.Set(k, v)
|
||||
}
|
||||
|
||||
// demo port
|
||||
viper.Set("uri", fmt.Sprintf("0.0.0.0:%d", defaultPort))
|
||||
|
||||
if err := viper.UnmarshalExact(&conf); err != nil {
|
||||
log.FATAL.Fatalf("failed loading demo config: %v", err)
|
||||
}
|
||||
|
||||
return conf
|
||||
conf.Network.Port = defaultPort
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
network:
|
||||
port: 7070
|
||||
|
||||
log: info
|
||||
|
||||
interval: 3s
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ func runDump(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s", server.FormattedVersion())
|
||||
|
||||
// load config
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
if err := loadConfigFile(cfgFile, &conf); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ func runMeter(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s", server.FormattedVersion())
|
||||
|
||||
// load config
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
if err := loadConfigFile(cfgFile, &conf); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
51
cmd/root.go
51
cmd/root.go
|
|
@ -2,12 +2,11 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // pprof handler
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
|
@ -34,6 +33,14 @@ var (
|
|||
ignoreMqtt = []string{"auth", "releaseNotes"} // excessive size may crash certain brokers
|
||||
)
|
||||
|
||||
var conf = config{
|
||||
Network: networkConfig{
|
||||
Schema: "http",
|
||||
Host: "evcc.local",
|
||||
Port: 7070,
|
||||
},
|
||||
}
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "evcc",
|
||||
|
|
@ -72,8 +79,10 @@ func init() {
|
|||
cobra.OnInitialize(initConfig)
|
||||
configureCommand(rootCmd)
|
||||
|
||||
rootCmd.PersistentFlags().StringP("uri", "u", "0.0.0.0:7070", "Listen address")
|
||||
bind(rootCmd, "uri")
|
||||
rootCmd.PersistentFlags().IntP("port", "p", 7070, "Listen port")
|
||||
if err := viper.BindPFlag("network.port", rootCmd.PersistentFlags().Lookup("port")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().DurationP("interval", "i", 10*time.Second, "Update interval")
|
||||
bind(rootCmd, "interval")
|
||||
|
|
@ -134,16 +143,23 @@ func run(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s", server.FormattedVersion())
|
||||
|
||||
// load config and re-configure logging after reading config file
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
if err := loadConfigFile(cfgFile, &conf); err != nil {
|
||||
log.ERROR.Println("missing evcc config - switching into demo mode")
|
||||
conf = demoConfig()
|
||||
demoConfig(&conf)
|
||||
}
|
||||
|
||||
util.LogLevel(viper.GetString("log"), viper.GetStringMapString("levels"))
|
||||
|
||||
uri := viper.GetString("uri")
|
||||
log.INFO.Println("listening at", uri)
|
||||
// network config
|
||||
if viper.GetString("uri") != "" {
|
||||
log.ERROR.Println("`uri` is deprecated and will be ignored. Use `network` instead.")
|
||||
}
|
||||
|
||||
if cmd.PersistentFlags().Lookup("port").Changed {
|
||||
conf.Network.Port = viper.GetInt("network.port")
|
||||
}
|
||||
|
||||
log.INFO.Printf("listening at :%d", conf.Network.Port)
|
||||
|
||||
// setup environment
|
||||
if err := configureEnvironment(conf); err != nil {
|
||||
|
|
@ -183,16 +199,15 @@ func run(cmd *cobra.Command, args []string) {
|
|||
|
||||
// create webserver
|
||||
socketHub := server.NewSocketHub()
|
||||
httpd := server.NewHTTPd(uri, site, socketHub, cache)
|
||||
httpd := server.NewHTTPd(fmt.Sprintf(":%d", conf.Network.Port), site, socketHub, cache)
|
||||
|
||||
// announce webserver on mDNS
|
||||
if _, port, err := net.SplitHostPort(uri); err == nil {
|
||||
if portInt, err := strconv.Atoi(port); err == nil {
|
||||
if zc, err := zeroconf.RegisterProxy("evcc Website", "_http._tcp", "local.", portInt, "evcc", nil, []string{}, nil); err == nil {
|
||||
shutdown.Register(zc.Shutdown)
|
||||
} else {
|
||||
log.ERROR.Printf("mDNS announcement: %s", err)
|
||||
}
|
||||
if strings.HasSuffix(conf.Network.Host, ".local") {
|
||||
host := strings.TrimSuffix(conf.Network.Host, ".local")
|
||||
if zc, err := zeroconf.RegisterProxy("EV Charge Controller", "_http._tcp", "local.", conf.Network.Port, host, nil, []string{}, nil); err == nil {
|
||||
shutdown.Register(zc.Shutdown)
|
||||
} else {
|
||||
log.ERROR.Printf("mDNS announcement: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +240,7 @@ func run(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
|
||||
// allow web access for vehicles
|
||||
cp.webControl(httpd, valueChan)
|
||||
cp.webControl(conf.Network, httpd.Router(), valueChan)
|
||||
|
||||
// version check
|
||||
go updater.Run(log, httpd, tee, valueChan)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func init() {
|
|||
|
||||
var cp = new(ConfigProvider)
|
||||
|
||||
func loadConfigFile(cfgFile string) (conf config, err error) {
|
||||
func loadConfigFile(cfgFile string, conf *config) (err error) {
|
||||
if cfgFile != "" {
|
||||
log.INFO.Println("using config file", cfgFile)
|
||||
if err := viper.UnmarshalExact(&conf); err != nil {
|
||||
|
|
@ -41,7 +41,7 @@ func loadConfigFile(cfgFile string) (conf config, err error) {
|
|||
err = errors.New("missing evcc config")
|
||||
}
|
||||
|
||||
return conf, err
|
||||
return err
|
||||
}
|
||||
|
||||
func configureEnvironment(conf config) (err error) {
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ func runToken(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s", server.FormattedVersion())
|
||||
|
||||
// load config
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
if err := loadConfigFile(cfgFile, &conf); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +54,7 @@ func runToken(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
|
||||
var token *oauth2.Token
|
||||
var err error
|
||||
|
||||
switch strings.ToLower(vehicleConf.Type) {
|
||||
case "tesla":
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -133,13 +132,8 @@ func tronityToken(conf config, vehicleConf qualifiedConfig) (*oauth2.Token, erro
|
|||
}
|
||||
|
||||
if oc.RedirectURL = cc.RedirectURI; oc.RedirectURL == "" {
|
||||
_, port, err := net.SplitHostPort(conf.URI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oc.RedirectURL = fmt.Sprintf("http://%s/auth/tronity", net.JoinHostPort("localhost", port))
|
||||
oc.RedirectURL = fmt.Sprintf("%s/auth/tronity", conf.Network.URI())
|
||||
}
|
||||
|
||||
return tronityAuthorize(conf.URI, oc)
|
||||
return tronityAuthorize(conf.Network.HostPort(), oc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ func runVehicle(cmd *cobra.Command, args []string) {
|
|||
log.INFO.Printf("evcc %s", server.FormattedVersion())
|
||||
|
||||
// load config
|
||||
conf, err := loadConfigFile(cfgFile)
|
||||
if err != nil {
|
||||
if err := loadConfigFile(cfgFile, &conf); err != nil {
|
||||
log.FATAL.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ func (site *Site) DumpConfig() {
|
|||
|
||||
if len(site.pvMeters) > 0 {
|
||||
for i, pv := range site.pvMeters {
|
||||
site.log.INFO.Println(meterCapabilities(fmt.Sprintf("pv %d", i), pv))
|
||||
site.log.INFO.Println(meterCapabilities(fmt.Sprintf("pv %d", i+1), pv))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ func (site *Site) DumpConfig() {
|
|||
for i, battery := range site.batteryMeters {
|
||||
_, ok := battery.(api.Battery)
|
||||
site.log.INFO.Println(
|
||||
meterCapabilities(fmt.Sprintf("battery %d", i), battery),
|
||||
meterCapabilities(fmt.Sprintf("battery %d", i+1), battery),
|
||||
fmt.Sprintf("soc %s", presence[ok]),
|
||||
)
|
||||
}
|
||||
|
|
@ -217,7 +217,7 @@ func (site *Site) DumpConfig() {
|
|||
_, status := v.(api.ChargeState)
|
||||
_, climate := v.(api.VehicleClimater)
|
||||
lp.log.INFO.Printf(" vehicle %d: range %s finish %s status %s climate %s",
|
||||
i, presence[rng], presence[finish], presence[status], presence[climate],
|
||||
i+1, presence[rng], presence[finish], presence[status], presence[climate],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,15 @@
|
|||
uri: 0.0.0.0:7070 # uri for ui
|
||||
network:
|
||||
# schema is the HTTP schema
|
||||
# setting to `https` does not enable https, it only changes the way URLs are generated
|
||||
schema: http
|
||||
# host is the hostname or IP address
|
||||
# if the host name contains a `.local` suffix, the name will be announced on MDNS
|
||||
# docker: MDNS announcements don't work. host must be set to the docker host's name.
|
||||
host: evcc.local
|
||||
# port is the listening port for UI and api
|
||||
# evcc will listen on all available interfaces
|
||||
port: 7070
|
||||
|
||||
interval: 10s # control cycle interval
|
||||
|
||||
# sponsor token enables optional features (request at https://cloud.evcc.io)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ type HTTPd struct {
|
|||
}
|
||||
|
||||
// NewHTTPd creates HTTP server with configured routes for loadpoint
|
||||
func NewHTTPd(url string, site site.API, hub *SocketHub, cache *util.Cache) *HTTPd {
|
||||
func NewHTTPd(addr string, site site.API, hub *SocketHub, cache *util.Cache) *HTTPd {
|
||||
routes := map[string]route{
|
||||
"health": {[]string{"GET"}, "/health", healthHandler(site)},
|
||||
"state": {[]string{"GET"}, "/state", stateHandler(cache)},
|
||||
|
|
@ -98,7 +98,7 @@ func NewHTTPd(url string, site site.API, hub *SocketHub, cache *util.Cache) *HTT
|
|||
|
||||
srv := &HTTPd{
|
||||
Server: &http.Server{
|
||||
Addr: url,
|
||||
Addr: addr,
|
||||
Handler: router,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
// Mercedes is an api.Vehicle implementation for Mercedes cars
|
||||
type Mercedes struct {
|
||||
*embed
|
||||
api.ProviderLogin
|
||||
*mercedes.Provider
|
||||
}
|
||||
|
||||
|
|
@ -63,8 +64,9 @@ func NewMercedesFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
api := mercedes.NewAPI(log, identity, cc.Sandbox)
|
||||
|
||||
v := &Mercedes{
|
||||
embed: &cc.embed,
|
||||
Provider: mercedes.NewProvider(api, strings.ToUpper(cc.VIN), cc.Cache),
|
||||
embed: &cc.embed,
|
||||
Provider: mercedes.NewProvider(api, strings.ToUpper(cc.VIN), cc.Cache),
|
||||
ProviderLogin: identity, // expose the OAuth2 login
|
||||
}
|
||||
|
||||
return v, nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue