Skip to content

Commit

Permalink
BMW/Mini: add hcaptcha (#17445)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickTop87 authored Nov 26, 2024
1 parent d7e87bf commit 9754bb9
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 5 deletions.
9 changes: 9 additions & 0 deletions templates/definition/vehicle/bmw.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
template: bmw
products:
- brand: BMW
requirements:
description:
de: |
Benötigt `hcaptcha` Token. Dieses muss einmalig unter https://bimmer-connected.readthedocs.io/en/latest/captcha/rest_of_world.html generiert werden. Das Token ist nur für kurze Zeit gültig. Bitte möglichst schnell nach Generierung in die Konfiguration kopieren und evcc starten.
en: |
Requires `hcaptcha` token. This must be generated once at https://bimmer-connected.readthedocs.io/en/latest/captcha/rest_of_world.html. The token is only valid for a short time. Please copy it into the configuration and start evcc as soon as possible after generation.
params:
- preset: vehicle-base
- preset: vehicle-identify
Expand All @@ -19,6 +25,8 @@ params:
advanced: true
- name: welcomecharge
advanced: true
- name: hcaptcha
required: true
render: |
type: bmw
{{ include "vehicle-base" . }}
Expand All @@ -35,3 +43,4 @@ render: |
- welcomecharge
{{- end }}
{{- end }}
hcaptcha: {{ .hcaptcha }}
9 changes: 9 additions & 0 deletions templates/definition/vehicle/mini.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
template: mini
products:
- brand: Mini
requirements:
description:
de: |
Benötigt `hcaptcha` Token. Dieses muss einmalig unter https://bimmer-connected.readthedocs.io/en/latest/captcha/rest_of_world.html generiert werden. Das Token ist nur für kurze Zeit gültig. Bitte möglichst schnell nach Generierung in die Konfiguration kopieren und evcc starten.
en: |
Requires `hcaptcha` token. This must be generated once at https://bimmer-connected.readthedocs.io/en/latest/captcha/rest_of_world.html. The token is only valid for a short time. Please copy it into the configuration and start evcc as soon as possible after generation.
params:
- preset: vehicle-base
- preset: vehicle-identify
Expand All @@ -17,6 +23,8 @@ params:
advanced: true
- name: welcomecharge
advanced: true
- name: hcaptcha
required: true
render: |
type: mini
{{ include "vehicle-base" . }}
Expand All @@ -27,3 +35,4 @@ render: |
{{- if eq .welcomecharge "true" }}
features: ["welcomecharge"]
{{- end }}
hcaptcha: {{ .hcaptcha }}
5 changes: 3 additions & 2 deletions vehicle/bmw.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func NewBMWMiniFromConfig(brand string, other map[string]interface{}) (api.Vehic
cc := struct {
embed `mapstructure:",squash"`
User, Password, VIN string
Hcaptcha string
Region string
Cache time.Duration
}{
Expand All @@ -45,7 +46,7 @@ func NewBMWMiniFromConfig(brand string, other map[string]interface{}) (api.Vehic
return nil, err
}

if cc.User == "" || cc.Password == "" {
if cc.User == "" || cc.Password == "" || cc.Hcaptcha == "" {
return nil, api.ErrMissingCredentials
}

Expand All @@ -56,7 +57,7 @@ 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, cc.Region)

ts, err := identity.Login(cc.User, cc.Password)
ts, err := identity.Login(cc.User, cc.Password, cc.Hcaptcha)
if err != nil {
return nil, err
}
Expand Down
40 changes: 37 additions & 3 deletions vehicle/bmw/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"time"

"github.com/evcc-io/evcc/server/db/settings"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/oauth"
"github.com/evcc-io/evcc/util/request"
Expand All @@ -23,22 +24,41 @@ const (
type Identity struct {
*request.Helper
region Region
log *util.Logger
user string
}

// NewIdentity creates BMW identity
func NewIdentity(log *util.Logger, region string) *Identity {
v := &Identity{
Helper: request.NewHelper(log),
region: regions[strings.ToUpper(region)],
log: log,
}

return v
}

func (v *Identity) Login(user, password string) (oauth2.TokenSource, error) {
func (v *Identity) Login(user, password, hcaptcha string) (oauth2.TokenSource, error) {
v.Client.CheckRedirect = request.DontFollow
defer func() { v.Client.CheckRedirect = nil }()

v.user = user

// database token
var tok oauth2.Token
if err := settings.Json(v.settingsKey(), &tok); err == nil {
v.log.DEBUG.Println("identity.Login - database token found")
tok, err := v.RefreshToken(&tok)
if err == nil {
ts := oauth2.ReuseTokenSourceWithExpiry(tok, oauth.RefreshTokenSource(tok, v), 15*time.Minute)
return ts, nil
}
v.log.DEBUG.Println("identity.Login - database token invalid. Proceeding to login via user, password and captcha.")
} else {
v.log.DEBUG.Println("identity.Login - no database token found. Proceeding to login via user, password and captcha.")
}

cv := oauth2.GenerateVerifier()

v.Jar, _ = cookiejar.New(&cookiejar.Options{
Expand All @@ -60,7 +80,11 @@ func (v *Identity) Login(user, password string) (oauth2.TokenSource, error) {
}

uri := fmt.Sprintf("%s/oauth/authenticate", v.region.AuthURI)
req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), request.URLEncoding)
headers := map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
"hcaptchatoken": hcaptcha,
}
req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), headers)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -141,9 +165,15 @@ func (v *Identity) retrieveToken(data url.Values) (*oauth2.Token, error) {
var tok oauth2.Token
if err == nil {
err = v.DoJSON(req, &tok)

Check failure on line 167 in vehicle/bmw/identity.go

View workflow job for this annotation

GitHub Actions / Lint

ineffectual assignment to err (ineffassign)
} else {
return nil, err
}

return util.TokenWithExpiry(&tok), err
tokex := util.TokenWithExpiry(&tok)

err = settings.SetJson(v.settingsKey(), tokex)

return tokex, err
}

func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
Expand All @@ -155,3 +185,7 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {

return v.retrieveToken(data)
}

func (v *Identity) settingsKey() string {
return fmt.Sprintf("bmw.%s", v.user)
}

0 comments on commit 9754bb9

Please sign in to comment.