From 7b5eddefa8d83212a3ea9d841dbc25989c998674 Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 21 Sep 2024 12:20:49 +0200 Subject: [PATCH] Tariffs: swallow startup errors (#16258) --- cmd/setup.go | 18 +++++++++++++++++- tariff/wrapper.go | 40 ++++++++++++++++++++++++++++++++++++++++ vehicle/wrapper.go | 13 +++---------- 3 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 tariff/wrapper.go diff --git a/cmd/setup.go b/cmd/setup.go index e096882804..f54f886a72 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -760,12 +760,28 @@ func configureMessengers(conf globalconfig.Messaging, vehicles push.Vehicles, va return messageChan, nil } +func tariffInstance(name string, conf config.Typed) (api.Tariff, error) { + instance, err := tariff.NewFromConfig(conf.Type, conf.Other) + if err != nil { + var ce *util.ConfigError + if errors.As(err, &ce) { + return nil, err + } + + // wrap non-config tariff errors to prevent fatals + log.ERROR.Printf("creating tariff %s failed: %v", name, err) + instance = tariff.NewWrapper(conf.Type, conf.Other, err) + } + + return instance, nil +} + func configureTariff(name string, conf config.Typed, t *api.Tariff) error { if conf.Type == "" { return nil } - res, err := tariff.NewFromConfig(conf.Type, conf.Other) + res, err := tariffInstance(name, conf) if err != nil { return &DeviceError{name, err} } diff --git a/tariff/wrapper.go b/tariff/wrapper.go new file mode 100644 index 0000000000..70b430e51f --- /dev/null +++ b/tariff/wrapper.go @@ -0,0 +1,40 @@ +package tariff + +import ( + "fmt" + + "github.com/evcc-io/evcc/api" +) + +// Wrapper wraps an api.Tariff to capture initialization errors +type Wrapper struct { + typ string + config map[string]interface{} + err error +} + +// NewWrapper creates an offline tariff wrapper +func NewWrapper(typ string, other map[string]interface{}, err error) api.Tariff { + v := &Wrapper{ + typ: typ, + config: other, + err: fmt.Errorf("tariff not available: %w", err), + } + + return v +} + +// WrappedConfig indicates a device with wrapped configuration +func (v *Wrapper) WrappedConfig() (string, map[string]interface{}) { + return v.typ, v.config +} + +// Rates implements the api.Tariff interface +func (t *Wrapper) Rates() (api.Rates, error) { + return nil, t.err +} + +// Type implements the api.Tariff interface +func (t *Wrapper) Type() api.TariffType { + return 0 +} diff --git a/vehicle/wrapper.go b/vehicle/wrapper.go index 6727185b26..09485a9ae5 100644 --- a/vehicle/wrapper.go +++ b/vehicle/wrapper.go @@ -17,7 +17,7 @@ type Wrapper struct { } // NewWrapper creates an offline Vehicle wrapper -func NewWrapper(name string, typ string, other map[string]interface{}, err error) api.Vehicle { +func NewWrapper(name, typ string, other map[string]interface{}, err error) api.Vehicle { var cc struct { embed `mapstructure:",squash"` Other map[string]interface{} `mapstructure:",remain"` @@ -44,18 +44,11 @@ func NewWrapper(name string, typ string, other map[string]interface{}, err error return v } -// Error returns the initialization error -func (v *Wrapper) Error() string { - return v.err.Error() -} - -// Error returns the initialization error -func (v *Wrapper) Config() (string, map[string]interface{}) { +// WrappedConfig indicates a device with wrapped configuration +func (v *Wrapper) WrappedConfig() (string, map[string]interface{}) { return v.typ, v.config } -var _ api.Vehicle = (*Wrapper)(nil) - // SetTitle implements the api.TitleSetter interface func (v *Wrapper) SetTitle(title string) { v.Title_ = title