From 22756de73d50856d5dbe58cab17979867178e505 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 8 Oct 2025 17:53:53 +0200 Subject: [PATCH 01/16] Vehicles: remove title from yaml config --- api/api.go | 6 ++++-- api/internal/context.go | 6 ++++++ vehicle/aiways.go | 4 ++-- vehicle/audi.go | 1 - vehicle/embed.go | 18 ++++++------------ vehicle/ford-connect.go | 8 ++++---- vehicle/seat-cupra.go | 5 ++--- vehicle/skoda-enyaq.go | 6 +++--- vehicle/tesla.go | 6 ++---- vehicle/volvo-connected.go | 5 +++-- vehicle/vw.go | 6 ++---- vehicle/wrapper.go | 12 ------------ 12 files changed, 34 insertions(+), 49 deletions(-) create mode 100644 api/internal/context.go diff --git a/api/api.go b/api/api.go index 7e3df35ea3..057c0ce084 100644 --- a/api/api.go +++ b/api/api.go @@ -5,10 +5,14 @@ import ( "io" "net/url" "time" + + "github.com/evcc-io/evcc/api/internal" ) //go:generate go tool mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,PhaseCurrents,Vehicle,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Tariff +var ContextTitle internal.ContextKey + // Meter provides total active power in W type Meter interface { CurrentPower() (float64, error) @@ -143,8 +147,6 @@ type Vehicle interface { IconDescriber FeatureDescriber PhaseDescriber - TitleDescriber - SetTitle(string) Identifiers() []string OnIdentified() ActionConfig } diff --git a/api/internal/context.go b/api/internal/context.go new file mode 100644 index 0000000000..561d6fea9e --- /dev/null +++ b/api/internal/context.go @@ -0,0 +1,6 @@ +package internal + +// ContextKey is just an empty struct. It exists so context keys can be +// an immutable public variable with a unique type. It's immutable +// because nobody else can create a ContextKey, being unexported. +type ContextKey struct{} diff --git a/vehicle/aiways.go b/vehicle/aiways.go index b0b462e88c..f8ecd6ca07 100644 --- a/vehicle/aiways.go +++ b/vehicle/aiways.go @@ -20,7 +20,7 @@ type Aiways struct { } func init() { - registry.Add("aiways", NewAiwaysFromConfig) + registry.AddCtx("aiways", NewAiwaysFromConfig) } // NewAiwaysFromConfig creates a new vehicle @@ -44,7 +44,7 @@ func NewAiwaysFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Aiways{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("aiways").Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/audi.go b/vehicle/audi.go index 755a1916ab..ff4f3c6a20 100644 --- a/vehicle/audi.go +++ b/vehicle/audi.go @@ -85,7 +85,6 @@ func NewAudiFromConfig(other map[string]interface{}) (api.Vehicle, error) { api := id.NewAPI(log, its) api.Client.Timeout = cc.Timeout - v.fromVehicle(vehicle.Nickname, 0) v.Provider = id.NewProvider(api, vehicle.VIN, cc.Cache) } diff --git a/vehicle/embed.go b/vehicle/embed.go index e97ffcd604..3495260386 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -1,6 +1,8 @@ package vehicle import ( + "context" + "github.com/evcc-io/evcc/api" ) @@ -15,14 +17,11 @@ type embed struct { OnIdentify api.ActionConfig `mapstructure:"onIdentify"` } -// Title implements the api.Vehicle interface -func (v *embed) fromVehicle(title string, capacity float64) { - if v.Title_ == "" { - v.Title_ = title - } - if v.Capacity_ == 0 { - v.Capacity_ = capacity +func (v embed) withContext(ctx context.Context) *embed { + if title := ctx.Value(api.ContextTitle); title != nil { + v.Title_ = title.(string) } + return &v } // GetTitle implements the api.Vehicle interface @@ -30,11 +29,6 @@ func (v *embed) GetTitle() string { return v.Title_ } -// SetTitle implements the api.TitleSetter interface -func (v *embed) SetTitle(title string) { - v.Title_ = title -} - // Capacity implements the api.Vehicle interface func (v *embed) Capacity() float64 { return v.Capacity_ diff --git a/vehicle/ford-connect.go b/vehicle/ford-connect.go index f87a24024c..d34cf78b96 100644 --- a/vehicle/ford-connect.go +++ b/vehicle/ford-connect.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -17,11 +18,11 @@ type FordConnect struct { } func init() { - registry.Add("ford-connect", NewFordConnectFromConfig) + registry.AddCtx("ford-connect", NewFordConnectFromConfig) } // NewFordConnectFromConfig creates a new vehicle -func NewFordConnectFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewFordConnectFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` Credentials ClientCredentials @@ -37,7 +38,7 @@ func NewFordConnectFromConfig(other map[string]interface{}) (api.Vehicle, error) } v := &FordConnect{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } if err := cc.Credentials.Error(); err != nil { @@ -59,7 +60,6 @@ func NewFordConnectFromConfig(other map[string]interface{}) (api.Vehicle, error) }) if err == nil { - v.fromVehicle(vehicle.NickName, 0) v.Provider = connect.NewProvider(api, vehicle.VehicleID, cc.Cache) } diff --git a/vehicle/seat-cupra.go b/vehicle/seat-cupra.go index b6e405b7b2..b975f37095 100644 --- a/vehicle/seat-cupra.go +++ b/vehicle/seat-cupra.go @@ -20,7 +20,7 @@ type Cupra struct { } func init() { - registry.Add("cupra", NewCupraFromConfig) + registry.AddCtx("cupra", NewCupraFromConfig) } // NewCupraFromConfig creates a new vehicle @@ -44,7 +44,7 @@ func NewCupraFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Cupra{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("cupra").Redact(cc.User, cc.Password, cc.VIN) @@ -73,7 +73,6 @@ func NewCupraFromConfig(other map[string]interface{}) (api.Vehicle, error) { ) if err == nil { - v.fromVehicle(vehicle.VehicleNickname, 0) v.Provider = cupra.NewProvider(api, ui.Subject, vehicle.VIN, cc.Cache) } diff --git a/vehicle/skoda-enyaq.go b/vehicle/skoda-enyaq.go index 1ba3b0d07d..3d13da78b6 100644 --- a/vehicle/skoda-enyaq.go +++ b/vehicle/skoda-enyaq.go @@ -19,7 +19,7 @@ type Enyaq struct { } func init() { - registry.Add("enyaq", NewEnyaqFromConfig) + registry.AddCtx("enyaq", NewEnyaqFromConfig) } // NewEnyaqFromConfig creates a new vehicle @@ -43,7 +43,7 @@ func NewEnyaqFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Enyaq{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } var err error @@ -70,7 +70,7 @@ func NewEnyaqFromConfig(other map[string]interface{}) (api.Vehicle, error) { } if err == nil { - v.fromVehicle(vehicle.Name, float64(vehicle.Specification.Battery.CapacityInKWh)) + v.Capacity_ = float64(vehicle.Specification.Battery.CapacityInKWh) } // reuse tokenService to build provider diff --git a/vehicle/tesla.go b/vehicle/tesla.go index 59765df4a1..a40bfce937 100644 --- a/vehicle/tesla.go +++ b/vehicle/tesla.go @@ -22,7 +22,7 @@ type Tesla struct { } func init() { - registry.Add("tesla", NewTeslaFromConfig) + registry.AddCtx("tesla", NewTeslaFromConfig) } // NewTeslaFromConfig creates a new vehicle @@ -109,12 +109,10 @@ func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) { tcc.SetBaseUrl(cc.CommandProxy) v := &Tesla{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: tesla.NewProvider(vehicle, cc.Cache), Controller: tesla.NewController(vehicle.WithClient(tcc)), } - v.fromVehicle(vehicle.DisplayName, 0) - return v, nil } diff --git a/vehicle/volvo-connected.go b/vehicle/volvo-connected.go index d96af176b2..a0a1f56fef 100644 --- a/vehicle/volvo-connected.go +++ b/vehicle/volvo-connected.go @@ -8,6 +8,7 @@ import ( "github.com/evcc-io/evcc/plugin/auth" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/vehicle/volvo/connected" + "github.com/spf13/cast" ) // VolvoConnected is an api.Vehicle implementation for Volvo Connected Car vehicles @@ -40,7 +41,7 @@ func NewVolvoConnectedFromConfig(ctx context.Context, other map[string]interface log := util.NewLogger("volvo-connected").Redact(cc.VIN, cc.VccApiKey) oc := connected.Oauth2Config(cc.Credentials.ID, cc.Credentials.Secret, cc.RedirectUri) - ts, err := auth.NewOauth(ctx, cc.embed.GetTitle(), oc) + ts, err := auth.NewOauth(ctx, cast.ToString(ctx.Value(api.ContextTitle)), oc) if err != nil { return nil, err } @@ -50,7 +51,7 @@ func NewVolvoConnectedFromConfig(ctx context.Context, other map[string]interface cc.VIN, err = ensureVehicle(cc.VIN, api.Vehicles) v := &VolvoConnected{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: connected.NewProvider(api, cc.VIN, cc.Cache), } diff --git a/vehicle/vw.go b/vehicle/vw.go index 28ce2a8024..5bf23e2a49 100644 --- a/vehicle/vw.go +++ b/vehicle/vw.go @@ -20,8 +20,7 @@ type ID struct { } func init() { - registry.Add("vw", NewIDFromConfig) - registry.Add("id", NewIDFromConfig) + registry.AddCtx("vw", NewIDFromConfig) } // NewIDFromConfig creates a new vehicle @@ -45,7 +44,7 @@ func NewIDFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &ID{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("id").Redact(cc.User, cc.Password, cc.VIN) @@ -72,7 +71,6 @@ func NewIDFromConfig(other map[string]interface{}) (api.Vehicle, error) { ) if err == nil { - v.fromVehicle(vehicle.Nickname, 0) v.Provider = id.NewProvider(api, vehicle.VIN, cc.Cache) } diff --git a/vehicle/wrapper.go b/vehicle/wrapper.go index 09485a9ae5..6b99419ceb 100644 --- a/vehicle/wrapper.go +++ b/vehicle/wrapper.go @@ -2,7 +2,6 @@ package vehicle import ( "fmt" - "strings" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/util" @@ -26,11 +25,6 @@ func NewWrapper(name, typ string, other map[string]interface{}, err error) api.V // try to decode vehicle-specific config and look for title attribute _ = util.DecodeOther(other, &cc) - if cc.Title_ == "" { - //lint:ignore SA1019 as Title is safe on ascii - cc.Title_ = strings.Title(name) - } - v := &Wrapper{ embed: cc.embed, typ: typ, @@ -39,7 +33,6 @@ func NewWrapper(name, typ string, other map[string]interface{}, err error) api.V } v.Features_ = append(v.Features_, api.Offline, api.Retryable) - v.SetTitle(cc.Title_) return v } @@ -49,11 +42,6 @@ func (v *Wrapper) WrappedConfig() (string, map[string]interface{}) { return v.typ, v.config } -// SetTitle implements the api.TitleSetter interface -func (v *Wrapper) SetTitle(title string) { - v.Title_ = title -} - var _ api.Battery = (*Wrapper)(nil) // Soc implements the api.Battery interface From d6c9d1987e483da24d7f0c299c4cf0964bad146c Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 8 Oct 2025 21:44:12 +0200 Subject: [PATCH 02/16] wip --- api/api.go | 2 +- cmd/soc/main.go | 5 +- core/site/api.go | 2 +- core/site_vehicles.go | 61 ++++++++++------------ push/hub.go | 1 - util/templates/defaults.yaml | 1 + util/templates/includes/vehicle-common.tpl | 3 -- vehicle/aiways.go | 3 +- vehicle/audi.go | 7 ++- vehicle/bluelink.go | 17 +++--- vehicle/bmw.go | 17 +++--- vehicle/cardata.go | 2 +- vehicle/carwings.go | 7 +-- vehicle/cloud.go | 6 +-- vehicle/embed.go | 15 +++--- vehicle/fiat.go | 2 +- vehicle/ford.go | 7 +-- vehicle/homeassistant.go | 7 +-- vehicle/jlr.go | 9 ++-- vehicle/mercedes.go | 13 ++--- vehicle/mg.go | 7 +-- vehicle/nissan.go | 7 +-- vehicle/niu.go | 7 +-- vehicle/ovms.go | 7 +-- vehicle/polestar.go | 6 +-- vehicle/porsche.go | 7 +-- vehicle/psa.go | 21 ++++---- vehicle/renault.go | 13 ++--- vehicle/seat-cupra.go | 6 +-- vehicle/seat.go | 10 ++-- vehicle/skoda-enyaq.go | 3 +- vehicle/smart-hello.go | 7 +-- vehicle/tesla.go | 2 +- vehicle/toyota.go | 7 +-- vehicle/tronity.go | 6 +-- vehicle/vehicle.go | 2 +- vehicle/volvo_deprecated.go | 7 +-- vehicle/vw.go | 3 +- vehicle/wrapper.go | 15 +++--- vehicle/zeromotorcycles.go | 7 +-- 40 files changed, 177 insertions(+), 160 deletions(-) diff --git a/api/api.go b/api/api.go index 057c0ce084..39415f98da 100644 --- a/api/api.go +++ b/api/api.go @@ -144,9 +144,9 @@ type PhaseDescriber interface { type Vehicle interface { Battery BatteryCapacity - IconDescriber FeatureDescriber PhaseDescriber + TitleDescriber Identifiers() []string OnIdentified() ActionConfig } diff --git a/cmd/soc/main.go b/cmd/soc/main.go index 3f436b9987..b3bd609322 100644 --- a/cmd/soc/main.go +++ b/cmd/soc/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "log" "math" @@ -34,7 +35,7 @@ func main() { log.Fatal("not enough arguments") } - params := make(map[string]interface{}) + params := make(map[string]any) params["brand"] = strings.ToLower(os.Args[1]) action := "soc" @@ -65,7 +66,7 @@ func main() { log.Fatal("unexpected number of parameters") } - v, err := vehicle.NewCloudFromConfig(params) + v, err := vehicle.NewCloudFromConfig(context.Background(), params) if err != nil { log.Fatal(err) } diff --git a/core/site/api.go b/core/site/api.go index a514a092da..2f86fb3a21 100644 --- a/core/site/api.go +++ b/core/site/api.go @@ -9,7 +9,7 @@ import ( type API interface { Healthy() bool Loadpoints() []loadpoint.API - Vehicles() Vehicles + // Vehicles() Vehicles // Meta GetTitle() string diff --git a/core/site_vehicles.go b/core/site_vehicles.go index 570e0f0316..edbbf952ae 100644 --- a/core/site_vehicles.go +++ b/core/site_vehicles.go @@ -5,9 +5,6 @@ import ( "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/keys" - "github.com/evcc-io/evcc/core/site" - "github.com/evcc-io/evcc/core/vehicle" - "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/config" "github.com/samber/lo" ) @@ -49,8 +46,8 @@ func (site *Site) publishVehicles() { ac := instance.OnIdentified() res[v.Name()] = vehicleStruct{ - Title: instance.GetTitle(), - Icon: instance.Icon(), + Title: instance.GetTitle(), + // Icon: instance.Icon(), Capacity: instance.Capacity(), Phases: instance.Phases(), MinSoc: v.GetMinSoc(), @@ -87,39 +84,39 @@ func (site *Site) updateVehicles(op config.Operation, dev config.Device[api.Vehi site.publishVehicles() } -var _ site.Vehicles = (*vehicles)(nil) +// var _ site.Vehicles = (*vehicles)(nil) -type vehicles struct { - log *util.Logger -} +// type vehicles struct { +// log *util.Logger +// } -func (vv *vehicles) Instances() []api.Vehicle { - devs := config.Vehicles().Devices() +// func (vv *vehicles) Instances() []api.Vehicle { +// devs := config.Vehicles().Devices() - res := make([]api.Vehicle, 0, len(devs)) - for _, dev := range devs { - res = append(res, dev.Instance()) - } +// res := make([]api.Vehicle, 0, len(devs)) +// for _, dev := range devs { +// res = append(res, dev.Instance()) +// } - return res -} +// return res +// } -func (vv *vehicles) Settings() []vehicle.API { - devs := config.Vehicles().Devices() +// func (vv *vehicles) Settings() []vehicle.API { +// devs := config.Vehicles().Devices() - res := make([]vehicle.API, 0, len(devs)) - for _, dev := range devs { - res = append(res, vehicle.Adapter(vv.log, dev)) - } +// res := make([]vehicle.API, 0, len(devs)) +// for _, dev := range devs { +// res = append(res, vehicle.Adapter(vv.log, dev)) +// } - return res -} +// return res +// } -func (vv *vehicles) ByName(name string) (vehicle.API, error) { - dev, err := config.Vehicles().ByName(name) - if err != nil { - return nil, err - } +// func (vv *vehicles) ByName(name string) (vehicle.API, error) { +// dev, err := config.Vehicles().ByName(name) +// if err != nil { +// return nil, err +// } - return vehicle.Adapter(vv.log, dev), nil -} +// return vehicle.Adapter(vv.log, dev), nil +// } diff --git a/push/hub.go b/push/hub.go index ff6b65b578..c85795956a 100644 --- a/push/hub.go +++ b/push/hub.go @@ -85,7 +85,6 @@ func (h *Hub) apply(ev Event, tmpl string) (string, error) { instance := v.Instance() attr["vehicleTitle"] = instance.GetTitle() - attr["vehicleIcon"] = instance.Icon() attr["vehicleCapacity"] = instance.Capacity() } } diff --git a/util/templates/defaults.yaml b/util/templates/defaults.yaml index 54109562b7..6a2e60a474 100644 --- a/util/templates/defaults.yaml +++ b/util/templates/defaults.yaml @@ -485,6 +485,7 @@ presets: vehicle-common: params: - name: title + deprecated: true - name: icon default: car advanced: true diff --git a/util/templates/includes/vehicle-common.tpl b/util/templates/includes/vehicle-common.tpl index e5158ee5c0..e2de2a4554 100644 --- a/util/templates/includes/vehicle-common.tpl +++ b/util/templates/includes/vehicle-common.tpl @@ -1,7 +1,4 @@ {{ define "vehicle-common" }} -{{- if .title }} -title: {{ .title }} -{{- end }} {{- if .icon }} icon: {{ .icon }} {{- end }} diff --git a/vehicle/aiways.go b/vehicle/aiways.go index f8ecd6ca07..efa7de46d5 100644 --- a/vehicle/aiways.go +++ b/vehicle/aiways.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "strconv" "time" @@ -24,7 +25,7 @@ func init() { } // NewAiwaysFromConfig creates a new vehicle -func NewAiwaysFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewAiwaysFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string diff --git a/vehicle/audi.go b/vehicle/audi.go index ff4f3c6a20..3aba93a34f 100644 --- a/vehicle/audi.go +++ b/vehicle/audi.go @@ -24,12 +24,11 @@ type Audi struct { } func init() { - registry.Add("audi", NewAudiFromConfig) - registry.Add("etron", NewAudiFromConfig) + registry.AddCtx("audi", NewAudiFromConfig) } // NewAudiFromConfig creates a new vehicle -func NewAudiFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewAudiFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -49,7 +48,7 @@ func NewAudiFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Audi{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("audi").Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/bluelink.go b/vehicle/bluelink.go index 4ed3c2fed2..7715d75964 100644 --- a/vehicle/bluelink.go +++ b/vehicle/bluelink.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -18,12 +19,12 @@ type Bluelink struct { } func init() { - registry.Add("kia", NewKiaFromConfig) - registry.Add("hyundai", NewHyundaiFromConfig) + registry.AddCtx("kia", NewKiaFromConfig) + registry.AddCtx("hyundai", NewHyundaiFromConfig) } // NewHyundaiFromConfig creates a new vehicle -func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewHyundaiFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { settings := bluelink.Config{ URI: "https://prd.eu-ccapi.hyundai.com:8080", BasicToken: "NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==", @@ -40,11 +41,11 @@ func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) { // BrandAuthUrl: "%s/auth/api/v2/user/oauth2/authorize?response_type=code&client_id=%s&redirect_uri=%s/api/v1/user/oauth2/redirect&lang=%s&state=ccsp", } - return newBluelinkFromConfig("hyundai", other, settings) + return newBluelinkFromConfig(ctx, "hyundai", other, settings) } // NewKiaFromConfig creates a new vehicle -func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewKiaFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { settings := bluelink.Config{ URI: "https://prd.eu-ccapi.kia.com:8080", BasicToken: "ZmRjODVjMDAtMGEyZi00YzY0LWJjYjQtMmNmYjE1MDA3MzBhOnNlY3JldA==", @@ -58,11 +59,11 @@ func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) { Brand: "kia", } - return newBluelinkFromConfig("kia", other, settings) + return newBluelinkFromConfig(ctx, "kia", other, settings) } // newBluelinkFromConfig creates a new Vehicle -func newBluelinkFromConfig(brand string, other map[string]interface{}, settings bluelink.Config) (api.Vehicle, error) { +func newBluelinkFromConfig(ctx context.Context, brand string, other map[string]interface{}, settings bluelink.Config) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password string @@ -100,7 +101,7 @@ func newBluelinkFromConfig(brand string, other map[string]interface{}, settings } v := &Bluelink{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: bluelink.NewProvider(api, vehicle, cc.Expiry, cc.Cache), } diff --git a/vehicle/bmw.go b/vehicle/bmw.go index 159682a33e..d8606a2d72 100644 --- a/vehicle/bmw.go +++ b/vehicle/bmw.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -15,22 +16,22 @@ type BMW struct { } func init() { - registry.Add("bmw", NewBMWFromConfig) - registry.Add("mini", NewMiniFromConfig) + registry.AddCtx("bmw", NewBMWFromConfig) + registry.AddCtx("mini", NewMiniFromConfig) } // NewBMWFromConfig creates a new vehicle -func NewBMWFromConfig(other map[string]interface{}) (api.Vehicle, error) { - return NewBMWMiniFromConfig("bmw", other) +func NewBMWFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { + return NewBMWMiniFromConfig(ctx, "bmw", other) } // NewMiniFromConfig creates a new vehicle -func NewMiniFromConfig(other map[string]interface{}) (api.Vehicle, error) { - return NewBMWMiniFromConfig("mini", other) +func NewMiniFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { + return NewBMWMiniFromConfig(ctx, "mini", other) } // NewBMWMiniFromConfig creates a new vehicle -func NewBMWMiniFromConfig(brand string, other map[string]interface{}) (api.Vehicle, error) { +func NewBMWMiniFromConfig(ctx context.Context, brand string, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -51,7 +52,7 @@ func NewBMWMiniFromConfig(brand string, other map[string]interface{}) (api.Vehic } v := &BMW{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger(brand).Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/cardata.go b/vehicle/cardata.go index 480dfe018b..ce55f48cc6 100644 --- a/vehicle/cardata.go +++ b/vehicle/cardata.go @@ -46,7 +46,7 @@ func NewCardataFromConfig(ctx context.Context, other map[string]interface{}) (ap } v := &Cardata{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } oc := cardata.Config diff --git a/vehicle/carwings.go b/vehicle/carwings.go index e5ac36f655..f57ea4594c 100644 --- a/vehicle/carwings.go +++ b/vehicle/carwings.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "errors" "fmt" "net" @@ -31,11 +32,11 @@ type CarWings struct { } func init() { - registry.Add("carwings", NewCarWingsFromConfig) + registry.AddCtx("carwings", NewCarWingsFromConfig) } // NewCarWingsFromConfig creates a new vehicle -func NewCarWingsFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewCarWingsFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, Region, VIN string @@ -75,7 +76,7 @@ func NewCarWingsFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &CarWings{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), user: cc.User, password: cc.Password, session: &carwings.Session{ diff --git a/vehicle/cloud.go b/vehicle/cloud.go index 373daf3c8b..cab7e853db 100644 --- a/vehicle/cloud.go +++ b/vehicle/cloud.go @@ -25,11 +25,11 @@ type Cloud struct { } func init() { - registry.Add("cloud", NewCloudFromConfig) + registry.AddCtx("cloud", NewCloudFromConfig) } // NewCloudFromConfig creates a new vehicle -func NewCloudFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewCloudFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` Brand string @@ -53,7 +53,7 @@ func NewCloudFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Cloud{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), token: sponsor.Token, brand: cc.Brand, config: cc.Other, diff --git a/vehicle/embed.go b/vehicle/embed.go index 3495260386..a31e537cf5 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -8,8 +8,9 @@ import ( // TODO align phases with OnIdentify type embed struct { - Title_ string `mapstructure:"title"` - Icon_ string `mapstructure:"icon"` + _Title string `mapstructure:"title"` // TODO deprecated + _Icon string `mapstructure:"-"` // TODO deprecated + Title_ string `mapstructure:"-" json:"-"` Capacity_ float64 `mapstructure:"capacity"` Phases_ int `mapstructure:"phases"` Identifiers_ []string `mapstructure:"identifiers"` @@ -51,12 +52,12 @@ func (v *embed) OnIdentified() api.ActionConfig { return v.OnIdentify } -var _ api.IconDescriber = (*embed)(nil) +// var _ api.IconDescriber = (*embed)(nil) -// Icon implements the api.IconDescriber interface -func (v *embed) Icon() string { - return v.Icon_ -} +// // Icon implements the api.IconDescriber interface +// func (v *embed) Icon() string { +// return v.Icon_ +// } var _ api.FeatureDescriber = (*embed)(nil) diff --git a/vehicle/fiat.go b/vehicle/fiat.go index 24d1345f90..75339c1660 100644 --- a/vehicle/fiat.go +++ b/vehicle/fiat.go @@ -44,7 +44,7 @@ func NewFiatFromConfig(ctx context.Context, other map[string]interface{}) (api.V } v := &Fiat{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("fiat").Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/ford.go b/vehicle/ford.go index 77ed1892d2..0b847b3c15 100644 --- a/vehicle/ford.go +++ b/vehicle/ford.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "time" @@ -21,11 +22,11 @@ type Ford struct { } func init() { - registry.Add("ford", NewFordFromConfig) + registry.AddCtx("ford", NewFordFromConfig) } // NewFordFromConfig creates a new vehicle -func NewFordFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewFordFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -45,7 +46,7 @@ func NewFordFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Ford{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("ford").Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/homeassistant.go b/vehicle/homeassistant.go index 7eef465509..ed6b67c483 100644 --- a/vehicle/homeassistant.go +++ b/vehicle/homeassistant.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "errors" "strconv" "time" @@ -18,11 +19,11 @@ type HomeAssistant struct { // Register on startup func init() { - registry.Add("homeassistant", NewHomeAssistantVehicleFromConfig) + registry.AddCtx("homeassistant", NewHomeAssistantVehicleFromConfig) } // Constructor from YAML config -func NewHomeAssistantVehicleFromConfig(other map[string]any) (api.Vehicle, error) { +func NewHomeAssistantVehicleFromConfig(ctx context.Context, other map[string]any) (api.Vehicle, error) { var cc struct { embed `mapstructure:",squash"` URI string @@ -64,7 +65,7 @@ func NewHomeAssistantVehicleFromConfig(other map[string]any) (api.Vehicle, error } res := &HomeAssistant{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), conn: conn, soc: cc.Sensors.Soc, } diff --git a/vehicle/jlr.go b/vehicle/jlr.go index 41b3cf9cd0..f57dc4f514 100644 --- a/vehicle/jlr.go +++ b/vehicle/jlr.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "net/http" "net/url" @@ -22,12 +23,12 @@ type JLR struct { } func init() { - registry.Add("jaguar", NewJLRFromConfig) - registry.Add("landrover", NewJLRFromConfig) + registry.AddCtx("jaguar", NewJLRFromConfig) + registry.AddCtx("landrover", NewJLRFromConfig) } // NewJLRFromConfig creates a new vehicle -func NewJLRFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewJLRFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -48,7 +49,7 @@ func NewJLRFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &JLR{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("jlr").Redact(cc.User, cc.Password, cc.VIN, cc.DeviceID) diff --git a/vehicle/mercedes.go b/vehicle/mercedes.go index 9eeab3f7f0..c7a970ade3 100644 --- a/vehicle/mercedes.go +++ b/vehicle/mercedes.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "errors" "time" @@ -16,16 +17,16 @@ type Mercedes struct { } func init() { - registry.Add("mercedes", func(other map[string]interface{}) (api.Vehicle, error) { - return newMercedesFromConfig("mercedes", other) + registry.AddCtx("mercedes", func(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { + return newMercedesFromConfig(ctx, "mercedes", other) }) - registry.Add("smart-eq", func(other map[string]interface{}) (api.Vehicle, error) { - return newMercedesFromConfig("smart-eq", other) + registry.AddCtx("smart-eq", func(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { + return newMercedesFromConfig(ctx, "smart-eq", other) }) } // newMercedesFromConfig creates a new vehicle -func newMercedesFromConfig(brand string, other map[string]interface{}) (api.Vehicle, error) { +func newMercedesFromConfig(ctx context.Context, brand string, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` Tokens Tokens @@ -71,7 +72,7 @@ func newMercedesFromConfig(brand string, other map[string]interface{}) (api.Vehi } v := &Mercedes{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: mercedes.NewProvider(api, cc.VIN, cc.Cache), } diff --git a/vehicle/mg.go b/vehicle/mg.go index 82e61cccf3..2edb2e5e0d 100644 --- a/vehicle/mg.go +++ b/vehicle/mg.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "strings" "time" @@ -16,11 +17,11 @@ type MG struct { } func init() { - registry.Add("mg", NewMGFromConfig) + registry.AddCtx("mg", NewMGFromConfig) } // NewBMWFromConfig creates a new vehicle -func NewMGFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewMGFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -57,7 +58,7 @@ func NewMGFromConfig(other map[string]interface{}) (api.Vehicle, error) { api := saic.NewAPI(log, identity) v := &MG{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: saic.NewProvider(api, cc.VIN, cc.Cache), } diff --git a/vehicle/nissan.go b/vehicle/nissan.go index a56f597871..f069075c67 100644 --- a/vehicle/nissan.go +++ b/vehicle/nissan.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "time" @@ -24,11 +25,11 @@ type Nissan struct { } func init() { - registry.Add("nissan", NewNissanFromConfig) + registry.AddCtx("nissan", NewNissanFromConfig) } // NewNissanFromConfig creates a new vehicle -func NewNissanFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewNissanFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -50,7 +51,7 @@ func NewNissanFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Nissan{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("nissan").Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/niu.go b/vehicle/niu.go index 6053913df1..7689952539 100644 --- a/vehicle/niu.go +++ b/vehicle/niu.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "crypto/md5" "encoding/hex" "errors" @@ -27,11 +28,11 @@ type Niu struct { } func init() { - registry.Add("niu", NewNiuFromConfig) + registry.AddCtx("niu", NewNiuFromConfig) } // NewFordFromConfig creates a new vehicle -func NewNiuFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewNiuFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, Serial string @@ -55,7 +56,7 @@ func NewNiuFromConfig(other map[string]interface{}) (api.Vehicle, error) { log := util.NewLogger("niu").Redact(cc.User, cc.Password) v := &Niu{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Helper: request.NewHelper(log), user: cc.User, password: cc.Password, diff --git a/vehicle/ovms.go b/vehicle/ovms.go index 1f9a94fc3e..aa4b6f5e4c 100644 --- a/vehicle/ovms.go +++ b/vehicle/ovms.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "net" "net/http" @@ -29,11 +30,11 @@ type Ovms struct { } func init() { - registry.Add("ovms", NewOvmsFromConfig) + registry.AddCtx("ovms", NewOvmsFromConfig) } // NewOVMSFromConfig creates a new vehicle -func NewOvmsFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewOvmsFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VehicleID, Server string @@ -49,7 +50,7 @@ func NewOvmsFromConfig(other map[string]interface{}) (api.Vehicle, error) { log := util.NewLogger("ovms").Redact(cc.User, cc.Password, cc.VehicleID) v := &Ovms{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Helper: request.NewHelper(log), user: cc.User, password: cc.Password, diff --git a/vehicle/polestar.go b/vehicle/polestar.go index de863480e2..c238426084 100644 --- a/vehicle/polestar.go +++ b/vehicle/polestar.go @@ -18,11 +18,11 @@ type Polestar struct { } func init() { - registry.Add("polestar", NewPolestarFromConfig) + registry.AddCtx("polestar", NewPolestarFromConfig) } // NewPolestarFromConfig creates a new vehicle -func NewPolestarFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewPolestarFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password string @@ -41,7 +41,7 @@ func NewPolestarFromConfig(other map[string]interface{}) (api.Vehicle, error) { log := util.NewLogger("polestar").Redact(cc.User, cc.Password, cc.VIN) v := &Polestar{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } identity, err := polestar.NewIdentity(log, cc.User, cc.Password) diff --git a/vehicle/porsche.go b/vehicle/porsche.go index 7792078ae9..4d15a81137 100644 --- a/vehicle/porsche.go +++ b/vehicle/porsche.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "errors" "fmt" "time" @@ -17,11 +18,11 @@ type Porsche struct { } func init() { - registry.Add("porsche", NewPorscheFromConfig) + registry.AddCtx("porsche", NewPorscheFromConfig) } // NewPorscheFromConfig creates a new vehicle -func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewPorscheFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -70,7 +71,7 @@ func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) { provider := porsche.NewProvider(log, api, emobApi, vehicle.VIN, capabilities.CarModel, cc.Cache) v := &Porsche{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: provider, } diff --git a/vehicle/psa.go b/vehicle/psa.go index 6ecf58ff3e..7f0a193888 100644 --- a/vehicle/psa.go +++ b/vehicle/psa.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "strings" "time" @@ -12,17 +13,17 @@ import ( // https://github.com/TA2k/ioBroker.psa func init() { - registry.Add("citroen", func(other map[string]any) (api.Vehicle, error) { - return newPSA("citroen", "clientsB2CCitroen", other) + registry.AddCtx("citroen", func(ctx context.Context, other map[string]any) (api.Vehicle, error) { + return newPSA(ctx, "citroen", "clientsB2CCitroen", other) }) - registry.Add("ds", func(other map[string]any) (api.Vehicle, error) { - return newPSA("ds", "clientsB2CDS", other) + registry.AddCtx("ds", func(ctx context.Context, other map[string]any) (api.Vehicle, error) { + return newPSA(ctx, "ds", "clientsB2CDS", other) }) - registry.Add("opel", func(other map[string]any) (api.Vehicle, error) { - return newPSA("opel", "clientsB2COpel", other) + registry.AddCtx("opel", func(ctx context.Context, other map[string]any) (api.Vehicle, error) { + return newPSA(ctx, "opel", "clientsB2COpel", other) }) - registry.Add("peugeot", func(other map[string]any) (api.Vehicle, error) { - return newPSA("peugeot", "clientsB2CPeugeot", other) + registry.AddCtx("peugeot", func(ctx context.Context, other map[string]any) (api.Vehicle, error) { + return newPSA(ctx, "peugeot", "clientsB2CPeugeot", other) }) } @@ -33,7 +34,7 @@ type PSA struct { } // newPSA creates a new vehicle -func newPSA(brand, realm string, other map[string]interface{}) (api.Vehicle, error) { +func newPSA(ctx context.Context, brand, realm string, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` VIN string @@ -60,7 +61,7 @@ func newPSA(brand, realm string, other map[string]interface{}) (api.Vehicle, err } v := &PSA{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger(brand) diff --git a/vehicle/renault.go b/vehicle/renault.go index 06c43e60a3..7efc9dfc91 100644 --- a/vehicle/renault.go +++ b/vehicle/renault.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -27,16 +28,16 @@ type Renault struct { } func init() { - registry.Add("dacia", func(other map[string]interface{}) (api.Vehicle, error) { - return NewRenaultDaciaFromConfig("dacia", other) + registry.AddCtx("dacia", func(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { + return NewRenaultDaciaFromConfig(ctx, "dacia", other) }) - registry.Add("renault", func(other map[string]interface{}) (api.Vehicle, error) { - return NewRenaultDaciaFromConfig("renault", other) + registry.AddCtx("renault", func(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { + return NewRenaultDaciaFromConfig(ctx, "renault", other) }) } // NewRenaultDaciaFromConfig creates a new Renault/Dacia vehicle -func NewRenaultDaciaFromConfig(brand string, other map[string]interface{}) (api.Vehicle, error) { +func NewRenaultDaciaFromConfig(ctx context.Context, brand string, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, Region, VIN string @@ -61,7 +62,7 @@ func NewRenaultDaciaFromConfig(brand string, other map[string]interface{}) (api. log := util.NewLogger(brand).Redact(cc.User, cc.Password, cc.VIN) v := &Renault{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } keys := keys.New(log) diff --git a/vehicle/seat-cupra.go b/vehicle/seat-cupra.go index b975f37095..b044ae37dc 100644 --- a/vehicle/seat-cupra.go +++ b/vehicle/seat-cupra.go @@ -24,7 +24,7 @@ func init() { } // NewCupraFromConfig creates a new vehicle -func NewCupraFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewCupraFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -55,8 +55,8 @@ func NewCupraFromConfig(other map[string]interface{}) (api.Vehicle, error) { } // get OIDC user information - ctx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewClient(log)) - ui, err := vwidentity.Config.NewProvider(ctx).UserInfo(ctx, ts) + oidcCtx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewClient(log)) + ui, err := vwidentity.Config.NewProvider(oidcCtx).UserInfo(oidcCtx, ts) if err != nil { return nil, fmt.Errorf("failed getting user information: %w", err) } diff --git a/vehicle/seat.go b/vehicle/seat.go index 0d6673ed81..e54c073fac 100644 --- a/vehicle/seat.go +++ b/vehicle/seat.go @@ -29,11 +29,11 @@ type Seat struct { } func init() { - registry.Add("seat", NewSeatFromConfig) + registry.AddCtx("seat", NewSeatFromConfig) } // NewSeatFromConfig creates a new vehicle -func NewSeatFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewSeatFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -53,7 +53,7 @@ func NewSeatFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Seat{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("seat").Redact(cc.User, cc.Password, cc.VIN) @@ -64,8 +64,8 @@ func NewSeatFromConfig(other map[string]interface{}) (api.Vehicle, error) { } // get OIDC user information - ctx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewClient(log)) - ui, err := vwidentity.Config.NewProvider(ctx).UserInfo(ctx, trs) + oidcCtx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewClient(log)) + ui, err := vwidentity.Config.NewProvider(oidcCtx).UserInfo(oidcCtx, trs) if err != nil { return nil, fmt.Errorf("failed getting user information: %w", err) } diff --git a/vehicle/skoda-enyaq.go b/vehicle/skoda-enyaq.go index 3d13da78b6..91bd0f42c9 100644 --- a/vehicle/skoda-enyaq.go +++ b/vehicle/skoda-enyaq.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -23,7 +24,7 @@ func init() { } // NewEnyaqFromConfig creates a new vehicle -func NewEnyaqFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewEnyaqFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string diff --git a/vehicle/smart-hello.go b/vehicle/smart-hello.go index 0c0b53dde5..48dbd46090 100644 --- a/vehicle/smart-hello.go +++ b/vehicle/smart-hello.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "time" @@ -16,11 +17,11 @@ type SmartHello struct { } func init() { - registry.Add("smart-hello", NewSmartHelloFromConfig) + registry.AddCtx("smart-hello", NewSmartHelloFromConfig) } // NewSmartHelloFromConfig creates a new vehicle -func NewSmartHelloFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewSmartHelloFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password string @@ -41,7 +42,7 @@ func NewSmartHelloFromConfig(other map[string]interface{}) (api.Vehicle, error) log := util.NewLogger("smart-hello").Redact(cc.User, cc.Password, cc.VIN) v := &SmartHello{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } identity, err := hello.NewIdentity(log, cc.User, cc.Password) diff --git a/vehicle/tesla.go b/vehicle/tesla.go index a40bfce937..a31ae81e2e 100644 --- a/vehicle/tesla.go +++ b/vehicle/tesla.go @@ -26,7 +26,7 @@ func init() { } // NewTeslaFromConfig creates a new vehicle -func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewTeslaFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` Credentials ClientCredentials diff --git a/vehicle/toyota.go b/vehicle/toyota.go index 91da7b4f31..b71c6d79ed 100644 --- a/vehicle/toyota.go +++ b/vehicle/toyota.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "time" @@ -16,11 +17,11 @@ type Toyota struct { } func init() { - registry.Add("toyota", NewToyotaFromConfig) + registry.AddCtx("toyota", NewToyotaFromConfig) } // NewToyotaFromConfig creates a new vehicle -func NewToyotaFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewToyotaFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -38,7 +39,7 @@ func NewToyotaFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &Toyota{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), } log := util.NewLogger("toyota").Redact(cc.User, cc.Password, cc.VIN) diff --git a/vehicle/tronity.go b/vehicle/tronity.go index 9f2b9c2e89..f18b1d9796 100644 --- a/vehicle/tronity.go +++ b/vehicle/tronity.go @@ -45,13 +45,13 @@ type Tronity struct { } func init() { - registry.Add("tronity", NewTronityFromConfig) + registry.AddCtx("tronity", NewTronityFromConfig) } //go:generate go tool decorate -f decorateTronity -b *Tronity -r api.Vehicle -t "api.ChargeState,Status,func() (api.ChargeStatus, error)" -t "api.VehicleOdometer,Odometer,func() (float64, error)" -t "api.ChargeController,ChargeEnable,func(bool) error" // NewTronityFromConfig creates a new vehicle -func NewTronityFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewTronityFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` Credentials ClientCredentials @@ -84,7 +84,7 @@ func NewTronityFromConfig(other map[string]interface{}) (api.Vehicle, error) { v := &Tronity{ log: log, - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Helper: request.NewHelper(log), oc: oc, } diff --git a/vehicle/vehicle.go b/vehicle/vehicle.go index 795b422990..bcfe4f85b3 100644 --- a/vehicle/vehicle.go +++ b/vehicle/vehicle.go @@ -49,7 +49,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{} } v := &Vehicle{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), socG: socG, } diff --git a/vehicle/volvo_deprecated.go b/vehicle/volvo_deprecated.go index 7ed3141df4..d29b20c96c 100644 --- a/vehicle/volvo_deprecated.go +++ b/vehicle/volvo_deprecated.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "time" @@ -20,11 +21,11 @@ type Volvo struct { } func init() { - registry.Add("volvo", NewVolvoFromConfig) + registry.AddCtx("volvo", NewVolvoFromConfig) } // NewVolvoFromConfig creates a new vehicle -func NewVolvoFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewVolvoFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string @@ -46,7 +47,7 @@ func NewVolvoFromConfig(other map[string]interface{}) (api.Vehicle, error) { log := util.NewLogger("volvo").Redact(cc.User, cc.Password, cc.VIN, basicAuth) v := &Volvo{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Helper: request.NewHelper(log), vin: cc.VIN, } diff --git a/vehicle/vw.go b/vehicle/vw.go index 5bf23e2a49..bd92677f1f 100644 --- a/vehicle/vw.go +++ b/vehicle/vw.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -24,7 +25,7 @@ func init() { } // NewIDFromConfig creates a new vehicle -func NewIDFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewIDFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { cc := struct { embed `mapstructure:",squash"` User, Password, VIN string diff --git a/vehicle/wrapper.go b/vehicle/wrapper.go index 6b99419ceb..c7ea140aa3 100644 --- a/vehicle/wrapper.go +++ b/vehicle/wrapper.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "fmt" "github.com/evcc-io/evcc/api" @@ -9,24 +10,24 @@ import ( // Wrapper wraps an api.Vehicle to capture initialization errors type Wrapper struct { - embed + *embed typ string - config map[string]interface{} + config map[string]any err error } // NewWrapper creates an offline Vehicle wrapper -func NewWrapper(name, typ string, other map[string]interface{}, err error) api.Vehicle { +func NewWrapper(ctx context.Context, name, typ string, other map[string]any, err error) api.Vehicle { var cc struct { embed `mapstructure:",squash"` - Other map[string]interface{} `mapstructure:",remain"` + Other map[string]any `mapstructure:",remain"` } - // try to decode vehicle-specific config and look for title attribute + // try to decode vehicle-specific config and look for embedded attributes _ = util.DecodeOther(other, &cc) v := &Wrapper{ - embed: cc.embed, + embed: cc.embed.withContext(ctx), typ: typ, config: cc.Other, err: fmt.Errorf("vehicle not available: %w", err), @@ -38,7 +39,7 @@ func NewWrapper(name, typ string, other map[string]interface{}, err error) api.V } // WrappedConfig indicates a device with wrapped configuration -func (v *Wrapper) WrappedConfig() (string, map[string]interface{}) { +func (v *Wrapper) WrappedConfig() (string, map[string]any) { return v.typ, v.config } diff --git a/vehicle/zeromotorcycles.go b/vehicle/zeromotorcycles.go index afd7f7d6e2..3e7a63a9c0 100644 --- a/vehicle/zeromotorcycles.go +++ b/vehicle/zeromotorcycles.go @@ -1,6 +1,7 @@ package vehicle import ( + "context" "time" "github.com/evcc-io/evcc/api" @@ -15,11 +16,11 @@ type ZeroMotorcycle struct { } func init() { - registry.Add("zero", NewZeroFromConfig) + registry.AddCtx("zero", NewZeroFromConfig) } // NewZeroFromConfig creates a new vehicle -func NewZeroFromConfig(other map[string]interface{}) (api.Vehicle, error) { +func NewZeroFromConfig(ctx context.Context, other map[string]interface{}) (api.Vehicle, error) { var res *zero.API var err error @@ -55,7 +56,7 @@ func NewZeroFromConfig(other map[string]interface{}) (api.Vehicle, error) { } v := &ZeroMotorcycle{ - embed: &cc.embed, + embed: cc.embed.withContext(ctx), Provider: zero.NewProvider(res, vehicle.UnitNumber, cc.Cache), } From 32efd5a69fc706fa97cadc13f7cc338209296bf5 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 8 Oct 2025 21:44:44 +0200 Subject: [PATCH 03/16] wip --- util/templates/defaults.yaml | 3 +-- util/templates/includes/vehicle-common.tpl | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/util/templates/defaults.yaml b/util/templates/defaults.yaml index 6a2e60a474..a6d7c22b72 100644 --- a/util/templates/defaults.yaml +++ b/util/templates/defaults.yaml @@ -487,8 +487,7 @@ presets: - name: title deprecated: true - name: icon - default: car - advanced: true + deprecated: true - name: capacity - name: phases advanced: true diff --git a/util/templates/includes/vehicle-common.tpl b/util/templates/includes/vehicle-common.tpl index e2de2a4554..3f69940e4e 100644 --- a/util/templates/includes/vehicle-common.tpl +++ b/util/templates/includes/vehicle-common.tpl @@ -1,7 +1,4 @@ {{ define "vehicle-common" }} -{{- if .icon }} -icon: {{ .icon }} -{{- end }} {{- if .capacity }} capacity: {{ .capacity }} {{- end }} From 3f4cd9b4a65ecada96eee6c1616136d6fc187e81 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 8 Oct 2025 21:53:28 +0200 Subject: [PATCH 04/16] wip --- cmd/setup.go | 26 +++++++++++++++----------- core/site_vehicles.go | 7 ++++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/cmd/setup.go b/cmd/setup.go index 35ed78c768..90f9ee8bf4 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -367,8 +367,9 @@ func configureChargers(static []config.Named, names ...string) error { return eg.Wait() } -func vehicleInstance(cc config.Named) (api.Vehicle, error) { - ctx := util.WithLogger(context.TODO(), util.NewLogger(cc.Name)) +func vehicleInstance(ctx context.Context, cc config.Named) (api.Vehicle, error) { + // TODO move key + ctx = util.WithLogger(ctx, util.NewLogger(cc.Name)) props, err := customDevice(cc.Other) @@ -384,13 +385,7 @@ func vehicleInstance(cc config.Named) (api.Vehicle, error) { // wrap non-config vehicle errors to prevent fatals log.ERROR.Printf("creating vehicle %s failed: %v", cc.Name, err) - instance = vehicle.NewWrapper(cc.Name, cc.Type, cc.Other, err) - } - - // ensure vehicle config has title - if instance.GetTitle() == "" { - //lint:ignore SA1019 as Title is safe on ascii - instance.SetTitle(strings.Title(cc.Name)) + instance = vehicle.NewWrapper(ctx, cc.Name, cc.Type, cc.Other, err) } return instance, nil @@ -417,7 +412,9 @@ func configureVehicles(static []config.Named, names ...string) error { } eg.Go(func() error { - instance, err := vehicleInstance(cc) + ctx := context.WithValue(context.Background(), api.ContextTitle, cc.Name) + + instance, err := vehicleInstance(ctx, cc) if err != nil { return fmt.Errorf("cannot create vehicle '%s': %w", cc.Name, err) } @@ -447,7 +444,14 @@ func configureVehicles(static []config.Named, names ...string) error { return nil } - instance, err := vehicleInstance(cc) + title := conf.Properties.Title + if title == "" { + title = cc.Name + } + + ctx := context.WithValue(context.Background(), api.ContextTitle, title) + + instance, err := vehicleInstance(ctx, cc) if err != nil { return fmt.Errorf("cannot create vehicle '%s': %w", cc.Name, err) } diff --git a/core/site_vehicles.go b/core/site_vehicles.go index edbbf952ae..6e1bae6f93 100644 --- a/core/site_vehicles.go +++ b/core/site_vehicles.go @@ -32,10 +32,11 @@ type vehicleStruct struct { // publishVehicles returns a list of vehicle titles func (site *Site) publishVehicles() { - vv := site.Vehicles().Settings() - res := make(map[string]vehicleStruct, len(vv)) + devs := config.Vehicles().Devices() + res := make(map[string]vehicleStruct, len(devs)) - for _, v := range vv { + for _, dev := range devs { + v := dev.Instance() var plan *planStruct if time, precondition, soc := v.GetPlanSoc(); !time.IsZero() { From 80f25ddc979af19bf941c0022d39c83e77f712d5 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 07:58:19 +0200 Subject: [PATCH 05/16] wip --- core/site.go | 5 +-- core/site/api.go | 2 +- core/site/vehicles.go | 2 +- core/site_vehicles.go | 71 ++++++++++++++++++++++--------------------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/core/site.go b/core/site.go index 362ee26466..44eb0b3ca8 100644 --- a/core/site.go +++ b/core/site.go @@ -382,8 +382,9 @@ func meterCapabilities(name string, meter any) string { // DumpConfig site configuration func (site *Site) DumpConfig() { // verify vehicle detection - if vehicles := site.Vehicles().Instances(); len(vehicles) > 1 { - for _, v := range vehicles { + if devs := config.Vehicles().Devices(); len(devs) > 1 { + for _, dev := range devs { + v := dev.Instance() if _, ok := v.(api.ChargeState); !ok && len(v.Identifiers()) == 0 { site.log.WARN.Printf("vehicle '%s' does not support automatic detection", v.GetTitle()) } diff --git a/core/site/api.go b/core/site/api.go index 2f86fb3a21..a514a092da 100644 --- a/core/site/api.go +++ b/core/site/api.go @@ -9,7 +9,7 @@ import ( type API interface { Healthy() bool Loadpoints() []loadpoint.API - // Vehicles() Vehicles + Vehicles() Vehicles // Meta GetTitle() string diff --git a/core/site/vehicles.go b/core/site/vehicles.go index f36af09882..90f01960f7 100644 --- a/core/site/vehicles.go +++ b/core/site/vehicles.go @@ -12,6 +12,6 @@ type Vehicles interface { // ByName returns a single vehicle adapter by name ByName(string) (vehicle.API, error) - // All returns the list of vehicle instances + // Instance returns the list of vehicle instances Instances() []api.Vehicle } diff --git a/core/site_vehicles.go b/core/site_vehicles.go index 6e1bae6f93..5186d81716 100644 --- a/core/site_vehicles.go +++ b/core/site_vehicles.go @@ -5,6 +5,9 @@ import ( "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/keys" + "github.com/evcc-io/evcc/core/site" + "github.com/evcc-io/evcc/core/vehicle" + "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/config" "github.com/samber/lo" ) @@ -36,21 +39,21 @@ func (site *Site) publishVehicles() { res := make(map[string]vehicleStruct, len(devs)) for _, dev := range devs { - v := dev.Instance() - var plan *planStruct + v := vehicle.Adapter(site.log, dev) + instance := v.Instance() + ac := instance.OnIdentified() + + var plan *planStruct if time, precondition, soc := v.GetPlanSoc(); !time.IsZero() { plan = &planStruct{Soc: soc, Precondition: int64(precondition.Seconds()), Time: time} } - instance := v.Instance() - ac := instance.OnIdentified() - res[v.Name()] = vehicleStruct{ - Title: instance.GetTitle(), - // Icon: instance.Icon(), + Title: instance.GetTitle(), Capacity: instance.Capacity(), Phases: instance.Phases(), + Icon: deviceProperties(dev).Icon, // device meta data MinSoc: v.GetMinSoc(), LimitSoc: v.GetLimitSoc(), MinCurrent: ac.MinCurrent, @@ -85,39 +88,39 @@ func (site *Site) updateVehicles(op config.Operation, dev config.Device[api.Vehi site.publishVehicles() } -// var _ site.Vehicles = (*vehicles)(nil) +var _ site.Vehicles = (*vehicles)(nil) -// type vehicles struct { -// log *util.Logger -// } +type vehicles struct { + log *util.Logger +} -// func (vv *vehicles) Instances() []api.Vehicle { -// devs := config.Vehicles().Devices() +func (vv *vehicles) Instances() []api.Vehicle { + devs := config.Vehicles().Devices() -// res := make([]api.Vehicle, 0, len(devs)) -// for _, dev := range devs { -// res = append(res, dev.Instance()) -// } + res := make([]api.Vehicle, 0, len(devs)) + for _, dev := range devs { + res = append(res, dev.Instance()) + } -// return res -// } + return res +} -// func (vv *vehicles) Settings() []vehicle.API { -// devs := config.Vehicles().Devices() +func (vv *vehicles) Settings() []vehicle.API { + devs := config.Vehicles().Devices() -// res := make([]vehicle.API, 0, len(devs)) -// for _, dev := range devs { -// res = append(res, vehicle.Adapter(vv.log, dev)) -// } + res := make([]vehicle.API, 0, len(devs)) + for _, dev := range devs { + res = append(res, vehicle.Adapter(vv.log, dev)) + } -// return res -// } + return res +} -// func (vv *vehicles) ByName(name string) (vehicle.API, error) { -// dev, err := config.Vehicles().ByName(name) -// if err != nil { -// return nil, err -// } +func (vv *vehicles) ByName(name string) (vehicle.API, error) { + dev, err := config.Vehicles().ByName(name) + if err != nil { + return nil, err + } -// return vehicle.Adapter(vv.log, dev), nil -// } + return vehicle.Adapter(vv.log, dev), nil +} From 4068055f45caf36d489bb16f9125c1caf7e816fd Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 08:09:46 +0200 Subject: [PATCH 06/16] wip --- api/api.go | 4 ---- cmd/setup.go | 4 ++-- vehicle/embed.go | 14 ++++++++++---- {api => vehicle}/internal/context.go | 0 vehicle/volvo-connected.go | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) rename {api => vehicle}/internal/context.go (100%) diff --git a/api/api.go b/api/api.go index 39415f98da..332baa4c81 100644 --- a/api/api.go +++ b/api/api.go @@ -5,14 +5,10 @@ import ( "io" "net/url" "time" - - "github.com/evcc-io/evcc/api/internal" ) //go:generate go tool mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,PhaseCurrents,Vehicle,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Tariff -var ContextTitle internal.ContextKey - // Meter provides total active power in W type Meter interface { CurrentPower() (float64, error) diff --git a/cmd/setup.go b/cmd/setup.go index 90f9ee8bf4..3633b7ac55 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -412,7 +412,7 @@ func configureVehicles(static []config.Named, names ...string) error { } eg.Go(func() error { - ctx := context.WithValue(context.Background(), api.ContextTitle, cc.Name) + ctx := context.WithValue(context.Background(), vehicle.CtxDeviceTitle, cc.Name) instance, err := vehicleInstance(ctx, cc) if err != nil { @@ -449,7 +449,7 @@ func configureVehicles(static []config.Named, names ...string) error { title = cc.Name } - ctx := context.WithValue(context.Background(), api.ContextTitle, title) + ctx := context.WithValue(context.Background(), vehicle.CtxDeviceTitle, title) instance, err := vehicleInstance(ctx, cc) if err != nil { diff --git a/vehicle/embed.go b/vehicle/embed.go index a31e537cf5..5f290ba65f 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -4,13 +4,18 @@ import ( "context" "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/vehicle/internal" ) +var CtxDeviceTitle internal.ContextKey + // TODO align phases with OnIdentify type embed struct { - _Title string `mapstructure:"title"` // TODO deprecated - _Icon string `mapstructure:"-"` // TODO deprecated - Title_ string `mapstructure:"-" json:"-"` + _Title string `mapstructure:"title"` // TODO deprecated + _Icon string `mapstructure:"icon"` // TODO deprecated + Title_ string `mapstructure:"-" json:"-"` // TODO deprecated + Icon_ string `mapstructure:"-" json:"-"` // TODO deprecated + Capacity_ float64 `mapstructure:"capacity"` Phases_ int `mapstructure:"phases"` Identifiers_ []string `mapstructure:"identifiers"` @@ -18,8 +23,9 @@ type embed struct { OnIdentify api.ActionConfig `mapstructure:"onIdentify"` } +// withContext extracts the device title from the context func (v embed) withContext(ctx context.Context) *embed { - if title := ctx.Value(api.ContextTitle); title != nil { + if title := ctx.Value(CtxDeviceTitle); title != nil { v.Title_ = title.(string) } return &v diff --git a/api/internal/context.go b/vehicle/internal/context.go similarity index 100% rename from api/internal/context.go rename to vehicle/internal/context.go diff --git a/vehicle/volvo-connected.go b/vehicle/volvo-connected.go index a0a1f56fef..a3182b22b5 100644 --- a/vehicle/volvo-connected.go +++ b/vehicle/volvo-connected.go @@ -41,7 +41,7 @@ func NewVolvoConnectedFromConfig(ctx context.Context, other map[string]interface log := util.NewLogger("volvo-connected").Redact(cc.VIN, cc.VccApiKey) oc := connected.Oauth2Config(cc.Credentials.ID, cc.Credentials.Secret, cc.RedirectUri) - ts, err := auth.NewOauth(ctx, cast.ToString(ctx.Value(api.ContextTitle)), oc) + ts, err := auth.NewOauth(ctx, cast.ToString(ctx.Value(CtxDeviceTitle)), oc) if err != nil { return nil, err } From c2b7049e4442fffab17b730fec97f53b92d2023f Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 08:17:14 +0200 Subject: [PATCH 07/16] wip --- templates/definition/vehicle/niu-e-scooter.yaml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/templates/definition/vehicle/niu-e-scooter.yaml b/templates/definition/vehicle/niu-e-scooter.yaml index e29ed51bb1..47eca07b9c 100644 --- a/templates/definition/vehicle/niu-e-scooter.yaml +++ b/templates/definition/vehicle/niu-e-scooter.yaml @@ -6,8 +6,9 @@ products: group: scooter params: - name: title + deprecated: true - name: icon - default: scooter + deprecated: true - name: user required: true - name: password @@ -21,12 +22,6 @@ params: default: 4 render: | type: niu - {{- if .title }} - title: {{ .title }} - {{- end }} - {{- if .icon }} - icon: {{ .icon }} - {{- end }} user: {{ .user }} # NIU app user password: {{ .password }} # NIU app password serial: {{ .serial }} # NIU E-Scooter serial number like shown in app From f30f888e021944d4d0113f589b8d3df35576cd49 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 08:21:18 +0200 Subject: [PATCH 08/16] wip --- vehicle/embed.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vehicle/embed.go b/vehicle/embed.go index 5f290ba65f..1ad1b40827 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -11,11 +11,8 @@ var CtxDeviceTitle internal.ContextKey // TODO align phases with OnIdentify type embed struct { - _Title string `mapstructure:"title"` // TODO deprecated - _Icon string `mapstructure:"icon"` // TODO deprecated - Title_ string `mapstructure:"-" json:"-"` // TODO deprecated - Icon_ string `mapstructure:"-" json:"-"` // TODO deprecated - + Title_ string `mapstructure:"title" json:"-"` // TODO deprecated + Icon_ string `mapstructure:"icon" json:"-"` // TODO deprecated Capacity_ float64 `mapstructure:"capacity"` Phases_ int `mapstructure:"phases"` Identifiers_ []string `mapstructure:"identifiers"` From 2278e82227d57aa14da962560226a043ad15f173 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 08:23:13 +0200 Subject: [PATCH 09/16] wip --- vehicle/embed.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vehicle/embed.go b/vehicle/embed.go index 1ad1b40827..b4656b15ba 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -11,8 +11,10 @@ var CtxDeviceTitle internal.ContextKey // TODO align phases with OnIdentify type embed struct { - Title_ string `mapstructure:"title" json:"-"` // TODO deprecated - Icon_ string `mapstructure:"icon" json:"-"` // TODO deprecated + _Title string `mapstructure:"title"` // TODO deprecated + _Icon string `mapstructure:"icon"` // TODO deprecated + + Title_ string `mapstructure:"-" json:"-"` Capacity_ float64 `mapstructure:"capacity"` Phases_ int `mapstructure:"phases"` Identifiers_ []string `mapstructure:"identifiers"` From 0204c46b56982e1dcbc8e6f8b2527bc6528abbe3 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 08:33:18 +0200 Subject: [PATCH 10/16] wip --- cmd/setup.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/setup.go b/cmd/setup.go index 3633b7ac55..1b37e293e1 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -368,7 +368,6 @@ func configureChargers(static []config.Named, names ...string) error { } func vehicleInstance(ctx context.Context, cc config.Named) (api.Vehicle, error) { - // TODO move key ctx = util.WithLogger(ctx, util.NewLogger(cc.Name)) props, err := customDevice(cc.Other) From 47424f3a63a33ec961c8e5c85c0aa811c0a73c72 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 08:41:14 +0200 Subject: [PATCH 11/16] wip --- core/helper.go | 19 ------------------- core/site.go | 2 +- core/site_battery.go | 2 +- core/site_circuits.go | 2 +- core/site_optimizer.go | 2 +- core/site_vehicles.go | 2 +- util/config/api.go | 19 +++++++++++++++++++ util/config/config.go | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 util/config/api.go diff --git a/core/helper.go b/core/helper.go index 4035b9716a..59ad7de9a5 100644 --- a/core/helper.go +++ b/core/helper.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/evcc-io/evcc/api" - "github.com/evcc-io/evcc/util/config" ) var ( @@ -47,24 +46,6 @@ func ptrValueEqual[T comparable](a, b *T) bool { return a == nil && b == nil || (*a) == (*b) } -// deviceProperties returns the common device data for the given reference -func deviceProperties[T any](dev config.Device[T]) config.Properties { - if d, ok := dev.(config.ConfigurableDevice[T]); ok { - return d.Properties() - } - return config.Properties{} -} - -// deviceTitleOrName returns device title or name -func deviceTitleOrName[T any](dev config.Device[T]) string { - if d, ok := dev.(config.ConfigurableDevice[T]); ok { - if title := d.Properties().Title; title != "" { - return title - } - } - return dev.Config().Name -} - // circuitMaxPower returns a circuits power limit func circuitMaxPower(circuit api.Circuit) float64 { if circuit == nil { diff --git a/core/site.go b/core/site.go index 44eb0b3ca8..c65d6286a3 100644 --- a/core/site.go +++ b/core/site.go @@ -519,7 +519,7 @@ func (site *Site) collectMeters(key string, meters []config.Device[api.Meter]) [ } } - props := deviceProperties(dev) + props := config.DeviceProperties(dev) mm[i] = measurement{ Title: props.Title, Icon: props.Icon, diff --git a/core/site_battery.go b/core/site_battery.go index aa2b3b21be..4b6fc5babb 100644 --- a/core/site_battery.go +++ b/core/site_battery.go @@ -121,7 +121,7 @@ func (site *Site) batteryMaxSocReached(dev config.Device[api.Meter]) (bool, erro } if _, max := batLimiter.GetSocLimits(); max > 0 && max < 100 && soc >= max { - site.log.DEBUG.Printf("battery %s: limit soc reached (%.0f > %.0f)", deviceTitleOrName(dev), soc, max) + site.log.DEBUG.Printf("battery %s: limit soc reached (%.0f > %.0f)", config.DeviceTitleOrName(dev), soc, max) return true, nil } diff --git a/core/site_circuits.go b/core/site_circuits.go index 1a63054177..e2d678e6e2 100644 --- a/core/site_circuits.go +++ b/core/site_circuits.go @@ -23,7 +23,7 @@ func (site *Site) publishCircuits() { for _, c := range cc { instance := c.Instance() - props := deviceProperties(c) + props := config.DeviceProperties(c) data := circuitStruct{ Title: props.Title, diff --git a/core/site_optimizer.go b/core/site_optimizer.go index 89199c1180..39cc6acfdc 100644 --- a/core/site_optimizer.go +++ b/core/site_optimizer.go @@ -256,7 +256,7 @@ func (site *Site) optimizerUpdate(battery []measurement) error { details.BatteryDetails = append(details.BatteryDetails, batteryDetail{ Type: batteryTypeBattery, Name: dev.Config().Name, - Title: deviceProperties(dev).Title, + Title: config.DeviceProperties(dev).Title, Capacity: *b.Capacity, }) } diff --git a/core/site_vehicles.go b/core/site_vehicles.go index 5186d81716..ab1da9fb8a 100644 --- a/core/site_vehicles.go +++ b/core/site_vehicles.go @@ -53,7 +53,7 @@ func (site *Site) publishVehicles() { Title: instance.GetTitle(), Capacity: instance.Capacity(), Phases: instance.Phases(), - Icon: deviceProperties(dev).Icon, // device meta data + Icon: config.DeviceProperties(dev).Icon, // device meta data MinSoc: v.GetMinSoc(), LimitSoc: v.GetLimitSoc(), MinCurrent: ac.MinCurrent, diff --git a/util/config/api.go b/util/config/api.go new file mode 100644 index 0000000000..63d4207eff --- /dev/null +++ b/util/config/api.go @@ -0,0 +1,19 @@ +package config + +// DeviceProperties returns the common device data for the given reference +func DeviceProperties[T any](dev Device[T]) Properties { + if d, ok := dev.(ConfigurableDevice[T]); ok { + return d.Properties() + } + return Properties{} +} + +// DeviceTitleOrName returns device title or name +func DeviceTitleOrName[T any](dev Device[T]) string { + if d, ok := dev.(ConfigurableDevice[T]); ok { + if title := d.Properties().Title; title != "" { + return title + } + } + return dev.Config().Name +} diff --git a/util/config/config.go b/util/config/config.go index 36340004e7..8eea9b5680 100644 --- a/util/config/config.go +++ b/util/config/config.go @@ -14,7 +14,7 @@ import ( // Config is the database mapping for device configurations // The device prefix ensures unique namespace // -// TODO migrate vehicle and loadpoints to this schema +// TODO migrate loadpoints to this schema type Config struct { ID int `gorm:"primarykey"` Class templates.Class From 9b99d7117a50f90f8682e59d7c52d443d3b53494 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 10:44:59 +0200 Subject: [PATCH 12/16] wip --- vehicle/volvo-connected.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vehicle/volvo-connected.go b/vehicle/volvo-connected.go index a3182b22b5..4471cc7520 100644 --- a/vehicle/volvo-connected.go +++ b/vehicle/volvo-connected.go @@ -8,7 +8,6 @@ import ( "github.com/evcc-io/evcc/plugin/auth" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/vehicle/volvo/connected" - "github.com/spf13/cast" ) // VolvoConnected is an api.Vehicle implementation for Volvo Connected Car vehicles @@ -39,9 +38,10 @@ func NewVolvoConnectedFromConfig(ctx context.Context, other map[string]interface } log := util.NewLogger("volvo-connected").Redact(cc.VIN, cc.VccApiKey) + embed := cc.embed.withContext(ctx) oc := connected.Oauth2Config(cc.Credentials.ID, cc.Credentials.Secret, cc.RedirectUri) - ts, err := auth.NewOauth(ctx, cast.ToString(ctx.Value(CtxDeviceTitle)), oc) + ts, err := auth.NewOauth(ctx, embed.GetTitle(), oc) if err != nil { return nil, err } @@ -51,7 +51,7 @@ func NewVolvoConnectedFromConfig(ctx context.Context, other map[string]interface cc.VIN, err = ensureVehicle(cc.VIN, api.Vehicles) v := &VolvoConnected{ - embed: cc.embed.withContext(ctx), + embed: embed, Provider: connected.NewProvider(api, cc.VIN, cc.Cache), } From f41a4fad3f302058b0919b65c3501b9954fe3e4a Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 10:51:43 +0200 Subject: [PATCH 13/16] wip --- api/mock.go | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/api/mock.go b/api/mock.go index 8c72708327..1e18892677 100644 --- a/api/mock.go +++ b/api/mock.go @@ -554,20 +554,6 @@ func (mr *MockVehicleMockRecorder) GetTitle() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTitle", reflect.TypeOf((*MockVehicle)(nil).GetTitle)) } -// Icon mocks base method. -func (m *MockVehicle) Icon() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Icon") - ret0, _ := ret[0].(string) - return ret0 -} - -// Icon indicates an expected call of Icon. -func (mr *MockVehicleMockRecorder) Icon() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Icon", reflect.TypeOf((*MockVehicle)(nil).Icon)) -} - // Identifiers mocks base method. func (m *MockVehicle) Identifiers() []string { m.ctrl.T.Helper() @@ -610,18 +596,6 @@ func (mr *MockVehicleMockRecorder) Phases() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Phases", reflect.TypeOf((*MockVehicle)(nil).Phases)) } -// SetTitle mocks base method. -func (m *MockVehicle) SetTitle(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetTitle", arg0) -} - -// SetTitle indicates an expected call of SetTitle. -func (mr *MockVehicleMockRecorder) SetTitle(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTitle", reflect.TypeOf((*MockVehicle)(nil).SetTitle), arg0) -} - // Soc mocks base method. func (m *MockVehicle) Soc() (float64, error) { m.ctrl.T.Helper() From 1750deeb0d92c3edf12731a3e6b68e9d9f949e60 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 10:56:53 +0200 Subject: [PATCH 14/16] wip --- core/loadpoint_vehicle_test.go | 3 --- templates/definition/vehicle/audi.yaml | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core/loadpoint_vehicle_test.go b/core/loadpoint_vehicle_test.go index 4c1ea44905..672497a10c 100644 --- a/core/loadpoint_vehicle_test.go +++ b/core/loadpoint_vehicle_test.go @@ -19,7 +19,6 @@ import ( func expectVehiclePublish(vehicle *api.MockVehicle) { vehicle.EXPECT().GetTitle().Return("target").AnyTimes() vehicle.EXPECT().Capacity().AnyTimes() - vehicle.EXPECT().Icon().AnyTimes() vehicle.EXPECT().Features().AnyTimes() vehicle.EXPECT().Phases().AnyTimes() vehicle.EXPECT().OnIdentified().AnyTimes() @@ -157,7 +156,6 @@ func TestDefaultVehicle(t *testing.T) { dflt := api.NewMockVehicle(ctrl) dflt.EXPECT().GetTitle().Return("default").AnyTimes() - dflt.EXPECT().Icon().Return("").AnyTimes() dflt.EXPECT().Capacity().AnyTimes() dflt.EXPECT().Phases().AnyTimes() dflt.EXPECT().OnIdentified().Return(api.ActionConfig{ @@ -167,7 +165,6 @@ func TestDefaultVehicle(t *testing.T) { vehicle := api.NewMockVehicle(ctrl) vehicle.EXPECT().GetTitle().Return("target").AnyTimes() - vehicle.EXPECT().Icon().Return("").AnyTimes() vehicle.EXPECT().Capacity().AnyTimes() vehicle.EXPECT().Phases().AnyTimes() vehicle.EXPECT().OnIdentified().AnyTimes() diff --git a/templates/definition/vehicle/audi.yaml b/templates/definition/vehicle/audi.yaml index 91b2b5deb6..3f2edc92c0 100644 --- a/templates/definition/vehicle/audi.yaml +++ b/templates/definition/vehicle/audi.yaml @@ -7,5 +7,5 @@ params: - name: vin example: WAUZZZ... render: | - type: etron + type: audi {{ include "vehicle-base" . }} From 66231b0789de3f1e6a6c29f52cda1fd563525f6a Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 11:54:37 +0200 Subject: [PATCH 15/16] wip --- vehicle/embed.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vehicle/embed.go b/vehicle/embed.go index b4656b15ba..6a9d897be0 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -11,8 +11,9 @@ var CtxDeviceTitle internal.ContextKey // TODO align phases with OnIdentify type embed struct { - _Title string `mapstructure:"title"` // TODO deprecated - _Icon string `mapstructure:"icon"` // TODO deprecated + // TODO deprecated + _Title string `mapstructure:"title"` //nolint:unused + _Icon string `mapstructure:"icon"` //nolint:unused Title_ string `mapstructure:"-" json:"-"` Capacity_ float64 `mapstructure:"capacity"` From 477d4430073340faf6ad104c96c4d00ced8826d9 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 9 Oct 2025 13:48:21 +0200 Subject: [PATCH 16/16] wip --- core/site/vehicles.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/site/vehicles.go b/core/site/vehicles.go index 90f01960f7..9bb8bbfd84 100644 --- a/core/site/vehicles.go +++ b/core/site/vehicles.go @@ -12,6 +12,6 @@ type Vehicles interface { // ByName returns a single vehicle adapter by name ByName(string) (vehicle.API, error) - // Instance returns the list of vehicle instances + // Instances returns the list of vehicle instances Instances() []api.Vehicle }