From f016a1f26942d446b9cc9a06aef2e0310650716d Mon Sep 17 00:00:00 2001 From: HerrBri Date: Mon, 30 Jun 2025 08:01:35 +0200 Subject: [PATCH] Volvo: add odometer (BC) (#22124) --- templates/definition/vehicle/volvo-connected.yaml | 4 ++-- vehicle/volvo/connected/api.go | 10 ++++++++++ vehicle/volvo/connected/oauth2.go | 1 + vehicle/volvo/connected/provider.go | 12 ++++++++++++ vehicle/volvo/connected/types.go | 11 +++++++++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/templates/definition/vehicle/volvo-connected.yaml b/templates/definition/vehicle/volvo-connected.yaml index 9c68354e2..c85a46c7d 100644 --- a/templates/definition/vehicle/volvo-connected.yaml +++ b/templates/definition/vehicle/volvo-connected.yaml @@ -6,7 +6,7 @@ requirements: de: | Für die Nutzung mit EVCC benötigst du einen Volvo Account und einen Volvo Connected Car API Key. Erstelle dazu auf der [Account Seite](https://developer.volvocars.com/account/) eine neue Applikation und speichere den primären VCC API Key ab. - Veröffentliche nun deine Applikation und wähle unter "Scopes" die Berechtigungen "Connected Vehicle API -> conve:vehicle-relation" und "Energy API -> energy:state:read" aus. + Veröffentliche nun deine Applikation und wähle unter "Scopes" die Berechtigungen "Connected Vehicle API -> conve:vehicle-relation" + "conve:odometer-status" und "Energy API -> energy:state:read" aus. Als Redirect URL musst du die URL deiner EVCC Instanz eintragen, zb "https://evcc.example.org/oauth/callback". Sobald die Applikation erstellt ist, wird sie als "Publication under Review" angezeigt. Das ist nicht weiter schlimm, es funktioniert trotzdem. Beim Anlegen des Fahrzeugs über die UI wird ein Fehler angezeigt. @@ -15,7 +15,7 @@ requirements: en: | To use with EVCC, you need a Volvo account and a Volvo Connected Car API Key. To do this, create a new application on the [Account page](https://developer.volvocars.com/account/) and save the primary VCC API key. - Now publish your application and select the permissions "Connected Vehicle API -> conve:vehicle-relation" and "Energy API -> energy:state:read" under "Scopes". + Now publish your application and select the permissions "Connected Vehicle API -> conve:vehicle-relation" + "conve:odometer-status" and "Energy API -> energy:state:read" under "Scopes". You must enter the URL of your EVCC instance as the redirect URL, e.g. "https://evcc.example.org/oauth/callback". Once the application is created, it will be displayed as "Publication under Review". This is not a problem, it still works. When adding the vehicle via the UI, an error message is displayed. diff --git a/vehicle/volvo/connected/api.go b/vehicle/volvo/connected/api.go index a8bf4130b..8b00d06b4 100644 --- a/vehicle/volvo/connected/api.go +++ b/vehicle/volvo/connected/api.go @@ -59,3 +59,13 @@ func (v *API) EnergyState(vin string) (EnergyState, error) { return res, err } + +// Range provides range status api response +func (v *API) OdometerState(vin string) (OdometerState, error) { + uri := fmt.Sprintf("%s/connected-vehicle/v2/vehicles/%s/odometer", ApiURL, vin) + + var res OdometerState + err := v.GetJSON(uri, &res) + + return res, err +} diff --git a/vehicle/volvo/connected/oauth2.go b/vehicle/volvo/connected/oauth2.go index 41afef61c..34bfe1c1d 100644 --- a/vehicle/volvo/connected/oauth2.go +++ b/vehicle/volvo/connected/oauth2.go @@ -19,6 +19,7 @@ func Oauth2Config(id, secret, redirecturi string) *oauth2.Config { oidc.ScopeOpenID, "conve:vehicle_relation", "energy:state:read", + "conve:odometer_status", }, } } diff --git a/vehicle/volvo/connected/provider.go b/vehicle/volvo/connected/provider.go index 7106584f2..a569610c8 100644 --- a/vehicle/volvo/connected/provider.go +++ b/vehicle/volvo/connected/provider.go @@ -10,6 +10,7 @@ import ( // Provider implements the vehicle api type Provider struct { statusG func() (EnergyState, error) + odoG func() (OdometerState, error) } // NewProvider creates a vehicle api provider @@ -18,6 +19,9 @@ func NewProvider(api *API, vin string, cache time.Duration) *Provider { statusG: util.Cached(func() (EnergyState, error) { return api.EnergyState(vin) }, cache), + odoG: util.Cached(func() (OdometerState, error) { + return api.OdometerState(vin) + }, cache), } return impl } @@ -73,3 +77,11 @@ func (v *Provider) GetLimitSoc() (int64, error) { return int64(res.TargetBatteryChargeLevel.Value), err } + +var _ api.VehicleOdometer = (*Provider)(nil) + +// Odometer implements the api.VehicleOdometer interface +func (v *Provider) Odometer() (float64, error) { + res, err := v.odoG() + return float64(res.Data.Odometer.Value), err +} diff --git a/vehicle/volvo/connected/types.go b/vehicle/volvo/connected/types.go index 75ac9d7b9..27714549e 100644 --- a/vehicle/volvo/connected/types.go +++ b/vehicle/volvo/connected/types.go @@ -64,3 +64,14 @@ type EnergyState struct { type Vehicle struct { VIN string } + +type OdometerState struct { + Data struct { + Odometer struct { + Status string + Value int64 + Unit string + Timestamp time.Time + } + } +}