More changes
All checks were successful
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Successful in 27s
All checks were successful
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Successful in 27s
This commit is contained in:
811
client.go
811
client.go
@ -27,6 +27,370 @@ type Client struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New function creates a New MySubaru client
|
||||||
|
func New(config *config.Config) (*Client, error) {
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
credentials: config.MySubaru.Credentials,
|
||||||
|
country: config.MySubaru.Region,
|
||||||
|
updateInterval: 7200,
|
||||||
|
fetchInterval: 360,
|
||||||
|
logger: config.Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := resty.New()
|
||||||
|
httpClient.
|
||||||
|
SetBaseURL(MOBILE_API_SERVER[client.country]).
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"User-Agent": "Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.191030.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36",
|
||||||
|
"Origin": "file://",
|
||||||
|
"X-Requested-With": MOBILE_APP[client.country],
|
||||||
|
"Accept-Language": "en-US,en;q=0.9",
|
||||||
|
"Accept-Encoding": "gzip, deflate",
|
||||||
|
"Accept": "*/*"})
|
||||||
|
|
||||||
|
client.httpClient = httpClient
|
||||||
|
resp := client.auth()
|
||||||
|
|
||||||
|
if r, ok := client.parseResponse(resp); ok {
|
||||||
|
var sd SessionData
|
||||||
|
err := json.Unmarshal(r.Data, &sd)
|
||||||
|
if err != nil {
|
||||||
|
client.logger.Error("error while parsing json", "request", "auth", "error", err.Error())
|
||||||
|
}
|
||||||
|
// client.logger.Debug("unmarshaled json data", "request", "auth", "type", "sessionData", "body", sd)
|
||||||
|
|
||||||
|
if sd.DeviceRegistered && sd.RegisteredDevicePermanent {
|
||||||
|
client.logger.Debug("client authentication successful")
|
||||||
|
client.isAuthenticated = true
|
||||||
|
client.isRegistered = true
|
||||||
|
} else {
|
||||||
|
client.logger.Debug("client authentication successful, but devices is not registered")
|
||||||
|
client.registerDevice()
|
||||||
|
}
|
||||||
|
|
||||||
|
// client.logger.Debug("parsing cars assigned to the account", "quantity", len(sd.Vehicles))
|
||||||
|
if len(sd.Vehicles) > 0 {
|
||||||
|
for _, vehicle := range sd.Vehicles {
|
||||||
|
// client.logger.Debug("parsing car", "vin", vehicle.Vin)
|
||||||
|
client.listOfVins = append(client.listOfVins, vehicle.Vin)
|
||||||
|
}
|
||||||
|
client.currentVin = client.listOfVins[0]
|
||||||
|
} else {
|
||||||
|
client.logger.Error("there no cars assigned to the account")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Work on errors
|
||||||
|
// error, _ := respParsed.Path("errorCode").Data().(string)
|
||||||
|
// switch {
|
||||||
|
// case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
|
||||||
|
// client.logger.Debug("Invalid account")
|
||||||
|
// case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
|
||||||
|
// client.logger.Debug("Client authentication failed")
|
||||||
|
// case error == apiErrors["ERROR_PASSWORD_WARNING"]:
|
||||||
|
// client.logger.Debug("Multiple Password Failures.")
|
||||||
|
// default:
|
||||||
|
// client.logger.Debug("Uknown error")
|
||||||
|
// }
|
||||||
|
client.logger.Error("request was not successfull", "request", "auth")
|
||||||
|
// TODO: Work on providing error
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectVehicle .
|
||||||
|
func (c *Client) SelectVehicle(vin string) VehicleData {
|
||||||
|
// API > json > dataName > vehicle
|
||||||
|
if vin == "" {
|
||||||
|
vin = c.currentVin
|
||||||
|
}
|
||||||
|
|
||||||
|
vinCheck(vin)
|
||||||
|
|
||||||
|
params := map[string]string{
|
||||||
|
"vin": vin,
|
||||||
|
"_": timestamp()}
|
||||||
|
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
|
||||||
|
resp := c.execute(reqURL, GET, params, "", false)
|
||||||
|
// c.logger.Debug("http request output", "request", "SelectVehicle", "body", resp)
|
||||||
|
|
||||||
|
if r, ok := c.parseResponse(resp); ok {
|
||||||
|
var vd VehicleData
|
||||||
|
err := json.Unmarshal(r.Data, &vd)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
||||||
|
}
|
||||||
|
// c.logger.Debug("http request output", "request", "GetVehicleStatus", "body", resp)
|
||||||
|
|
||||||
|
return vd
|
||||||
|
} else {
|
||||||
|
return VehicleData{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resp := c.execute(reqURL, GET, params, "", false)
|
||||||
|
// logger.Debugf("SELECT VEHICLE OUTPUT >> %v\n", string([]byte(resp)))
|
||||||
|
|
||||||
|
// ERRORS
|
||||||
|
// {"success":false,"errorCode":"vehicleNotInAccount","dataName":null,"data":null}
|
||||||
|
|
||||||
|
// """Select active vehicle for accounts with multiple VINs."""
|
||||||
|
// params = {"vin": vin, "_": int(time.time())}
|
||||||
|
// js_resp = await self.get(API_SELECT_VEHICLE, params=params)
|
||||||
|
// _LOGGER.debug(pprint.pformat(js_resp))
|
||||||
|
// if js_resp.get("success"):
|
||||||
|
// self._current_vin = vin
|
||||||
|
// _LOGGER.debug("Current vehicle: vin=%s", js_resp["data"]["vin"])
|
||||||
|
// return js_resp["data"]
|
||||||
|
// if not js_resp.get("success") and js_resp.get("errorCode") == "VEHICLESETUPERROR":
|
||||||
|
// # Occasionally happens every few hours. Resetting the session seems to deal with it.
|
||||||
|
// _LOGGER.warning("VEHICLESETUPERROR received. Resetting session.")
|
||||||
|
// self.reset_session()
|
||||||
|
// return False
|
||||||
|
// _LOGGER.debug("Failed to switch vehicle errorCode=%s", js_resp.get("errorCode"))
|
||||||
|
// # Something else is probably wrong with the backend server context - try resetting
|
||||||
|
// self.reset_session()
|
||||||
|
// raise SubaruException("Failed to switch vehicle %s - resetting session." % js_resp.get("errorCode"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVehicles .
|
||||||
|
func (c *Client) GetVehicles() []*Vehicle {
|
||||||
|
var vehicles []*Vehicle
|
||||||
|
for _, vin := range c.listOfVins {
|
||||||
|
vehicle := c.GetVehicleByVIN(vin)
|
||||||
|
vehicles = append(vehicles, vehicle)
|
||||||
|
}
|
||||||
|
return vehicles
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVehicleByVIN .
|
||||||
|
func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
|
||||||
|
var vehicle *Vehicle
|
||||||
|
if contains(c.listOfVins, vin) {
|
||||||
|
params := map[string]string{
|
||||||
|
"vin": vin,
|
||||||
|
"_": timestamp()}
|
||||||
|
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
|
||||||
|
resp := c.execute(reqURL, GET, params, "", false)
|
||||||
|
// c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
|
||||||
|
|
||||||
|
if r, ok := c.parseResponse(resp); ok {
|
||||||
|
var vd VehicleData
|
||||||
|
err := json.Unmarshal(r.Data, &vd)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error())
|
||||||
|
}
|
||||||
|
// c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
|
||||||
|
|
||||||
|
vehicle = &Vehicle{
|
||||||
|
Vin: vin,
|
||||||
|
CarName: vd.VehicleName,
|
||||||
|
CarNickname: vd.Nickname,
|
||||||
|
ModelName: vd.ModelName,
|
||||||
|
ModelYear: vd.ModelYear,
|
||||||
|
ModelCode: vd.ModelCode,
|
||||||
|
ExtDescrip: vd.ExtDescrip,
|
||||||
|
IntDescrip: vd.IntDescrip,
|
||||||
|
TransCode: vd.TransCode,
|
||||||
|
EngineSize: vd.EngineSize,
|
||||||
|
VehicleKey: vd.VehicleKey,
|
||||||
|
LicensePlate: vd.LicensePlate,
|
||||||
|
LicensePlateState: vd.LicensePlateState,
|
||||||
|
Features: vd.Features,
|
||||||
|
SubscriptionFeatures: vd.SubscriptionFeatures,
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
vehicle.Doors = make(map[string]Door)
|
||||||
|
vehicle.Windows = make(map[string]Window)
|
||||||
|
vehicle.Tires = make(map[string]Tire)
|
||||||
|
vehicle.ClimateProfiles = make(map[string]ClimateProfile)
|
||||||
|
|
||||||
|
vehicle.GetVehicleStatus()
|
||||||
|
vehicle.GetVehicleCondition()
|
||||||
|
vehicle.GetVehicleHealth()
|
||||||
|
vehicle.GetClimatePresets()
|
||||||
|
vehicle.GetClimateUserPresets()
|
||||||
|
vehicle.GetClimateQuickPresets()
|
||||||
|
|
||||||
|
return vehicle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN")
|
||||||
|
return &Vehicle{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func isPINRequired() {}
|
||||||
|
// func getVehicles() {}
|
||||||
|
// func getEVStatus() {}
|
||||||
|
// func getRemoteOptionsStatus() {}
|
||||||
|
// func getRemoteStartStatus() {}
|
||||||
|
// func getSafetyStatus() {}
|
||||||
|
// func getSubscriptionStatus() {}
|
||||||
|
// func getAPIGen() {}
|
||||||
|
// func getClimateData() {}
|
||||||
|
// func saveClimateSettings() {}
|
||||||
|
// func getVehicleName() {}
|
||||||
|
// func fetch() {}
|
||||||
|
|
||||||
|
// Exec method executes a Client instance with the API URL
|
||||||
|
func (c *Client) execute(requestUrl string, method string, params map[string]string, pollingUrl string, j bool) []byte {
|
||||||
|
defer timeTrack("[TIMETRK] Executing Get Request")
|
||||||
|
|
||||||
|
var resp *resty.Response
|
||||||
|
|
||||||
|
// GET Requests
|
||||||
|
if method == "GET" {
|
||||||
|
resp, _ = c.httpClient.
|
||||||
|
R().
|
||||||
|
SetQueryParams(params).
|
||||||
|
Get(requestUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST Requests
|
||||||
|
if method == "POST" {
|
||||||
|
if j {
|
||||||
|
// POST > JSON Body
|
||||||
|
resp, _ = c.httpClient.
|
||||||
|
R().
|
||||||
|
SetBody(params).
|
||||||
|
Post(requestUrl)
|
||||||
|
} else {
|
||||||
|
// POST > Form Data
|
||||||
|
resp, _ = c.httpClient.
|
||||||
|
R().
|
||||||
|
SetFormData(params).
|
||||||
|
Post(requestUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while getting body", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var r Response
|
||||||
|
err = json.Unmarshal(resBytes, &r)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
|
||||||
|
}
|
||||||
|
c.logger.Debug("parsed http request output", "request", "HTTP POLLING", "data", r.Data)
|
||||||
|
|
||||||
|
if r.Success {
|
||||||
|
var sr ServiceRequest
|
||||||
|
err := json.Unmarshal(r.Data, &sr)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if pollingUrl != "" {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
attempts := 20
|
||||||
|
|
||||||
|
// for {
|
||||||
|
// if attempt >= attempts {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// resp := c.execute(pollingUrl, GET, map[string]string{"serviceRequestId": sr.ServiceRequestID}, pollingUrl, false)
|
||||||
|
// var r Response
|
||||||
|
// err = json.Unmarshal(resp, &r)
|
||||||
|
// if err != nil {
|
||||||
|
// c.logger.Error("error while parsing json", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
|
||||||
|
// }
|
||||||
|
// c.logger.Debug("parsed http request output", "request", "HTTP POLLING", "data", r.Data)
|
||||||
|
|
||||||
|
// var sr ServiceRequest
|
||||||
|
// err := json.Unmarshal(r.Data, &sr)
|
||||||
|
// if err != nil {
|
||||||
|
// c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// switch {
|
||||||
|
// case sr.RemoteServiceState == "finished":
|
||||||
|
// c.logger.Debug("Remote service request completed successfully", "request id", sr.ServiceRequestID)
|
||||||
|
// break
|
||||||
|
// case sr.RemoteServiceState == "started":
|
||||||
|
// c.logger.Debug("Subaru API reports remote service request is in progress", "request id", sr.ServiceRequestID)
|
||||||
|
// }
|
||||||
|
// attempt++
|
||||||
|
// }
|
||||||
|
|
||||||
|
poolingLoop:
|
||||||
|
for attempts > 0 {
|
||||||
|
resp, _ = c.httpClient.
|
||||||
|
SetBaseURL(MOBILE_API_SERVER[c.country]).
|
||||||
|
R().
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"serviceRequestId": sr.ServiceRequestID,
|
||||||
|
}).
|
||||||
|
Get(pollingUrl)
|
||||||
|
resBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
c.logger.Debug("POLLING HTTP OUTPUT", "body", string(resBytes))
|
||||||
|
// {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}}
|
||||||
|
|
||||||
|
var r Response
|
||||||
|
err := json.Unmarshal(resBytes, &r)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
||||||
|
}
|
||||||
|
c.logger.Debug("parsed loop http request output", "request", "HTTP POLLING", "data", r.Data)
|
||||||
|
|
||||||
|
if r.Success {
|
||||||
|
var sr ServiceRequest
|
||||||
|
err := json.Unmarshal(r.Data, &sr)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case sr.RemoteServiceState == "finished":
|
||||||
|
c.logger.Debug("Remote service request completed successfully", "request id", sr.ServiceRequestID)
|
||||||
|
break poolingLoop
|
||||||
|
case sr.RemoteServiceState == "started":
|
||||||
|
c.logger.Debug("Subaru API reports remote service request is in progress", "request id", sr.ServiceRequestID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.logger.Debug("Backend session expired, please try again")
|
||||||
|
break poolingLoop
|
||||||
|
}
|
||||||
|
attempts--
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.logger.Error("request is not successfull", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// // isResponseSuccessfull .
|
||||||
|
// func (c *Client) isResponseSuccessfull(resp []byte) bool {
|
||||||
|
// respParsed, err := gabs.ParseJSON(resp)
|
||||||
|
// if err != nil {
|
||||||
|
// c.logger.Debug("error while parsing json response", "error", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// success, ok := respParsed.Path("success").Data().(bool)
|
||||||
|
// if !ok {
|
||||||
|
// c.logger.Debug("response is not successful", "error", resp)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ERRORS FROM CLIENT CREATION AFTER AUTH
|
||||||
|
// // error, _ := respParsed.Path("errorCode").Data().(string)
|
||||||
|
// // switch {
|
||||||
|
// // case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
|
||||||
|
// // fmt.Println("Invalid account")
|
||||||
|
// // case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
|
||||||
|
// // {"success":false,"errorCode":"InvalidCredentials","dataName":"remoteServiceStatus","data":{"serviceRequestId":null,"success":false,"cancelled":false,"remoteServiceType":null,"remoteServiceState":null,"subState":null,"errorCode":null,"result":null,"updateTime":null,"vin":null,"errorDescription":"The credentials supplied are invalid, tries left 2"}}
|
||||||
|
// // fmt.Println("Client authentication failed")
|
||||||
|
// // case error == apiErrors["ERROR_PASSWORD_WARNING"]:
|
||||||
|
// // fmt.Println("Multiple Password Failures.")
|
||||||
|
// // default:
|
||||||
|
// // fmt.Println("Uknown error")
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// return success
|
||||||
|
// }
|
||||||
|
|
||||||
// auth .
|
// auth .
|
||||||
func (c *Client) auth() []byte {
|
func (c *Client) auth() []byte {
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
@ -44,6 +408,59 @@ func (c *Client) auth() []byte {
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseResponse .
|
||||||
|
func (c *Client) parseResponse(b []byte) (Response, bool) {
|
||||||
|
var r Response
|
||||||
|
err := json.Unmarshal(b, &r)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateSession .
|
||||||
|
func (c *Client) validateSession() bool {
|
||||||
|
// {
|
||||||
|
// "success": true,
|
||||||
|
// "errorCode": null,
|
||||||
|
// "dataName": null,
|
||||||
|
// "data": null
|
||||||
|
// }
|
||||||
|
reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"]
|
||||||
|
resp := c.execute(reqURL, GET, map[string]string{}, "", false)
|
||||||
|
c.logger.Debug("http request output", "request", "validateSession", "body", resp)
|
||||||
|
|
||||||
|
var r Response
|
||||||
|
err := json.Unmarshal(resp, &r)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("error while parsing json", "request", "validateSession", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Success {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
|
||||||
|
// result = False
|
||||||
|
// js_resp = await self.__open(API_VALIDATE_SESSION, GET)
|
||||||
|
// _LOGGER.debug(pprint.pformat(js_resp))
|
||||||
|
// if js_resp["success"]:
|
||||||
|
// if vin != self._current_vin:
|
||||||
|
// # API call for VIN that is not the current remote context.
|
||||||
|
// _LOGGER.debug("Switching Subaru API vehicle context to: %s", vin)
|
||||||
|
// if await self._select_vehicle(vin):
|
||||||
|
// result = True
|
||||||
|
// else:
|
||||||
|
// result = True
|
||||||
|
|
||||||
|
// if result is False:
|
||||||
|
// await self._authenticate(vin)
|
||||||
|
// # New session cookie. Must call selectVehicle.json before any other API call.
|
||||||
|
// if await self._select_vehicle(vin):
|
||||||
|
// result = True
|
||||||
|
}
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
// https://www.mysubaru.com/profile/verifyDeviceName.json?clientId=2574212&deviceName=Alex%20Google%20Pixel%204%20XL
|
// https://www.mysubaru.com/profile/verifyDeviceName.json?clientId=2574212&deviceName=Alex%20Google%20Pixel%204%20XL
|
||||||
// RESP: true/false
|
// RESP: true/false
|
||||||
@ -158,397 +575,3 @@ func (c *Client) listDevices() {
|
|||||||
// logger.Debugf("LIST DEVICES OUTPUT >> %v\n", string(resp))
|
// logger.Debugf("LIST DEVICES OUTPUT >> %v\n", string(resp))
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateSession .
|
|
||||||
func (c *Client) validateSession() bool {
|
|
||||||
// {
|
|
||||||
// "success": true,
|
|
||||||
// "errorCode": null,
|
|
||||||
// "dataName": null,
|
|
||||||
// "data": null
|
|
||||||
// }
|
|
||||||
reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"]
|
|
||||||
resp := c.execute(reqURL, GET, map[string]string{}, "", false)
|
|
||||||
c.logger.Debug("http request output", "request", "validateSession", "body", resp)
|
|
||||||
|
|
||||||
var r Response
|
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "validateSession", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
|
|
||||||
// result = False
|
|
||||||
// js_resp = await self.__open(API_VALIDATE_SESSION, GET)
|
|
||||||
// _LOGGER.debug(pprint.pformat(js_resp))
|
|
||||||
// if js_resp["success"]:
|
|
||||||
// if vin != self._current_vin:
|
|
||||||
// # API call for VIN that is not the current remote context.
|
|
||||||
// _LOGGER.debug("Switching Subaru API vehicle context to: %s", vin)
|
|
||||||
// if await self._select_vehicle(vin):
|
|
||||||
// result = True
|
|
||||||
// else:
|
|
||||||
// result = True
|
|
||||||
|
|
||||||
// if result is False:
|
|
||||||
// await self._authenticate(vin)
|
|
||||||
// # New session cookie. Must call selectVehicle.json before any other API call.
|
|
||||||
// if await self._select_vehicle(vin):
|
|
||||||
// result = True
|
|
||||||
}
|
|
||||||
|
|
||||||
// New function creates a New MySubaru client
|
|
||||||
func New(config *config.Config) (*Client, error) {
|
|
||||||
|
|
||||||
client := &Client{
|
|
||||||
credentials: config.MySubaru.Credentials,
|
|
||||||
country: config.MySubaru.Region,
|
|
||||||
updateInterval: 7200,
|
|
||||||
fetchInterval: 360,
|
|
||||||
logger: config.Logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient := resty.New()
|
|
||||||
httpClient.
|
|
||||||
SetBaseURL(MOBILE_API_SERVER[client.country]).
|
|
||||||
SetHeaders(map[string]string{
|
|
||||||
"User-Agent": "Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.191030.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36",
|
|
||||||
"Origin": "file://",
|
|
||||||
"X-Requested-With": MOBILE_APP[client.country],
|
|
||||||
"Accept-Language": "en-US,en;q=0.9",
|
|
||||||
"Accept-Encoding": "gzip, deflate",
|
|
||||||
"Accept": "*/*"})
|
|
||||||
|
|
||||||
client.httpClient = httpClient
|
|
||||||
|
|
||||||
resp := client.auth()
|
|
||||||
var r Response
|
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
client.logger.Error("error while parsing json", "request", "auth", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var sd SessionData
|
|
||||||
err := json.Unmarshal(r.Data, &sd)
|
|
||||||
if err != nil {
|
|
||||||
client.logger.Error("error while parsing json", "request", "auth", "error", err.Error())
|
|
||||||
}
|
|
||||||
// client.logger.Debug("unmarshaled json data", "request", "auth", "type", "sessionData", "body", sd)
|
|
||||||
|
|
||||||
if sd.DeviceRegistered && sd.RegisteredDevicePermanent {
|
|
||||||
client.logger.Debug("client authentication successful")
|
|
||||||
client.isAuthenticated = true
|
|
||||||
client.isRegistered = true
|
|
||||||
} else {
|
|
||||||
client.logger.Debug("client authentication successful, but devices is not registered")
|
|
||||||
client.registerDevice()
|
|
||||||
}
|
|
||||||
|
|
||||||
// client.logger.Debug("parsing cars assigned to the account", "quantity", len(sd.Vehicles))
|
|
||||||
if len(sd.Vehicles) > 0 {
|
|
||||||
for _, vehicle := range sd.Vehicles {
|
|
||||||
// client.logger.Debug("parsing car", "vin", vehicle.Vin)
|
|
||||||
client.listOfVins = append(client.listOfVins, vehicle.Vin)
|
|
||||||
}
|
|
||||||
client.currentVin = client.listOfVins[0]
|
|
||||||
} else {
|
|
||||||
client.logger.Error("there no cars assigned to the account")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: Work on errors
|
|
||||||
// error, _ := respParsed.Path("errorCode").Data().(string)
|
|
||||||
// switch {
|
|
||||||
// case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
|
|
||||||
// client.logger.Debug("Invalid account")
|
|
||||||
// case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
|
|
||||||
// client.logger.Debug("Client authentication failed")
|
|
||||||
// case error == apiErrors["ERROR_PASSWORD_WARNING"]:
|
|
||||||
// client.logger.Debug("Multiple Password Failures.")
|
|
||||||
// default:
|
|
||||||
// client.logger.Debug("Uknown error")
|
|
||||||
// }
|
|
||||||
client.logger.Error("request was not successfull", "request", "auth", "error", err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectVehicle .
|
|
||||||
func (c *Client) SelectVehicle(vin string) VehicleData {
|
|
||||||
// API > json > dataName > vehicle
|
|
||||||
if vin == "" {
|
|
||||||
vin = c.currentVin
|
|
||||||
}
|
|
||||||
|
|
||||||
vinCheck(vin)
|
|
||||||
|
|
||||||
params := map[string]string{
|
|
||||||
"vin": vin,
|
|
||||||
"_": timestamp()}
|
|
||||||
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
|
|
||||||
resp := c.execute(reqURL, GET, params, "", false)
|
|
||||||
// c.logger.Debug("http request output", "request", "SelectVehicle", "body", resp)
|
|
||||||
|
|
||||||
var r Response
|
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "SelectVehicle", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var vd VehicleData
|
|
||||||
err = json.Unmarshal(r.Data, &vd)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
|
||||||
}
|
|
||||||
// c.logger.Debug("http request output", "request", "GetVehicleStatus", "body", resp)
|
|
||||||
|
|
||||||
return vd
|
|
||||||
} else {
|
|
||||||
return VehicleData{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// resp := c.execute(reqURL, GET, params, "", false)
|
|
||||||
// logger.Debugf("SELECT VEHICLE OUTPUT >> %v\n", string([]byte(resp)))
|
|
||||||
|
|
||||||
// ERRORS
|
|
||||||
// {"success":false,"errorCode":"vehicleNotInAccount","dataName":null,"data":null}
|
|
||||||
|
|
||||||
// """Select active vehicle for accounts with multiple VINs."""
|
|
||||||
// params = {"vin": vin, "_": int(time.time())}
|
|
||||||
// js_resp = await self.get(API_SELECT_VEHICLE, params=params)
|
|
||||||
// _LOGGER.debug(pprint.pformat(js_resp))
|
|
||||||
// if js_resp.get("success"):
|
|
||||||
// self._current_vin = vin
|
|
||||||
// _LOGGER.debug("Current vehicle: vin=%s", js_resp["data"]["vin"])
|
|
||||||
// return js_resp["data"]
|
|
||||||
// if not js_resp.get("success") and js_resp.get("errorCode") == "VEHICLESETUPERROR":
|
|
||||||
// # Occasionally happens every few hours. Resetting the session seems to deal with it.
|
|
||||||
// _LOGGER.warning("VEHICLESETUPERROR received. Resetting session.")
|
|
||||||
// self.reset_session()
|
|
||||||
// return False
|
|
||||||
// _LOGGER.debug("Failed to switch vehicle errorCode=%s", js_resp.get("errorCode"))
|
|
||||||
// # Something else is probably wrong with the backend server context - try resetting
|
|
||||||
// self.reset_session()
|
|
||||||
// raise SubaruException("Failed to switch vehicle %s - resetting session." % js_resp.get("errorCode"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVehicles .
|
|
||||||
func (c *Client) GetVehicles() []*Vehicle {
|
|
||||||
var vehicles []*Vehicle
|
|
||||||
for _, vin := range c.listOfVins {
|
|
||||||
vehicle := c.GetVehicleByVIN(vin)
|
|
||||||
vehicles = append(vehicles, vehicle)
|
|
||||||
}
|
|
||||||
return vehicles
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVehicleByVIN .
|
|
||||||
func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
|
|
||||||
var vehicle *Vehicle
|
|
||||||
if contains(c.listOfVins, vin) {
|
|
||||||
params := map[string]string{
|
|
||||||
"vin": vin,
|
|
||||||
"_": timestamp()}
|
|
||||||
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
|
|
||||||
resp := c.execute(reqURL, GET, params, "", false)
|
|
||||||
// c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
|
|
||||||
|
|
||||||
var r Response
|
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var vd VehicleData
|
|
||||||
err = json.Unmarshal(r.Data, &vd)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error())
|
|
||||||
}
|
|
||||||
// c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
|
|
||||||
|
|
||||||
vehicle = &Vehicle{
|
|
||||||
Vin: vin,
|
|
||||||
CarName: vd.VehicleName,
|
|
||||||
CarNickname: vd.Nickname,
|
|
||||||
ModelName: vd.ModelName,
|
|
||||||
ModelYear: vd.ModelYear,
|
|
||||||
ModelCode: vd.ModelCode,
|
|
||||||
ExtDescrip: vd.ExtDescrip,
|
|
||||||
IntDescrip: vd.IntDescrip,
|
|
||||||
TransCode: vd.TransCode,
|
|
||||||
EngineSize: vd.EngineSize,
|
|
||||||
VehicleKey: vd.VehicleKey,
|
|
||||||
LicensePlate: vd.LicensePlate,
|
|
||||||
LicensePlateState: vd.LicensePlateState,
|
|
||||||
Features: vd.Features,
|
|
||||||
SubscriptionFeatures: vd.SubscriptionFeatures,
|
|
||||||
client: c,
|
|
||||||
}
|
|
||||||
vehicle.Doors = make(map[string]Door)
|
|
||||||
vehicle.Windows = make(map[string]Window)
|
|
||||||
vehicle.Tires = make(map[string]Tire)
|
|
||||||
vehicle.ClimateProfiles = make(map[string]ClimateProfile)
|
|
||||||
|
|
||||||
vehicle.GetVehicleStatus()
|
|
||||||
vehicle.GetVehicleCondition()
|
|
||||||
vehicle.GetVehicleHealth()
|
|
||||||
vehicle.GetClimatePresets()
|
|
||||||
vehicle.GetClimateUserPresets()
|
|
||||||
vehicle.GetClimateQuickPresets()
|
|
||||||
|
|
||||||
return vehicle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN")
|
|
||||||
return &Vehicle{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func isPINRequired() {}
|
|
||||||
// func getVehicles() {}
|
|
||||||
// func getEVStatus() {}
|
|
||||||
// func getRemoteOptionsStatus() {}
|
|
||||||
// func getRemoteStartStatus() {}
|
|
||||||
// func getSafetyStatus() {}
|
|
||||||
// func getSubscriptionStatus() {}
|
|
||||||
// func getAPIGen() {}
|
|
||||||
// func getClimateData() {}
|
|
||||||
// func saveClimateSettings() {}
|
|
||||||
// func getVehicleName() {}
|
|
||||||
// func fetch() {}
|
|
||||||
|
|
||||||
// Exec method executes a Client instance with the API URL
|
|
||||||
func (c *Client) execute(requestUrl string, method string, params map[string]string, pollingUrl string, j bool) []byte {
|
|
||||||
defer timeTrack("[TIMETRK] Executing Get Request")
|
|
||||||
|
|
||||||
var resp *resty.Response
|
|
||||||
|
|
||||||
// GET Requests
|
|
||||||
if method == "GET" {
|
|
||||||
resp, _ = c.httpClient.
|
|
||||||
R().
|
|
||||||
SetQueryParams(params).
|
|
||||||
Get(requestUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST Requests
|
|
||||||
if method == "POST" {
|
|
||||||
if j {
|
|
||||||
// POST > JSON Body
|
|
||||||
resp, _ = c.httpClient.
|
|
||||||
R().
|
|
||||||
SetBody(params).
|
|
||||||
Post(requestUrl)
|
|
||||||
} else {
|
|
||||||
// POST > Form Data
|
|
||||||
resp, _ = c.httpClient.
|
|
||||||
R().
|
|
||||||
SetFormData(params).
|
|
||||||
Post(requestUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resBytes, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while getting body", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var r Response
|
|
||||||
err = json.Unmarshal(resBytes, &r)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
|
|
||||||
}
|
|
||||||
c.logger.Debug("parsed http request output", "request", "HTTP POLLING", "data", r.Data)
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var sr ServiceRequest
|
|
||||||
err := json.Unmarshal(r.Data, &sr)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if pollingUrl != "" {
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
attempts := 20
|
|
||||||
poolingLoop:
|
|
||||||
for attempts > 0 {
|
|
||||||
resp, _ = c.httpClient.
|
|
||||||
SetBaseURL(MOBILE_API_SERVER[c.country]).
|
|
||||||
R().
|
|
||||||
SetQueryParams(map[string]string{
|
|
||||||
"serviceRequestId": sr.ServiceRequestID,
|
|
||||||
}).
|
|
||||||
Get(pollingUrl)
|
|
||||||
resBytes, _ := io.ReadAll(resp.Body)
|
|
||||||
c.logger.Debug("POLLING HTTP OUTPUT", "body", string(resBytes))
|
|
||||||
// {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}}
|
|
||||||
|
|
||||||
var r Response
|
|
||||||
err := json.Unmarshal(resBytes, &r)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
|
||||||
}
|
|
||||||
c.logger.Debug("parsed loop http request output", "request", "HTTP POLLING", "data", r.Data)
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var sr ServiceRequest
|
|
||||||
err := json.Unmarshal(r.Data, &sr)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case sr.RemoteServiceState == "finished":
|
|
||||||
c.logger.Debug("Remote service request completed successfully", "request id", sr.ServiceRequestID)
|
|
||||||
break poolingLoop
|
|
||||||
case sr.RemoteServiceState == "started":
|
|
||||||
c.logger.Debug("Subaru API reports remote service request is in progress", "request id", sr.ServiceRequestID)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.logger.Debug("Backend session expired, please try again")
|
|
||||||
break poolingLoop
|
|
||||||
}
|
|
||||||
attempts--
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.logger.Error("request is not successfull", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return resBytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// // isResponseSuccessfull .
|
|
||||||
// func (c *Client) isResponseSuccessfull(resp []byte) bool {
|
|
||||||
// respParsed, err := gabs.ParseJSON(resp)
|
|
||||||
// if err != nil {
|
|
||||||
// c.logger.Debug("error while parsing json response", "error", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// success, ok := respParsed.Path("success").Data().(bool)
|
|
||||||
// if !ok {
|
|
||||||
// c.logger.Debug("response is not successful", "error", resp)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // ERRORS FROM CLIENT CREATION AFTER AUTH
|
|
||||||
// // error, _ := respParsed.Path("errorCode").Data().(string)
|
|
||||||
// // switch {
|
|
||||||
// // case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
|
|
||||||
// // fmt.Println("Invalid account")
|
|
||||||
// // case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
|
|
||||||
// // {"success":false,"errorCode":"InvalidCredentials","dataName":"remoteServiceStatus","data":{"serviceRequestId":null,"success":false,"cancelled":false,"remoteServiceType":null,"remoteServiceState":null,"subState":null,"errorCode":null,"result":null,"updateTime":null,"vin":null,"errorDescription":"The credentials supplied are invalid, tries left 2"}}
|
|
||||||
// // fmt.Println("Client authentication failed")
|
|
||||||
// // case error == apiErrors["ERROR_PASSWORD_WARNING"]:
|
|
||||||
// // fmt.Println("Multiple Password Failures.")
|
|
||||||
// // default:
|
|
||||||
// // fmt.Println("Uknown error")
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// return success
|
|
||||||
// }
|
|
||||||
|
@ -43,7 +43,7 @@ func main() {
|
|||||||
|
|
||||||
// subaru.EngineStart()
|
// subaru.EngineStart()
|
||||||
fmt.Printf("SUBARU #1 (Vehicle Status):\n")
|
fmt.Printf("SUBARU #1 (Vehicle Status):\n")
|
||||||
subaru.GetVehicleStatus()
|
// subaru.GetVehicleStatus()
|
||||||
// fmt.Printf("SUBARU #1 (Vehicle Condition):\n")
|
// fmt.Printf("SUBARU #1 (Vehicle Condition):\n")
|
||||||
// subaru.GetVehicleCondition()
|
// subaru.GetVehicleCondition()
|
||||||
// fmt.Printf("SUBARU #1: %+v\n", subaru)
|
// fmt.Printf("SUBARU #1: %+v\n", subaru)
|
||||||
|
117
vehicle.go
117
vehicle.go
@ -88,6 +88,10 @@ type Vehicle struct {
|
|||||||
// ClimateProfile .
|
// ClimateProfile .
|
||||||
type ClimateProfile struct {
|
type ClimateProfile struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
VehicleType string `json:"vehicleType,omitempty"` // vehicleType [ gas | phev ]
|
||||||
|
PresetType string `json:"presetType,omitempty"` // presetType [ subaruPreset | userPreset ]
|
||||||
|
CanEdit bool `json:"canEdit,string,omitempty"` // canEdit [ false | true ]
|
||||||
|
Disabled bool `json:"disabled,string,omitempty"` // disabled [ false | true ]
|
||||||
RunTimeMinutes int `json:"runTimeMinutes,string"` // runTimeMinutes [ 5 | 10 ]
|
RunTimeMinutes int `json:"runTimeMinutes,string"` // runTimeMinutes [ 5 | 10 ]
|
||||||
ClimateZoneFrontTemp int `json:"climateZoneFrontTemp,string"` // climateZoneFrontTemp: [ for _ in range(60, 85 + 1)] // climateZoneFrontTempCelsius: [for _ in range(15, 30 + 1) ]
|
ClimateZoneFrontTemp int `json:"climateZoneFrontTemp,string"` // climateZoneFrontTemp: [ for _ in range(60, 85 + 1)] // climateZoneFrontTempCelsius: [for _ in range(15, 30 + 1) ]
|
||||||
ClimateZoneFrontAirMode string `json:"climateZoneFrontAirMode"` // climateZoneFrontAirMode: [ WINDOW | FEET_WINDOW | FACE | FEET | FEET_FACE_BALANCED | AUTO ]
|
ClimateZoneFrontAirMode string `json:"climateZoneFrontAirMode"` // climateZoneFrontAirMode: [ WINDOW | FEET_WINDOW | FACE | FEET | FEET_FACE_BALANCED | AUTO ]
|
||||||
@ -98,20 +102,14 @@ type ClimateProfile struct {
|
|||||||
HeatedSeatFrontLeft string `json:"heatedSeatFrontLeft"` // heatedSeatFrontLeft: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ]
|
HeatedSeatFrontLeft string `json:"heatedSeatFrontLeft"` // heatedSeatFrontLeft: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ]
|
||||||
HeatedSeatFrontRight string `json:"heatedSeatFrontRight"` // heatedSeatFrontRight: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ]
|
HeatedSeatFrontRight string `json:"heatedSeatFrontRight"` // heatedSeatFrontRight: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ]
|
||||||
StartConfiguration string `json:"startConfiguration"` // startConfiguration [ START_ENGINE_ALLOW_KEY_IN_IGNITION (gas) | START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION (phev) ]
|
StartConfiguration string `json:"startConfiguration"` // startConfiguration [ START_ENGINE_ALLOW_KEY_IN_IGNITION (gas) | START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION (phev) ]
|
||||||
CanEdit bool `json:"canEdit,string,omitempty"` // canEdit [ false | true ]
|
|
||||||
Disabled bool `json:"disabled,string,omitempty"` // disabled [ false | true ]
|
|
||||||
VehicleType string `json:"vehicleType,omitempty"` // vehicleType [ gas | phev ]
|
|
||||||
PresetType string `json:"presetType,omitempty"` // presetType [ subaruPreset | userPreset ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (cp *ClimateProfile) New() {}
|
|
||||||
|
|
||||||
// GeoLocation .
|
// GeoLocation .
|
||||||
type GeoLocation struct {
|
type GeoLocation struct {
|
||||||
Latitude float64 // 40.700184
|
Latitude float64 // 40.700184
|
||||||
Longitude float64 // -74.401375
|
Longitude float64 // -74.401375
|
||||||
Speed float64 // 0.00
|
|
||||||
Heading int // 189
|
Heading int // 189
|
||||||
|
Speed float64 // 0.00
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +266,7 @@ func (v *Vehicle) Unlock() {
|
|||||||
func (v *Vehicle) EngineStart() {
|
func (v *Vehicle) EngineStart() {
|
||||||
if v.getRemoteOptionsStatus() {
|
if v.getRemoteOptionsStatus() {
|
||||||
v.selectVehicle()
|
v.selectVehicle()
|
||||||
|
// TODO: Get Quick Climate Preset from the Currect Car
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"delay": "0",
|
"delay": "0",
|
||||||
"vin": v.Vin,
|
"vin": v.Vin,
|
||||||
@ -579,15 +578,9 @@ func (v *Vehicle) GetVehicleStatus() {
|
|||||||
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
|
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
|
||||||
// v.client.logger.Debug("http request output", "request", "GetVehicleStatus", "body", resp)
|
// v.client.logger.Debug("http request output", "request", "GetVehicleStatus", "body", resp)
|
||||||
|
|
||||||
var r Response
|
if r, ok := v.client.parseResponse(resp); ok {
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
v.client.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var vs VehicleStatus
|
var vs VehicleStatus
|
||||||
err = json.Unmarshal(r.Data, &vs)
|
err := json.Unmarshal(r.Data, &vs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.client.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
v.client.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
||||||
}
|
}
|
||||||
@ -614,6 +607,15 @@ func (v *Vehicle) GetVehicleStatus() {
|
|||||||
typeOfS := val.Type()
|
typeOfS := val.Type()
|
||||||
|
|
||||||
for i := 0; i < val.NumField(); i++ {
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
if val.Field(i).Interface() == "NOT_EQUIPPED" ||
|
||||||
|
val.Field(i).Interface() == "UNKNOWN" ||
|
||||||
|
val.Field(i).Interface() == "None" ||
|
||||||
|
val.Field(i).Interface() == "16383" ||
|
||||||
|
val.Field(i).Interface() == "65535" ||
|
||||||
|
val.Field(i).Interface() == "-64" ||
|
||||||
|
val.Field(i).Interface() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
fmt.Printf("Field: %s, Value: %v, Type: %v\n", typeOfS.Field(i).Name, val.Field(i).Interface(), val.Field(i).Type())
|
fmt.Printf("Field: %s, Value: %v, Type: %v\n", typeOfS.Field(i).Name, val.Field(i).Interface(), val.Field(i).Type())
|
||||||
}
|
}
|
||||||
// v := reflect.ValueOf(vSta)
|
// v := reflect.ValueOf(vSta)
|
||||||
@ -733,52 +735,6 @@ func (v *Vehicle) GetVehicleStatus() {
|
|||||||
// fmt.Printf("PARTS: %+v\n", parts)
|
// fmt.Printf("PARTS: %+v\n", parts)
|
||||||
v.Updated = time.Now()
|
v.Updated = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// vData := VehicleData{}
|
|
||||||
// vdString := respParsed.Path("data").String()
|
|
||||||
// json.Unmarshal([]byte(vdString), &vData)
|
|
||||||
|
|
||||||
// { "data": {
|
|
||||||
// "vhsId": 1038682433,
|
|
||||||
|
|
||||||
// "odometerValue": 24999,
|
|
||||||
// "odometerValueKilometers": 40223,
|
|
||||||
|
|
||||||
// "eventDate": 1642538410000,
|
|
||||||
// "eventDateStr": "2022-01-18T20:40+0000",
|
|
||||||
|
|
||||||
// "latitude": 40.70018,
|
|
||||||
// "longitude": -74.40139,
|
|
||||||
// "positionHeadingDegree": "155",
|
|
||||||
|
|
||||||
// "tirePressureFrontLeft": "2275",
|
|
||||||
// "tirePressureFrontRight": "2206",
|
|
||||||
// "tirePressureRearLeft": "2344",
|
|
||||||
// "tirePressureRearRight": "2275",
|
|
||||||
|
|
||||||
// "tirePressureFrontLeftPsi": "33",
|
|
||||||
// "tirePressureFrontRightPsi": "32",
|
|
||||||
// "tirePressureRearLeftPsi": "34",
|
|
||||||
// "tirePressureRearRightPsi": "33",
|
|
||||||
|
|
||||||
// "distanceToEmptyFuelMiles": 149.75,
|
|
||||||
// "distanceToEmptyFuelKilometers": 241,
|
|
||||||
|
|
||||||
// "avgFuelConsumptionMpg": 18.5,
|
|
||||||
// "avgFuelConsumptionLitersPer100Kilometers": 12.7,
|
|
||||||
|
|
||||||
// "evStateOfChargePercent": null,
|
|
||||||
// "evDistanceToEmptyMiles": null,
|
|
||||||
// "evDistanceToEmptyKilometers": null,
|
|
||||||
// "evDistanceToEmptyByStateMiles": null,
|
|
||||||
// "evDistanceToEmptyByStateKilometers": null,
|
|
||||||
|
|
||||||
// "vehicleStateType": "IGNITION_OFF",
|
|
||||||
|
|
||||||
// "distanceToEmptyFuelMiles10s": 150,
|
|
||||||
// "distanceToEmptyFuelKilometers10s": 240
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVehicleCondition .
|
// GetVehicleCondition .
|
||||||
@ -788,15 +744,9 @@ func (v *Vehicle) GetVehicleCondition() {
|
|||||||
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
|
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
|
||||||
// v.client.logger.Debug("http request output", "request", "GetVehicleCondition", "body", resp)
|
// v.client.logger.Debug("http request output", "request", "GetVehicleCondition", "body", resp)
|
||||||
|
|
||||||
var r Response
|
if r, ok := v.client.parseResponse(resp); ok {
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
v.client.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var sr ServiceRequest
|
var sr ServiceRequest
|
||||||
err = json.Unmarshal(r.Data, &sr)
|
err := json.Unmarshal(r.Data, &sr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.client.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
v.client.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
|
||||||
}
|
}
|
||||||
@ -813,6 +763,15 @@ func (v *Vehicle) GetVehicleCondition() {
|
|||||||
typeOfS := val.Type()
|
typeOfS := val.Type()
|
||||||
|
|
||||||
for i := 0; i < val.NumField(); i++ {
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
if val.Field(i).Interface() == "NOT_EQUIPPED" ||
|
||||||
|
val.Field(i).Interface() == "UNKNOWN" ||
|
||||||
|
val.Field(i).Interface() == "None" ||
|
||||||
|
val.Field(i).Interface() == "16383" ||
|
||||||
|
val.Field(i).Interface() == "65535" ||
|
||||||
|
val.Field(i).Interface() == "-64" ||
|
||||||
|
val.Field(i).Interface() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
fmt.Printf("Field: %s, Value: %v, Type: %v\n", typeOfS.Field(i).Name, val.Field(i).Interface(), val.Field(i).Type())
|
fmt.Printf("Field: %s, Value: %v, Type: %v\n", typeOfS.Field(i).Name, val.Field(i).Interface(), val.Field(i).Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,36 +911,20 @@ func (v *Vehicle) GetVehicleHealth() {
|
|||||||
resp := v.client.execute(reqURL, GET, params, "", false)
|
resp := v.client.execute(reqURL, GET, params, "", false)
|
||||||
// v.client.logger.Debug("http request output", "request", "GetVehicleHealth", "body", resp)
|
// v.client.logger.Debug("http request output", "request", "GetVehicleHealth", "body", resp)
|
||||||
|
|
||||||
var r Response
|
if r, ok := v.client.parseResponse(resp); ok {
|
||||||
err := json.Unmarshal(resp, &r)
|
|
||||||
if err != nil {
|
|
||||||
v.client.logger.Error("error while parsing json", "request", "GetVehicleHealth", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Success {
|
|
||||||
var vh VehicleHealth
|
var vh VehicleHealth
|
||||||
err = json.Unmarshal(r.Data, &vh)
|
err := json.Unmarshal(r.Data, &vh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.client.logger.Error("error while parsing json", "request", "GetVehicleHealth", "error", err.Error())
|
v.client.logger.Error("error while parsing json", "request", "GetVehicleHealth", "error", err.Error())
|
||||||
}
|
}
|
||||||
v.client.logger.Debug("http request output", "request", "GetVehicleHealth", "vehicle health", vh)
|
v.client.logger.Debug("http request output", "request", "GetVehicleHealth", "vehicle health", vh)
|
||||||
|
|
||||||
// TODO: Loop over all the Vehicle Health Items
|
// TODO: Loop over all the Vehicle Health Items
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
v.client.logger.Error("active STARLINK Security Plus subscription required")
|
v.client.logger.Error("active STARLINK Security Plus subscription required")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // GetClimateSettings .
|
|
||||||
// func (v *Vehicle) GetClimateSettings() {
|
|
||||||
// v.selectVehicle()
|
|
||||||
// reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_CLIMATE_SETTINGS"]
|
|
||||||
// v.client.execute(reqURL, GET, map[string]string{}, "", false)
|
|
||||||
// // TODO
|
|
||||||
// }
|
|
||||||
|
|
||||||
// GetFeaturesList .
|
// GetFeaturesList .
|
||||||
func (v *Vehicle) GetFeaturesList() {
|
func (v *Vehicle) GetFeaturesList() {
|
||||||
for i, f := range v.Features {
|
for i, f := range v.Features {
|
||||||
|
Reference in New Issue
Block a user