From 9a775d12ea67f0f438e4ead93e4ec3e6623c39ce Mon Sep 17 00:00:00 2001 From: premultiply Date: Wed, 27 Apr 2022 20:17:15 +0200 Subject: [PATCH] Renault: add wakeup api (#2841) Co-authored-by: chrissooo --- vehicle/renault.go | 71 +++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/vehicle/renault.go b/vehicle/renault.go index e3066186d..f9ffc9375 100644 --- a/vehicle/renault.go +++ b/vehicle/renault.go @@ -3,6 +3,7 @@ package vehicle import ( "errors" "fmt" + "io" "net/http" "net/url" "strings" @@ -16,11 +17,11 @@ import ( ) // Credits to +// https://github.com/hacf-fr/renault-api // https://github.com/edent/Renault-Zoe-API/issues/18 // https://github.com/epenet/Renault-Zoe-API/blob/newapimockup/Test/MyRenault.py // https://github.com/jamesremuscat/pyze -// https://github.com/hacf-fr/renault-api -// https://muscatoxblog.blogspot.com/2019/07/delving-into-renaults-new-api.html +// https://muscatoxblog.blogspot.com/2019/07/delving-into-renaults-new-api.html const ( keyStore = "https://renault-wrd-prod-1-euw1-myrapp-one.s3-eu-west-1.amazonaws.com/configuration/android/config_%s.json" @@ -199,10 +200,15 @@ func (v *Renault) authFlow() error { return err } -func (v *Renault) request(uri string, data url.Values, headers ...map[string]string) (*http.Request, error) { - req, err := request.New(http.MethodGet, uri, nil, headers...) +func (v *Renault) request(uri string, params url.Values, body io.Reader, headers ...map[string]string) (*http.Request, error) { + method := http.MethodGet + if body != nil { + method = http.MethodPost + } + + req, err := request.New(method, uri, body, headers...) if err == nil { - req.URL.RawQuery = data.Encode() + req.URL.RawQuery = params.Encode() } return req, err @@ -217,7 +223,7 @@ func (v *Renault) sessionCookie(user, password string) (string, error) { "apiKey": []string{v.gigya.APIKey}, } - req, err := v.request(uri, data) + req, err := v.request(uri, data, nil) var res gigyaResponse if err == nil { @@ -238,7 +244,7 @@ func (v *Renault) personID(sessionCookie string) (string, error) { "login_token": []string{sessionCookie}, } - req, err := v.request(uri, data) + req, err := v.request(uri, data, nil) var res gigyaResponse if err == nil { @@ -258,7 +264,7 @@ func (v *Renault) jwtToken(sessionCookie string) (string, error) { "expiration": []string{"900"}, } - req, err := v.request(uri, data) + req, err := v.request(uri, data, nil) var res gigyaResponse if err == nil { @@ -268,15 +274,15 @@ func (v *Renault) jwtToken(sessionCookie string) (string, error) { return res.IDToken, err } -func (v *Renault) kamereonRequest(uri string) (kamereonResponse, error) { - data := url.Values{"country": []string{"DE"}} +func (v *Renault) kamereonRequest(uri string, body io.Reader) (kamereonResponse, error) { + params := url.Values{"country": []string{"DE"}} headers := map[string]string{ "x-gigya-id_token": v.gigyaJwtToken, "apikey": "VAX7XYKGfa92yMvXculCkEFyfZbuM7Ss", // v.kamereon.APIKey } var res kamereonResponse - req, err := v.request(uri, data, headers) + req, err := v.request(uri, params, body, headers) if err == nil { err = v.DoJSON(req, &res) } @@ -286,7 +292,7 @@ func (v *Renault) kamereonRequest(uri string) (kamereonResponse, error) { func (v *Renault) kamereonPerson(personID string) (string, error) { uri := fmt.Sprintf("%s/commerce/v1/persons/%s", v.kamereon.Target, personID) - res, err := v.kamereonRequest(uri) + res, err := v.kamereonRequest(uri, nil) if len(res.Accounts) == 0 { return "", err @@ -297,7 +303,7 @@ func (v *Renault) kamereonPerson(personID string) (string, error) { func (v *Renault) kamereonVehicles(accountID string) ([]string, error) { uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/vehicles", v.kamereon.Target, accountID) - res, err := v.kamereonRequest(uri) + res, err := v.kamereonRequest(uri, nil) var vehicles []string if err == nil { @@ -314,12 +320,12 @@ func (v *Renault) kamereonVehicles(accountID string) ([]string, error) { // batteryAPI provides battery-status api response func (v *Renault) batteryAPI() (kamereonResponse, error) { uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v2/cars/%s/battery-status", v.kamereon.Target, v.accountID, v.vin) - res, err := v.kamereonRequest(uri) + res, err := v.kamereonRequest(uri, nil) // repeat auth if error if err != nil { if err = v.authFlow(); err == nil { - res, err = v.kamereonRequest(uri) + res, err = v.kamereonRequest(uri, nil) } } @@ -329,12 +335,12 @@ func (v *Renault) batteryAPI() (kamereonResponse, error) { // hvacAPI provides hvac-status api response func (v *Renault) hvacAPI() (kamereonResponse, error) { uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v1/cars/%s/hvac-status", v.kamereon.Target, v.accountID, v.vin) - res, err := v.kamereonRequest(uri) + res, err := v.kamereonRequest(uri, nil) // repeat auth if error if err != nil { if err = v.authFlow(); err == nil { - res, err = v.kamereonRequest(uri) + res, err = v.kamereonRequest(uri, nil) } } @@ -344,12 +350,12 @@ func (v *Renault) hvacAPI() (kamereonResponse, error) { // cockpitAPI provides cockpit api response func (v *Renault) cockpitAPI() (kamereonResponse, error) { uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v2/cars/%s/cockpit", v.kamereon.Target, v.accountID, v.vin) - res, err := v.kamereonRequest(uri) + res, err := v.kamereonRequest(uri, nil) // repeat auth if error if err != nil { if err = v.authFlow(); err == nil { - res, err = v.kamereonRequest(uri) + res, err = v.kamereonRequest(uri, nil) } } @@ -456,3 +462,30 @@ func (v *Renault) Climater() (active bool, outsideTemp float64, targetTemp float return false, 0, 0, err } + +var _ api.AlarmClock = (*Renault)(nil) + +// WakeUp implements the api.AlarmClock interface +func (v *Renault) WakeUp() error { + uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kcm/v1/vehicles/%s/charge/pause-resume", v.kamereon.Target, v.accountID, v.vin) + + data := map[string]interface{}{ + "data": map[string]interface{}{ + "type": "ChargePauseResume", + "attributes": map[string]interface{}{ + "action": "resume", + }, + }, + } + + _, err := v.kamereonRequest(uri, request.MarshalJSON(data)) + + // repeat auth if error + if err != nil { + if err = v.authFlow(); err == nil { + _, err = v.kamereonRequest(uri, request.MarshalJSON(data)) + } + } + + return err +}