Hyundai/Kia: fix token refresh (#24580)
This commit is contained in:
parent
aa82947700
commit
3fd38808a5
5 changed files with 32 additions and 100 deletions
|
|
@ -6,11 +6,11 @@ products:
|
|||
requirements:
|
||||
description:
|
||||
en: |
|
||||
Instead of your account's password, the password field needs to be filled with a `refresh_token` ([instructions](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh%E2%80%90Token-ermitteln#english-version)).
|
||||
Instead of your account's password, the password field needs to be filled with a `refresh_token` ([instructions](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh-Token)).
|
||||
|
||||
Some models (e.g. Kona) switch internally to 2 phases at low charging currents (< 8A). In cases where the wallbox also measures the phase currents, this leads to undesirable fluctuations in the charging power. The remedy here is to set the minimum charging current to 8A.
|
||||
de: |
|
||||
Anstelle des Passworts muss in das Passwort-Feld ein `refresh_token` eingetragen werden ([Anleitung](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh%E2%80%90Token-ermitteln)).
|
||||
Anstelle des Passworts muss in das Passwort-Feld ein `refresh_token` eingetragen werden ([Anleitung](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh-Token)).
|
||||
|
||||
Manche Modelle (z.B. Kona) schalten bei geringen Ladeströmen (< 8A) intern auf 2 Phasen um. In den Fällen, in denen die Wallbox auch die Phasenströme misst, führt das zu unerwünschten Schwankungen der Ladeleistung. Abhilfe schafft hier, den Mindestladestrom auf 8A zu setzen.
|
||||
params:
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ products:
|
|||
requirements:
|
||||
description:
|
||||
en: |
|
||||
Instead of your account's password, the password field needs to be filled with a `refresh_token` ([instructions](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh%E2%80%90Token-ermitteln#english-version)).
|
||||
Instead of your account's password, the password field needs to be filled with a `refresh_token` ([instructions](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh-Token)).
|
||||
|
||||
Some models (e.g. Niro EV) switch internally to 2 phases at low charging currents (< 8A). In cases where the wallbox also measures the phase currents, this leads to undesirable fluctuations in the charging power. The remedy here is to set the minimum charging current to 8A.
|
||||
de: |
|
||||
Anstelle des Passworts muss in das Passwort-Feld ein `refresh_token` eingetragen werden ([Anleitung](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh%E2%80%90Token-ermitteln)).
|
||||
Anstelle des Passworts muss in das Passwort-Feld ein `refresh_token` eingetragen werden ([Anleitung](https://github.com/evcc-io/evcc/wiki/Hyundai-Kia:-Refresh-Token)).
|
||||
|
||||
Manche Modelle (z.B. Niro EV) schalten bei geringen Ladeströmen (< 8A) intern auf 2 Phasen um. In den Fällen, in denen die Wallbox auch die Phasenströme misst, führt das zu unerwünschten Schwankungen der Ladeleistung. Abhilfe schafft hier, den Mindestladestrom auf 8A zu setzen.
|
||||
params:
|
||||
|
|
|
|||
|
|
@ -26,15 +26,14 @@ func init() {
|
|||
func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
settings := bluelink.Config{
|
||||
URI: "https://prd.eu-ccapi.hyundai.com:8080",
|
||||
BasicToken: "KUy49XxPzLpLuoK0xhBC77W6VXhmtQR9iQhmIFjjoY4IpxsV",
|
||||
CCSPServiceID: "6d477c38-3ca4-4cf3-9557-2a1929a94654",
|
||||
CCSPApplicationID: bluelink.HyundaiAppID,
|
||||
AuthClientID: "64621b96-0f0d-11ec-82a8-0242ac130003",
|
||||
PushType: "GCM",
|
||||
CCSPServiceSecret: "KUy49XxPzLpLuoK0xhBC77W6VXhmtQR9iQhmIFjjoY4IpxsV",
|
||||
CCSPApplicationID: "014d2225-8495-4735-812d-2616334fd15d",
|
||||
Cfb: "RFtoRq/vDXJmRndoZaZQyfOot7OrIqGVFj96iY2WL3yyH5Z/pUvlUhqmCxD2t+D65SQ=",
|
||||
Brand: "hyundai",
|
||||
BasicToken: "NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==",
|
||||
PushType: "GCM",
|
||||
LoginFormHost: "https://idpconnect-eu.hyundai.com",
|
||||
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",
|
||||
Brand: "hyundai",
|
||||
}
|
||||
|
||||
return newBluelinkFromConfig("hyundai", other, settings)
|
||||
|
|
@ -44,14 +43,13 @@ func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
settings := bluelink.Config{
|
||||
URI: "https://prd.eu-ccapi.kia.com:8080",
|
||||
BasicToken: "ZmRjODVjMDAtMGEyZi00YzY0LWJjYjQtMmNmYjE1MDA3MzBhOnNlY3JldA==",
|
||||
CCSPServiceID: "fdc85c00-0a2f-4c64-bcb4-2cfb1500730a",
|
||||
CCSPApplicationID: bluelink.KiaAppID,
|
||||
AuthClientID: "fdc85c00-0a2f-4c64-bcb4-2cfb1500730a",
|
||||
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",
|
||||
PushType: "APNS",
|
||||
CCSPServiceSecret: "secret",
|
||||
CCSPApplicationID: "a2b8469b-30a3-4361-8e13-6fceea8fbe74",
|
||||
Cfb: "wLTVxwidmH8CfJYBWSnHD6E0huk0ozdiuygB4hLkM5XCgzAL1Dk5sE36d/bx5PFMbZs=",
|
||||
BasicToken: "ZmRjODVjMDAtMGEyZi00YzY0LWJjYjQtMmNmYjE1MDA3MzBhOnNlY3JldA==",
|
||||
LoginFormHost: "https://idpconnect-eu.kia.com",
|
||||
PushType: "APNS",
|
||||
Brand: "kia",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,17 +25,16 @@ const (
|
|||
SilentSigninURL = "/api/v1/user/silentsignin"
|
||||
LanguageURL = "/api/v1/user/language"
|
||||
LoginURL = "/api/v1/user/signin"
|
||||
TokenURL = "/api/v1/user/oauth2/token"
|
||||
TokenURL = "/auth/api/v2/user/oauth2/token"
|
||||
)
|
||||
|
||||
// Config is the bluelink API configuration
|
||||
type Config struct {
|
||||
URI string
|
||||
AuthClientID string // v2
|
||||
BrandAuthUrl string // v2
|
||||
BasicToken string
|
||||
CCSPServiceID string
|
||||
CCSPApplicationID string
|
||||
CCSPServiceSecret string
|
||||
PushType string
|
||||
Cfb string
|
||||
LoginFormHost string
|
||||
|
|
@ -103,71 +102,20 @@ func (v *Identity) getDeviceID() (string, error) {
|
|||
return res.ResMsg.DeviceID, err
|
||||
}
|
||||
|
||||
func (v *Identity) exchangeCodeKiaEURefreshToken(accCode string) (*oauth2.Token, error) {
|
||||
uri := v.config.LoginFormHost + "/auth/api/v2/user/oauth2/token"
|
||||
// RefreshToken implements oauth.TokenRefresher
|
||||
func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
|
||||
var res oauth2.Token
|
||||
|
||||
uri := v.config.LoginFormHost + TokenURL
|
||||
headers := map[string]string{
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 4.1.1; Galaxy Nexus Build/JRO03C) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19_CCS_APP_AOS",
|
||||
}
|
||||
data := url.Values{
|
||||
"grant_type": {"refresh_token"},
|
||||
"refresh_token": {accCode},
|
||||
"refresh_token": {token.RefreshToken},
|
||||
"client_id": {v.config.CCSPServiceID},
|
||||
"client_secret": {"secret"},
|
||||
}
|
||||
|
||||
var token oauth2.Token
|
||||
|
||||
req, _ := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), headers)
|
||||
err := v.DoJSON(req, &token)
|
||||
|
||||
// manually set the refresh token
|
||||
token.RefreshToken = accCode
|
||||
|
||||
return util.TokenWithExpiry(&token), err
|
||||
}
|
||||
|
||||
// RefreshToken implements oauth.TokenRefresher
|
||||
func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
|
||||
var res oauth2.Token
|
||||
var err error
|
||||
var uri string
|
||||
var headers map[string]string
|
||||
var data url.Values
|
||||
switch v.config.Brand {
|
||||
case "hyundai":
|
||||
uri = v.config.URI + TokenURL
|
||||
headers = map[string]string{
|
||||
"Authorization": "Basic " + v.config.BasicToken,
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": "okhttp/3.10.0",
|
||||
}
|
||||
|
||||
data = url.Values{
|
||||
"grant_type": {"refresh_token"},
|
||||
"redirect_uri": {"https://www.getpostman.com/oauth2/callback"},
|
||||
"refresh_token": {token.RefreshToken},
|
||||
}
|
||||
|
||||
case "kia":
|
||||
uri = v.config.LoginFormHost + "/auth/api/v2/user/oauth2/token"
|
||||
headers = map[string]string{
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 4.1.1; Galaxy Nexus Build/JRO03C) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19_CCS_APP_AOS",
|
||||
}
|
||||
data = url.Values{
|
||||
"grant_type": {"refresh_token"},
|
||||
"refresh_token": {token.RefreshToken},
|
||||
"client_id": {v.config.CCSPServiceID},
|
||||
"client_secret": {"secret"},
|
||||
}
|
||||
default:
|
||||
err = errors.New("unsupported brand")
|
||||
}
|
||||
|
||||
// request token only if we didn't run unto the default branch
|
||||
if err != nil {
|
||||
return nil, err
|
||||
"client_secret": {v.config.CCSPServiceSecret},
|
||||
}
|
||||
|
||||
req, err := request.New(http.MethodPost, uri, strings.NewReader(data.Encode()), headers)
|
||||
|
|
@ -188,31 +136,23 @@ func (v *Identity) Login(user, password, language, brand string) (err error) {
|
|||
if user == "" || password == "" {
|
||||
return api.ErrMissingCredentials
|
||||
}
|
||||
// var code string
|
||||
|
||||
switch brand {
|
||||
case "kia":
|
||||
// the "password" now is the refresh token ...
|
||||
var token *oauth2.Token
|
||||
token, err = v.exchangeCodeKiaEURefreshToken(password)
|
||||
if err == nil {
|
||||
v.TokenSource = oauth.RefreshTokenSource(token, v)
|
||||
v.deviceID, err = v.getDeviceID()
|
||||
}
|
||||
|
||||
case "hyundai":
|
||||
var token *oauth2.Token
|
||||
token, err = v.exchangeCodeKiaEURefreshToken(password)
|
||||
if err == nil {
|
||||
v.TokenSource = oauth.RefreshTokenSource(token, v)
|
||||
v.deviceID, err = v.getDeviceID()
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unknown brand (%s)", brand)
|
||||
return fmt.Errorf("unknown brand (%s)", brand)
|
||||
}
|
||||
|
||||
token, err := v.RefreshToken(&oauth2.Token{RefreshToken: password})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("login failed: %w", err)
|
||||
return fmt.Errorf("login failed: %w", err)
|
||||
}
|
||||
v.TokenSource = oauth.RefreshTokenSource(token, v)
|
||||
|
||||
v.deviceID, err = v.getDeviceID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting device id: %w", err)
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
package bluelink
|
||||
|
||||
const (
|
||||
KiaAppID = "a2b8469b-30a3-4361-8e13-6fceea8fbe74"
|
||||
HyundaiAppID = "014d2225-8495-4735-812d-2616334fd15d"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue