From a25875d6aebe91bb5719a0995bf173da57407493 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 14 Nov 2021 17:25:50 +0100 Subject: [PATCH] BMW: upgrade api (#1876) --- vehicle/bmw/api.go | 44 +++++++++++++++++------------------------ vehicle/bmw/provider.go | 18 ++++++++--------- vehicle/bmw/types.go | 32 ++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 vehicle/bmw/types.go diff --git a/vehicle/bmw/api.go b/vehicle/bmw/api.go index b7556e774..9ff573569 100644 --- a/vehicle/bmw/api.go +++ b/vehicle/bmw/api.go @@ -3,6 +3,7 @@ package bmw import ( "fmt" "net/http" + "time" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/request" @@ -12,27 +13,11 @@ import ( // https://github.com/bimmerconnected/bimmer_connected // https://github.com/TA2k/ioBroker.bmw -const ApiURI = "https://b2vapi.bmwgroup.com/webapi/v1" - -type StatusResponse struct { - VehicleStatus struct { - ConnectionStatus string // CONNECTED - ChargingStatus string // CHARGING, ERROR, FINISHED_FULLY_CHARGED, FINISHED_NOT_FULL, INVALID, NOT_CHARGING, WAITING_FOR_CHARGING - ChargingLevelHv int - RemainingRangeElectric int - Mileage int - // UpdateTime time.Time // 2021-08-12T12:00:08+0000 - } -} - -type VehiclesResponse struct { - Vehicles []Vehicle -} - -type Vehicle struct { - VIN string - Model string -} +const ( + ApiURI = "https://b2vapi.bmwgroup.com/webapi/v1" + CocoApiURI = "https://cocoapi.bmwgroup.com" + XUserAgent = "android(v1.07_20200330);bmw;1.7.0(11152)" +) // API is an api.Vehicle implementation for BMW cars type API struct { @@ -58,6 +43,7 @@ func NewAPI(log *util.Logger, identity oauth2.TokenSource) *API { func (v *API) Vehicles() ([]string, error) { var resp VehiclesResponse uri := fmt.Sprintf("%s/user/vehicles", ApiURI) + // uri := fmt.Sprintf("%s/eadrax-vcs/v1/vehicles", CocoApiURI, vin) req, err := http.NewRequest(http.MethodGet, uri, nil) if err == nil { @@ -73,14 +59,20 @@ func (v *API) Vehicles() ([]string, error) { } // Status implements the /user/vehicles//status api -func (v *API) Status(vin string) (StatusResponse, error) { - var resp StatusResponse - uri := fmt.Sprintf("%s/user/vehicles/%s/status", ApiURI, vin) +func (v *API) Status(vin string) (VehicleStatus, error) { + var resp VehiclesStatusResponse + uri := fmt.Sprintf("%s/eadrax-vcs/v1/vehicles?apptimezone=60&appDateTime=%d&vin=%s", CocoApiURI, time.Now().Unix(), vin) - req, err := http.NewRequest(http.MethodGet, uri, nil) + req, err := request.New(http.MethodGet, uri, nil, map[string]string{ + "X-User-Agent": XUserAgent, + }) if err == nil { err = v.DoJSON(req, &resp) } - return resp, err + if l := len(resp); l != 1 { + return VehicleStatus{}, fmt.Errorf("unexpected length: %d", l) + } + + return resp[0], err } diff --git a/vehicle/bmw/provider.go b/vehicle/bmw/provider.go index e2d58ee3e..47337dba0 100644 --- a/vehicle/bmw/provider.go +++ b/vehicle/bmw/provider.go @@ -27,8 +27,8 @@ var _ api.Battery = (*Provider)(nil) // SoC implements the api.Vehicle interface func (v *Provider) SoC() (float64, error) { res, err := v.statusG() - if res, ok := res.(StatusResponse); err == nil && ok { - return float64(res.VehicleStatus.ChargingLevelHv), nil + if res, ok := res.(VehicleStatus); err == nil && ok { + return float64(res.Properties.ChargingState.ChargePercentage), nil } return 0, err @@ -41,11 +41,11 @@ func (v *Provider) Status() (api.ChargeStatus, error) { status := api.StatusA // disconnected res, err := v.statusG() - if res, ok := res.(StatusResponse); err == nil && ok { - if res.VehicleStatus.ConnectionStatus == "CONNECTED" { + if res, ok := res.(VehicleStatus); err == nil && ok { + if res.Properties.ChargingState.IsChargerConnected { status = api.StatusB } - if res.VehicleStatus.ChargingStatus == "CHARGING" { + if res.Properties.ChargingState.State == "CHARGING" { status = api.StatusC } } @@ -71,8 +71,8 @@ var _ api.VehicleRange = (*Provider)(nil) // Range implements the api.VehicleRange interface func (v *Provider) Range() (int64, error) { res, err := v.statusG() - if res, ok := res.(StatusResponse); err == nil && ok { - return int64(res.VehicleStatus.RemainingRangeElectric), nil + if res, ok := res.(VehicleStatus); err == nil && ok { + return int64(res.Properties.ElectricRange.Distance.Value), nil } return 0, err @@ -83,8 +83,8 @@ var _ api.VehicleOdometer = (*Provider)(nil) // Odometer implements the api.VehicleOdometer interface func (v *Provider) Odometer() (float64, error) { res, err := v.statusG() - if res, ok := res.(StatusResponse); err == nil && ok { - return float64(res.VehicleStatus.Mileage), nil + if res, ok := res.(VehicleStatus); err == nil && ok { + return float64(res.Status.CurrentMileage.Mileage), nil } return 0, err diff --git a/vehicle/bmw/types.go b/vehicle/bmw/types.go new file mode 100644 index 000000000..677c5e454 --- /dev/null +++ b/vehicle/bmw/types.go @@ -0,0 +1,32 @@ +package bmw + +type VehiclesResponse struct { + Vehicles []Vehicle +} + +type Vehicle struct { + VIN string + Model string +} + +type VehiclesStatusResponse []VehicleStatus + +type VehicleStatus struct { + Properties struct { + ChargingState struct { + ChargePercentage int + State string // CHARGING, ERROR, FINISHED_FULLY_CHARGED, FINISHED_NOT_FULL, INVALID, NOT_CHARGING, WAITING_FOR_CHARGING, COMPLETED + IsChargerConnected bool + } + ElectricRange struct { + Distance struct { + Value int + } + } + } + Status struct { + CurrentMileage struct { + Mileage int + } + } +}