Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Genesis API #13929

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ evcc is an extensible EV Charge Controller and home energy management system. Fe
- Sunspec-compatible inverter or home battery devices: Fronius, SMA, SolarEdge, KOSTAL, STECA, E3DC, ...
- and various others: Discovergy, Tesla PowerWall, LG ESS HOME, OpenEMS (FENECON)
- [vehicle](https://docs.evcc.io/docs/devices/vehicles) integration (state of charge, remote charge, battery and preconditioning status):
- Audi, BMW, Citroën, Dacia, Fiat, Ford, Hyundai, Jaguar, Kia, Landrover, ~~Mercedes~~, Mini, Nissan, Opel, Peugeot, Porsche, Renault, Seat, Smart, Skoda, Tesla, Volkswagen, Volvo, ...
- Audi, BMW, Citroën, Dacia, Fiat, Ford, Genesis, Hyundai, Jaguar, Kia, Landrover, ~~Mercedes~~, Mini, Nissan, Opel, Peugeot, Porsche, Renault, Seat, Smart, Skoda, Tesla, Volkswagen, Volvo, ...
- Services: OVMS, Tronity
- Scooters: Niu, ~~Silence~~
- [plugins](https://docs.evcc.io/docs/reference/plugins) for integrating with any charger/ meter/ vehicle:
Expand Down
14 changes: 14 additions & 0 deletions templates/definition/vehicle/genesis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
template: genesis
products:
- brand: Genesis
description:
generic: Bluelink
params:
- preset: vehicle-base
- preset: vehicle-language
- preset: vehicle-identify
render: |
type: genesis
{{ include "vehicle-base" . }}
{{ include "vehicle-language" . }}
{{ include "vehicle-identify" . }}
29 changes: 26 additions & 3 deletions vehicle/bluelink.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ type Bluelink struct {
func init() {
registry.Add("kia", NewKiaFromConfig)
registry.Add("hyundai", NewHyundaiFromConfig)
registry.Add("genesis", NewGenesisFromConfig)
}

// NewHyundaiFromConfig creates a new vehicle
func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
settings := bluelink.Config{
URI: "https://prd.eu-ccapi.hyundai.com:8080",
AuthURI: "https://prd.eu-ccapi.hyundai.com:8080",
APIURI: "https://prd.eu-ccapi.hyundai.com:8080",
RedirectPath: "/api/v1/user/oauth2/redirect",
BasicToken: "NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==",
CCSPServiceID: "6d477c38-3ca4-4cf3-9557-2a1929a94654",
CCSPApplicationID: bluelink.HyundaiAppID,
Expand All @@ -41,7 +44,9 @@ func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
// NewKiaFromConfig creates a new vehicle
func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
settings := bluelink.Config{
URI: "https://prd.eu-ccapi.kia.com:8080",
AuthURI: "https://prd.eu-ccapi.kia.com:8080",
APIURI: "https://prd.eu-ccapi.kia.com:8080",
RedirectPath: "/api/v1/user/oauth2/redirect",
BasicToken: "ZmRjODVjMDAtMGEyZi00YzY0LWJjYjQtMmNmYjE1MDA3MzBhOnNlY3JldA==",
CCSPServiceID: "fdc85c00-0a2f-4c64-bcb4-2cfb1500730a",
CCSPApplicationID: bluelink.KiaAppID,
Expand All @@ -54,6 +59,24 @@ func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
return newBluelinkFromConfig("kia", other, settings)
}

// NewGenesisFromConfig creates a new vehicle
func NewGenesisFromConfig(other map[string]interface{}) (api.Vehicle, error) {
settings := bluelink.Config{
AuthURI: "https://accounts-eu.genesis.com",
APIURI: "https://prd-eu-ccapi.genesis.com",
RedirectPath: "/realms/eugenesisidm/ga-api/redirect2",
BasicToken: "NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==",
CCSPServiceID: "3020afa2-30ff-412a-aa51-d28fbe901e10",
CCSPApplicationID: bluelink.GenesisAppID,
AuthClientID: "f11f2b86-e0e7-4851-90df-5600b01d8b70",
BrandAuthUrl: "https://accounts-eu.genesis.com/realms/eugenesisidm/protocol/openid-connect/auth?client_id=%s&scope=openid+profile+email+phone&response_type=code&hkid_session_reset=true&redirect_uri=%s/api/v1/user/integration/redirect/login&ui_locales=%s&state=%s:%s",
PushType: "GCM",
Cfb: "RFtoRq/vDXJmRndoZaZQyYo3/qFLtVReW8P7utRPcc0ZxOzOELm9mexvviBk/qqIp4A=",
}

return newBluelinkFromConfig("genesis", other, settings)
}

// newBluelinkFromConfig creates a new Vehicle
func newBluelinkFromConfig(brand string, other map[string]interface{}, settings bluelink.Config) (api.Vehicle, error) {
cc := struct {
Expand All @@ -80,7 +103,7 @@ func newBluelinkFromConfig(brand string, other map[string]interface{}, settings
return nil, err
}

api := bluelink.NewAPI(log, settings.URI, identity.Request)
api := bluelink.NewAPI(log, settings.APIURI, identity.Request)

vehicle, err := ensureVehicleEx(
cc.VIN, api.Vehicles,
Expand Down
2 changes: 1 addition & 1 deletion vehicle/bluelink/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
// ErrAuthFail indicates authorization failure
var ErrAuthFail = errors.New("authorization failed")

// API implements the Kia/Hyundai bluelink api.
// API implements the Kia/Hyundai/Genesis bluelink api.
type API struct {
*request.Helper
baseURI string
Expand Down
28 changes: 16 additions & 12 deletions vehicle/bluelink/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ const (

// Config is the bluelink API configuration
type Config struct {
URI string
AuthURI string
APIURI string
RedirectPath string
AuthClientID string // v2
BrandAuthUrl string // v2
BasicToken string
Expand Down Expand Up @@ -93,7 +95,7 @@ func (v *Identity) getDeviceID() (string, error) {
}
}

req, err := request.New(http.MethodPost, v.config.URI+DeviceIdURL, request.MarshalJSON(data), headers)
req, err := request.New(http.MethodPost, v.config.APIURI+DeviceIdURL, request.MarshalJSON(data), headers)
if err == nil {
err = v.DoJSON(req, &res)
}
Expand All @@ -113,9 +115,9 @@ func (v *Identity) getCookies() (cookieClient *request.Helper, err error) {

uri := fmt.Sprintf(
"%s/api/v1/user/oauth2/authorize?response_type=code&state=test&client_id=%s&redirect_uri=%s/api/v1/user/oauth2/redirect",
v.config.URI,
v.config.APIURI,
v.config.CCSPServiceID,
v.config.URI,
v.config.AuthURI,
)

resp, err := cookieClient.Get(uri)
Expand All @@ -131,7 +133,7 @@ func (v *Identity) setLanguage(cookieClient *request.Helper, language string) er
"lang": language,
}

req, err := request.New(http.MethodPost, v.config.URI+LanguageURL, request.MarshalJSON(data), request.JSONEncoding)
req, err := request.New(http.MethodPost, v.config.APIURI+LanguageURL, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
var resp *http.Response
if resp, err = cookieClient.Do(req); err == nil {
Expand All @@ -143,7 +145,7 @@ func (v *Identity) setLanguage(cookieClient *request.Helper, language string) er
}

func (v *Identity) brandLogin(cookieClient *request.Helper, user, password string) (string, error) {
req, err := request.New(http.MethodGet, v.config.URI+IntegrationInfoURL, nil, request.JSONEncoding)
req, err := request.New(http.MethodGet, v.config.APIURI+IntegrationInfoURL, nil, request.JSONEncoding)

var info struct {
UserId string `json:"userId"`
Expand All @@ -158,7 +160,7 @@ func (v *Identity) brandLogin(cookieClient *request.Helper, user, password strin
var resp *http.Response

if err == nil {
uri := fmt.Sprintf(v.config.BrandAuthUrl, v.config.AuthClientID, v.config.URI, "en", info.ServiceId, info.UserId)
uri := fmt.Sprintf(v.config.BrandAuthUrl, v.config.AuthClientID, v.config.APIURI, "en", info.ServiceId, info.UserId)

req, err = request.New(http.MethodGet, uri, nil)
if err == nil {
Expand Down Expand Up @@ -223,7 +225,7 @@ func (v *Identity) brandLogin(cookieClient *request.Helper, user, password strin
"intUserId": "",
}

req, err = request.New(http.MethodPost, v.config.URI+SilentSigninURL, request.MarshalJSON(data), request.JSONEncoding)
req, err = request.New(http.MethodPost, v.config.AuthURI+SilentSigninURL, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
req.Header.Set("ccsp-service-id", v.config.CCSPServiceID)
cookieClient.CheckRedirect = request.DontFollow
Expand Down Expand Up @@ -252,7 +254,7 @@ func (v *Identity) bluelinkLogin(cookieClient *request.Helper, user, password st
"password": password,
}

req, err := request.New(http.MethodPost, v.config.URI+LoginURL, request.MarshalJSON(data), request.JSONEncoding)
req, err := request.New(http.MethodPost, v.config.APIURI+LoginURL, request.MarshalJSON(data), request.JSONEncoding)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -284,13 +286,14 @@ func (v *Identity) exchangeCode(accCode string) (oauth.Token, error) {

data := url.Values{
"grant_type": {"authorization_code"},
"redirect_uri": {v.config.URI + "/api/v1/user/oauth2/redirect"},
"redirect_uri": {v.config.AuthURI + v.config.RedirectPath},
"code": {accCode},
"client_id": {v.config.CCSPServiceID},
}

var token oauth.Token

req, err := request.New(http.MethodPost, v.config.URI+TokenURL, strings.NewReader(data.Encode()), headers)
req, err := request.New(http.MethodPost, v.config.APIURI+TokenURL, strings.NewReader(data.Encode()), headers)
if err == nil {
err = v.DoJSON(req, &token)
}
Expand All @@ -310,9 +313,10 @@ func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
"grant_type": {"refresh_token"},
"redirect_uri": {"https://www.getpostman.com/oauth2/callback"},
"refresh_token": {token.RefreshToken},
"client_id": {v.config.CCSPServiceID},
}

req, err := request.New(http.MethodPost, v.config.URI+TokenURL, strings.NewReader(data.Encode()), headers)
req, err := request.New(http.MethodPost, v.config.APIURI+TokenURL, strings.NewReader(data.Encode()), headers)

var res oauth.Token
if err == nil {
Expand Down
1 change: 1 addition & 0 deletions vehicle/bluelink/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package bluelink
const (
KiaAppID = "a2b8469b-30a3-4361-8e13-6fceea8fbe74"
HyundaiAppID = "014d2225-8495-4735-812d-2616334fd15d"
GenesisAppID = "f11f2b86-e0e7-4851-90df-5600b01d8b70"
)