BMW/Mini: add regions support (#9865)
This commit is contained in:
parent
3524066604
commit
8abaea79b4
6 changed files with 83 additions and 17 deletions
|
|
@ -6,7 +6,18 @@ params:
|
|||
- preset: vehicle-identify
|
||||
- name: vin
|
||||
example: WBMW...
|
||||
- name: region
|
||||
description:
|
||||
de: Region
|
||||
en: Region
|
||||
values:
|
||||
- NA
|
||||
- EU
|
||||
default: EU
|
||||
render: |
|
||||
type: bmw
|
||||
{{ include "vehicle-base" . }}
|
||||
{{ include "vehicle-identify" . }}
|
||||
{{- if ne .region "EU" }}
|
||||
region: {{ .region }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,18 @@ params:
|
|||
- preset: vehicle-identify
|
||||
- name: vin
|
||||
example: WBMW...
|
||||
- name: region
|
||||
description:
|
||||
de: Region
|
||||
en: Region
|
||||
values:
|
||||
- NA
|
||||
- EU
|
||||
default: EU
|
||||
render: |
|
||||
type: mini
|
||||
{{ include "vehicle-base" . }}
|
||||
{{ include "vehicle-identify" . }}
|
||||
{{- if ne .region "EU" }}
|
||||
region: {{ .region }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -34,9 +34,11 @@ func NewBMWMiniFromConfig(brand string, other map[string]interface{}) (api.Vehic
|
|||
cc := struct {
|
||||
embed `mapstructure:",squash"`
|
||||
User, Password, VIN string
|
||||
Region string
|
||||
Cache time.Duration
|
||||
}{
|
||||
Cache: interval,
|
||||
Region: "EU",
|
||||
Cache: interval,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
|
|
@ -52,14 +54,14 @@ func NewBMWMiniFromConfig(brand string, other map[string]interface{}) (api.Vehic
|
|||
}
|
||||
|
||||
log := util.NewLogger(brand).Redact(cc.User, cc.Password, cc.VIN)
|
||||
identity := bmw.NewIdentity(log)
|
||||
identity := bmw.NewIdentity(log, cc.Region)
|
||||
|
||||
ts, err := identity.Login(cc.User, cc.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
api := bmw.NewAPI(log, brand, ts)
|
||||
api := bmw.NewAPI(log, brand, cc.Region, ts)
|
||||
|
||||
cc.VIN, err = ensureVehicle(cc.VIN, api.Vehicles)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package bmw
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
|
|
@ -13,21 +14,19 @@ import (
|
|||
// https://github.com/bimmerconnected/bimmer_connected
|
||||
// https://github.com/TA2k/ioBroker.bmw
|
||||
|
||||
const (
|
||||
CocoApiURI = "https://cocoapi.bmwgroup.com"
|
||||
)
|
||||
|
||||
// API is an api.Vehicle implementation for BMW cars
|
||||
type API struct {
|
||||
*request.Helper
|
||||
xUserAgent string
|
||||
region string
|
||||
}
|
||||
|
||||
// NewAPI creates a new vehicle
|
||||
func NewAPI(log *util.Logger, brand string, identity oauth2.TokenSource) *API {
|
||||
func NewAPI(log *util.Logger, brand, region string, identity oauth2.TokenSource) *API {
|
||||
v := &API{
|
||||
Helper: request.NewHelper(log),
|
||||
xUserAgent: fmt.Sprintf("android(SP1A.210812.016.C1);%s;99.0.0(99999);row", brand),
|
||||
region: strings.ToUpper(region),
|
||||
}
|
||||
|
||||
// replace client transport with authenticated transport
|
||||
|
|
@ -42,7 +41,7 @@ func NewAPI(log *util.Logger, brand string, identity oauth2.TokenSource) *API {
|
|||
// Vehicles implements returns the /user/vehicles api
|
||||
func (v *API) Vehicles() ([]string, error) {
|
||||
var res []Vehicle
|
||||
uri := fmt.Sprintf("%s/eadrax-vcs/v4/vehicles?apptimezone=120&appDateTime=%d", CocoApiURI, time.Now().UnixMilli())
|
||||
uri := fmt.Sprintf("%s/eadrax-vcs/v4/vehicles?apptimezone=120&appDateTime=%d", regions[v.region].CocoApiURI, time.Now().UnixMilli())
|
||||
|
||||
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
|
||||
"Content-Type": request.JSONContent,
|
||||
|
|
@ -67,7 +66,7 @@ func (v *API) Vehicles() ([]string, error) {
|
|||
// Status implements the /user/vehicles/<vin>/status api
|
||||
func (v *API) Status(vin string) (VehicleStatus, error) {
|
||||
var res VehicleStatus
|
||||
uri := fmt.Sprintf("%s/eadrax-vcs/v4/vehicles/state?apptimezone=120&appDateTime=%d", CocoApiURI, time.Now().UnixMilli())
|
||||
uri := fmt.Sprintf("%s/eadrax-vcs/v4/vehicles/state?apptimezone=120&appDateTime=%d", regions[v.region].CocoApiURI, time.Now().UnixMilli())
|
||||
|
||||
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
|
||||
"Content-Type": request.JSONContent,
|
||||
|
|
|
|||
|
|
@ -18,18 +18,19 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
AuthURI = "https://customer.bmwgroup.com/gcdm/oauth"
|
||||
RedirectURI = "com.bmw.connected://oauth"
|
||||
)
|
||||
|
||||
type Identity struct {
|
||||
*request.Helper
|
||||
region string
|
||||
}
|
||||
|
||||
// NewIdentity creates BMW identity
|
||||
func NewIdentity(log *util.Logger) *Identity {
|
||||
func NewIdentity(log *util.Logger, region string) *Identity {
|
||||
v := &Identity{
|
||||
Helper: request.NewHelper(log),
|
||||
region: strings.ToUpper(region),
|
||||
}
|
||||
|
||||
return v
|
||||
|
|
@ -52,10 +53,10 @@ func (v *Identity) Login(user, password string) (oauth2.TokenSource, error) {
|
|||
}
|
||||
|
||||
data := url.Values{
|
||||
"client_id": {"31c357a0-7a1d-4590-aa99-33b97244d048"},
|
||||
"client_id": {regions[v.region].ClientID},
|
||||
"response_type": {"code"},
|
||||
"redirect_uri": {RedirectURI},
|
||||
"state": {"cwU-gIE27j67poy2UcL3KQ"},
|
||||
"state": {regions[v.region].State},
|
||||
"scope": {"openid profile email offline_access smacc vehicle_data perseus dlm svds cesim vsapi remote_services fupo authenticate_user"},
|
||||
"nonce": {"login_nonce"},
|
||||
"code_challenge_method": {"S256"},
|
||||
|
|
@ -65,7 +66,7 @@ func (v *Identity) Login(user, password string) (oauth2.TokenSource, error) {
|
|||
"grant_type": {"authorization_code"},
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/authenticate", AuthURI)
|
||||
uri := fmt.Sprintf("%s/oauth/authenticate", regions[v.region].AuthURI)
|
||||
req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), request.URLEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -133,10 +134,10 @@ func (v *Identity) Login(user, password string) (oauth2.TokenSource, error) {
|
|||
}
|
||||
|
||||
func (v *Identity) retrieveToken(data url.Values) (*oauth2.Token, error) {
|
||||
uri := fmt.Sprintf("%s/token", AuthURI)
|
||||
uri := fmt.Sprintf("%s/oauth/token", regions[v.region].AuthURI)
|
||||
req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), map[string]string{
|
||||
"Content-Type": request.FormContent,
|
||||
"Authorization": "Basic MzFjMzU3YTAtN2ExZC00NTkwLWFhOTktMzNiOTcyNDRkMDQ4OmMwZTMzOTNkLTcwYTItNGY2Zi05ZDNjLTg1MzBhZjY0ZDU1Mg==",
|
||||
"Authorization": regions[v.region].Authorization,
|
||||
})
|
||||
|
||||
var tok oauth.Token
|
||||
|
|
|
|||
42
vehicle/bmw/param.go
Normal file
42
vehicle/bmw/param.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package bmw
|
||||
|
||||
type (
|
||||
Region struct {
|
||||
AuthURI, CocoApiURI string
|
||||
Token
|
||||
Authenticate
|
||||
}
|
||||
Token struct {
|
||||
Authorization string
|
||||
}
|
||||
Authenticate struct {
|
||||
ClientID, State string
|
||||
}
|
||||
)
|
||||
|
||||
var regions = map[string]Region{
|
||||
"NA": {
|
||||
"https://login.bmwusa.com/gcdm",
|
||||
"https://cocoapi.bmwgroup.us",
|
||||
Token{
|
||||
Authorization: "Basic NTQzOTRhNGItYjZjMS00NWZlLWI3YjItOGZkM2FhOTI1M2FhOmQ5MmYzMWMwLWY1NzktNDRmNS1hNzdkLTk2NmY4ZjAwZTM1MQ==",
|
||||
// Verifier: "KDarcVUpgymBDCgHDH0PwwMfzycDxu1joeklioOhwXA",
|
||||
},
|
||||
Authenticate{
|
||||
ClientID: "54394a4b-b6c1-45fe-b7b2-8fd3aa9253aa",
|
||||
State: "rgastJbZsMtup49-Lp0FMQ",
|
||||
},
|
||||
},
|
||||
"EU": {
|
||||
"https://customer.bmwgroup.com/gcdm",
|
||||
"https://cocoapi.bmwgroup.com",
|
||||
Token{
|
||||
Authorization: "Basic MzFjMzU3YTAtN2ExZC00NTkwLWFhOTktMzNiOTcyNDRkMDQ4OmMwZTMzOTNkLTcwYTItNGY2Zi05ZDNjLTg1MzBhZjY0ZDU1Mg==",
|
||||
// Verifier: "7PsmfPS5MpaNt0jEcPpi-B7M7u0gs1Nzw6ex0Y9pa-0",
|
||||
},
|
||||
Authenticate{
|
||||
ClientID: "31c357a0-7a1d-4590-aa99-33b97244d048",
|
||||
State: "cEG9eLAIi6Nv-aaCAniziE_B6FPoobva3qr5gukilYw",
|
||||
},
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue