Refactor some functions

This commit is contained in:
2025-07-02 12:06:23 +03:00
parent 21a928bf70
commit 0744d16401
5 changed files with 771 additions and 588 deletions

421
client.go
View File

@ -2,11 +2,11 @@ package mysubaru
import ( import (
"encoding/json" "encoding/json"
"errors"
"io" "io"
"log/slog" "log/slog"
"slices" "slices"
"sync" "sync"
"time"
"git.savin.nyc/alex/mysubaru/config" "git.savin.nyc/alex/mysubaru/config"
"resty.dev/v3" "resty.dev/v3"
@ -23,6 +23,7 @@ type Client struct {
listOfVins []string listOfVins []string
isAuthenticated bool isAuthenticated bool
isRegistered bool isRegistered bool
isAlive bool
logger *slog.Logger logger *slog.Logger
sync.RWMutex sync.RWMutex
} }
@ -52,56 +53,40 @@ func New(config *config.Config) (*Client, error) {
client.httpClient = httpClient client.httpClient = httpClient
resp := client.auth() resp := client.auth()
if r, ok := client.parseResponse(resp); ok { var sd SessionData
var sd SessionData err := json.Unmarshal(resp.Data, &sd)
err := json.Unmarshal(r.Data, &sd) if err != nil {
if err != nil { client.logger.Error("error while parsing json", "request", "auth", "error", err.Error())
client.logger.Error("error while parsing json", "request", "auth", "error", err.Error()) }
} // client.logger.Debug("unmarshaled json data", "request", "auth", "type", "sessionData", "body", sd)
// client.logger.Debug("unmarshaled json data", "request", "auth", "type", "sessionData", "body", sd)
if sd.DeviceRegistered && sd.RegisteredDevicePermanent { if sd.DeviceRegistered && sd.RegisteredDevicePermanent {
// client.logger.Debug("client authentication successful") // client.logger.Debug("client authentication successful")
client.isAuthenticated = true client.isAuthenticated = true
client.isRegistered = true client.isRegistered = true
} else { }
// client.logger.Debug("client authentication successful, but devices is not registered") // TODO: Work on registerDevice()
client.registerDevice() // } 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)) // client.logger.Debug("parsing cars assigned to the account", "quantity", len(sd.Vehicles))
if len(sd.Vehicles) > 0 { if len(sd.Vehicles) > 0 {
for _, vehicle := range sd.Vehicles { for _, vehicle := range sd.Vehicles {
// client.logger.Debug("parsing car", "vin", vehicle.Vin) // client.logger.Debug("parsing car", "vin", vehicle.Vin)
client.listOfVins = append(client.listOfVins, 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
} }
client.currentVin = client.listOfVins[0]
} else { } else {
// TODO: Work on errors client.logger.Error("there no cars assigned to the account")
// error, _ := respParsed.Path("errorCode").Data().(string) return nil, err
// 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 return client, nil
} }
// SelectVehicle . // SelectVehicle .
func (c *Client) SelectVehicle(vin string) VehicleData { func (c *Client) SelectVehicle(vin string) (*VehicleData, error) {
if vin == "" { if vin == "" {
vin = c.currentVin vin = c.currentVin
} }
@ -112,93 +97,93 @@ func (c *Client) SelectVehicle(vin string) VehicleData {
"vin": vin, "vin": vin,
"_": timestamp()} "_": timestamp()}
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"] reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
resp := c.execute(reqURL, GET, params, "", false) // TODO: Add error handling
resp, _ := c.execute(GET, reqURL, params, false)
// c.logger.Debug("http request output", "request", "SelectVehicle", "body", resp) // c.logger.Debug("http request output", "request", "SelectVehicle", "body", resp)
if r, ok := c.parseResponse(resp); ok { var vd VehicleData
var vd VehicleData err := json.Unmarshal(resp.Data, &vd)
err := json.Unmarshal(r.Data, &vd) if err != nil {
if err != nil { c.logger.Error("error while parsing json", "request", "SelectVehicle", "error", err.Error())
c.logger.Error("error while parsing json", "request", "SelectVehicle", "error", err.Error()) return nil, errors.New("error while parsing json while vehicle selection")
}
// c.logger.Debug("http request output", "request", "SelectVehicle", "body", resp)
return vd
} else {
return VehicleData{}
} }
// c.logger.Debug("http request output", "request", "SelectVehicle", "body", resp)
return &vd, nil
} }
// GetVehicles . // GetVehicles .
func (c *Client) GetVehicles() []*Vehicle { func (c *Client) GetVehicles() []*Vehicle {
var vehicles []*Vehicle var vehicles []*Vehicle
for _, vin := range c.listOfVins { for _, vin := range c.listOfVins {
vehicle := c.GetVehicleByVIN(vin) vehicle, err := c.GetVehicleByVIN(vin)
if err != nil {
c.logger.Error("cannot get vehile data", "request", "SelectVehicle", "error", err.Error())
}
vehicles = append(vehicles, vehicle) vehicles = append(vehicles, vehicle)
} }
return vehicles return vehicles
} }
// GetVehicleByVIN . // GetVehicleByVIN .
func (c *Client) GetVehicleByVIN(vin string) *Vehicle { func (c *Client) GetVehicleByVIN(vin string) (*Vehicle, error) {
var vehicle *Vehicle var vehicle *Vehicle
if slices.Contains(c.listOfVins, vin) { if slices.Contains(c.listOfVins, vin) {
params := map[string]string{ params := map[string]string{
"vin": vin, "vin": vin,
"_": timestamp()} "_": timestamp()}
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"] reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
resp := c.execute(reqURL, GET, params, "", false) // TODO: Add error handling
resp, _ := c.execute(GET, reqURL, params, false)
// c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp) // c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
if r, ok := c.parseResponse(resp); ok { var vd VehicleData
var vd VehicleData err := json.Unmarshal(resp.Data, &vd)
err := json.Unmarshal(r.Data, &vd) if err != nil {
if err != nil { c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error())
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.Troubles = make(map[string]Trouble)
if vehicle.isEV() {
vehicle.EV = true
} else {
vehicle.EV = false
}
vehicle.GetVehicleStatus()
vehicle.GetVehicleCondition()
vehicle.GetVehicleHealth()
vehicle.GetClimatePresets()
vehicle.GetClimateUserPresets()
vehicle.GetClimateQuickPresets()
return vehicle
} }
// 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.Troubles = make(map[string]Trouble)
if vehicle.isEV() {
vehicle.EV = true
} else {
vehicle.EV = false
}
vehicle.GetVehicleStatus()
vehicle.GetVehicleCondition()
vehicle.GetVehicleHealth()
vehicle.GetClimatePresets()
vehicle.GetClimateUserPresets()
vehicle.GetClimateQuickPresets()
return vehicle, nil
} }
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN") c.logger.Error("vin code is not in the list of the available vin codes", "request", "GetVehicleByVIN")
return &Vehicle{} return nil, errors.New("vin code is not in the list of the available vin codes")
} }
// func isPINRequired() {} // func isPINRequired() {}
@ -211,8 +196,12 @@ func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
// func getClimateData() {} // func getClimateData() {}
// func saveClimateSettings() {} // func saveClimateSettings() {}
func (c *Client) IsAlive() bool {
return c.isAlive
}
// Exec method executes a Client instance with the API URL // 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, attempts ...int) []byte { func (c *Client) execute(method string, url string, params map[string]string, j bool) (*Response, error) {
// defer timeTrack("[TIMETRK] Executing HTTP Request") // defer timeTrack("[TIMETRK] Executing HTTP Request")
var resp *resty.Response var resp *resty.Response
@ -221,7 +210,7 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
resp, _ = c.httpClient. resp, _ = c.httpClient.
R(). R().
SetQueryParams(params). SetQueryParams(params).
Get(requestUrl) Get(url)
} }
// POST Requests // POST Requests
@ -230,70 +219,47 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
resp, _ = c.httpClient. resp, _ = c.httpClient.
R(). R().
SetBody(params). SetBody(params).
Post(requestUrl) Post(url)
} else { // POST > Form Data } else { // POST > Form Data
resp, _ = c.httpClient. resp, _ = c.httpClient.
R(). R().
SetFormData(params). SetFormData(params).
Post(requestUrl) Post(url)
} }
} }
resBytes, err := io.ReadAll(resp.Body)
if err != nil {
c.logger.Error("error while getting body", "error", err.Error())
}
c.logger.Debug("parsed http request output", "data", string(resBytes))
if r, ok := c.parseResponse(resBytes); ok { if resp.IsSuccess() {
// c.logger.Debug("parsed http request output", "data", r.Data) resBytes, err := io.ReadAll(resp.Body)
if err != nil {
c.logger.Error("error while getting body", "error", err.Error())
}
c.logger.Debug("parsed http request output", "data", string(resBytes))
// dataName field has the list of the states [ remoteServiceStatus | errorResponse ] if r, ok := c.parseResponse(resBytes); ok {
if r.DataName == "remoteServiceStatus" { c.isAlive = true
var sr ServiceRequest return &r, nil
err := json.Unmarshal(r.Data, &sr) } else {
if err != nil { if r.DataName == "errorResponse" {
c.logger.Error("error while parsing json", "request", "remoteServiceStatus", "error", err.Error()) var er ErrorResponse
} err := json.Unmarshal(r.Data, &er)
if err != nil {
if pollingUrl != "" { c.logger.Error("error while parsing json", "request", "errorResponse", "error", err.Error())
switch {
case sr.RemoteServiceState == "finished":
// Finished RemoteServiceState Service Request does not include Service Request ID
c.logger.Debug("Remote service request completed successfully")
case sr.RemoteServiceState == "started":
time.Sleep(5 * time.Second)
c.logger.Debug("Subaru API reports remote service request (started) is in progress", "id", sr.ServiceRequestID)
c.execute(pollingUrl, GET, map[string]string{"serviceRequestId": sr.ServiceRequestID}, pollingUrl, false)
case sr.RemoteServiceState == "stopping":
time.Sleep(5 * time.Second)
c.logger.Debug("Subaru API reports remote service request (stopping) is in progress", "id", sr.ServiceRequestID)
c.execute(pollingUrl, GET, map[string]string{"serviceRequestId": sr.ServiceRequestID}, pollingUrl, false)
default:
time.Sleep(5 * time.Second)
c.logger.Debug("Subaru API reports remote service request (stopping) is in progress")
c.execute(pollingUrl, GET, map[string]string{"serviceRequestId": sr.ServiceRequestID}, pollingUrl, false, 1)
} }
if _, ok := API_ERRORS[er.ErrorLabel]; ok {
c.logger.Error("request got an error", "request", "execute", "method", method, "url", url, "label", er.ErrorLabel, "descrip[tion", er.ErrorDescription)
}
c.logger.Error("request got an unknown error", "request", "execute", "method", method, "url", url, "label", er.ErrorLabel, "descrip[tion", er.ErrorDescription)
return nil, errors.New("request is not successfull, HTTP code: " + resp.Status())
} }
c.logger.Error("request is not successfull", "request", "execute", "method", method, "url", url, "error", err.Error())
} }
} else {
if r.DataName == "errorResponse" {
var er ErrorResponse
err := json.Unmarshal(r.Data, &er)
if err != nil {
c.logger.Error("error while parsing json", "request", "errorResponse", "error", err.Error())
}
if _, ok := API_ERRORS[er.ErrorLabel]; ok {
c.logger.Error("request got an error", "request", "execute", "method", method, "url", requestUrl, "label", er.ErrorLabel, "descrip[tion", er.ErrorDescription)
}
c.logger.Error("request got an unknown error", "request", "execute", "method", method, "url", requestUrl, "label", er.ErrorLabel, "descrip[tion", er.ErrorDescription)
}
c.logger.Error("request is not successfull", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
} }
return resBytes c.isAlive = false
return nil, errors.New("request is not successfull, HTTP code: " + resp.Status())
} }
// auth . // auth .
func (c *Client) auth() []byte { func (c *Client) auth() *Response {
params := map[string]string{ params := map[string]string{
"env": "cloudprod", "env": "cloudprod",
"deviceType": "android", "deviceType": "android",
@ -304,7 +270,8 @@ func (c *Client) auth() []byte {
"selectedVin": "", "selectedVin": "",
"pushToken": ""} "pushToken": ""}
reqURL := MOBILE_API_VERSION + apiURLs["API_LOGIN"] reqURL := MOBILE_API_VERSION + apiURLs["API_LOGIN"]
resp := c.execute(reqURL, POST, params, "", false) // TODO: Add error handling
resp, _ := c.execute(POST, reqURL, params, false)
// c.logger.Debug("AUTH HTTP OUTPUT", "body", string([]byte(resp))) // c.logger.Debug("AUTH HTTP OUTPUT", "body", string([]byte(resp)))
return resp return resp
} }
@ -315,29 +282,23 @@ func (c *Client) parseResponse(b []byte) (Response, bool) {
err := json.Unmarshal(b, &r) err := json.Unmarshal(b, &r)
if err != nil { if err != nil {
c.logger.Error("error while parsing json", "error", err.Error()) c.logger.Error("error while parsing json", "error", err.Error())
return r, false
} }
return r, true return r, true
} }
// validateSession . // validateSession .
// TODO: add session validation process and add it to the proper functions
// func (c *Client) validateSession() bool { // func (c *Client) validateSession() bool {
// // {
// // "success": true,
// // "errorCode": null,
// // "dataName": null,
// // "data": null
// // }
// reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"] // reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"]
// resp := c.execute(reqURL, GET, map[string]string{}, "", false) // resp := c.execute(reqURL, GET, map[string]string{}, "", false)
// c.logger.Debug("http request output", "request", "validateSession", "body", resp) // // c.logger.Debug("http request output", "request", "validateSession", "body", resp)
// var r Response // if r, ok := c.parseResponse(resp); ok {
// err := json.Unmarshal(resp, &r)
// if err != nil {
// c.logger.Error("error while parsing json", "request", "validateSession", "error", err.Error()) // c.logger.Error("error while parsing json", "request", "validateSession", "error", err.Error())
// } // return true
// } else {
// if r.Success { // resp := c.auth()
// return true // return true
// } // }
// return false // return false
@ -358,50 +319,50 @@ func (c *Client) parseResponse(b []byte) (Response, bool) {
// {"success":true,"dataName":"authorizedDevices","data":[{"telematicsClientDeviceKey":2574212,"deviceType":"android","deviceName":"Alex Google Pixel 4 XL","createdDate":"2019-11-29T20:32:21.000+0000","modifiedDate":"2020-06-08T17:48:22.000+0000"},{"telematicsClientDeviceKey":4847533,"deviceName":"Home Assistant: Added 2021-03-03","createdDate":"2021-03-03T20:53:44.000+0000","modifiedDate":"2021-03-03T20:53:47.000+0000"},{"telematicsClientDeviceKey":7222995,"deviceType":"android","deviceName":"Alex Google Pixel 6 Pro","createdDate":"2021-10-28T15:27:36.000+0000","modifiedDate":"2021-10-28T15:27:58.000+0000"},{"telematicsClientDeviceKey":8207130,"deviceName":"Mac/iOS Chrome","createdDate":"2021-12-21T21:19:40.000+0000","modifiedDate":"2021-12-21T21:19:40.000+0000"}]} // {"success":true,"dataName":"authorizedDevices","data":[{"telematicsClientDeviceKey":2574212,"deviceType":"android","deviceName":"Alex Google Pixel 4 XL","createdDate":"2019-11-29T20:32:21.000+0000","modifiedDate":"2020-06-08T17:48:22.000+0000"},{"telematicsClientDeviceKey":4847533,"deviceName":"Home Assistant: Added 2021-03-03","createdDate":"2021-03-03T20:53:44.000+0000","modifiedDate":"2021-03-03T20:53:47.000+0000"},{"telematicsClientDeviceKey":7222995,"deviceType":"android","deviceName":"Alex Google Pixel 6 Pro","createdDate":"2021-10-28T15:27:36.000+0000","modifiedDate":"2021-10-28T15:27:58.000+0000"},{"telematicsClientDeviceKey":8207130,"deviceName":"Mac/iOS Chrome","createdDate":"2021-12-21T21:19:40.000+0000","modifiedDate":"2021-12-21T21:19:40.000+0000"}]}
// {"success":true,"dataName":"authorizedDevices","data":[{"telematicsClientDeviceKey":2574212,"deviceType":"android","deviceName":"Alex Google Pixel 4 XL","createdDate":"2019-11-29T20:32:21.000+0000","modifiedDate":"2020-06-08T17:48:22.000+0000"},{"telematicsClientDeviceKey":4847533,"deviceName":"Home Assistant: Added 2021-03-03","createdDate":"2021-03-03T20:53:44.000+0000","modifiedDate":"2021-03-03T20:53:47.000+0000"},{"telematicsClientDeviceKey":7222995,"deviceType":"android","deviceName":"Alex Google Pixel 6 Pro","createdDate":"2021-10-28T15:27:36.000+0000","modifiedDate":"2021-10-28T15:27:58.000+0000"},{"telematicsClientDeviceKey":8210723,"deviceName":"Hassio Golang Integration","createdDate":"2021-12-22T01:38:43.000+0000","modifiedDate":"2021-12-22T01:38:43.000+0000"},{"telematicsClientDeviceKey":8207130,"deviceName":"Mac/iOS Chrome","createdDate":"2021-12-21T21:19:40.000+0000","modifiedDate":"2021-12-21T21:19:40.000+0000"}]} // {"success":true,"dataName":"authorizedDevices","data":[{"telematicsClientDeviceKey":2574212,"deviceType":"android","deviceName":"Alex Google Pixel 4 XL","createdDate":"2019-11-29T20:32:21.000+0000","modifiedDate":"2020-06-08T17:48:22.000+0000"},{"telematicsClientDeviceKey":4847533,"deviceName":"Home Assistant: Added 2021-03-03","createdDate":"2021-03-03T20:53:44.000+0000","modifiedDate":"2021-03-03T20:53:47.000+0000"},{"telematicsClientDeviceKey":7222995,"deviceType":"android","deviceName":"Alex Google Pixel 6 Pro","createdDate":"2021-10-28T15:27:36.000+0000","modifiedDate":"2021-10-28T15:27:58.000+0000"},{"telematicsClientDeviceKey":8210723,"deviceName":"Hassio Golang Integration","createdDate":"2021-12-22T01:38:43.000+0000","modifiedDate":"2021-12-22T01:38:43.000+0000"},{"telematicsClientDeviceKey":8207130,"deviceName":"Mac/iOS Chrome","createdDate":"2021-12-21T21:19:40.000+0000","modifiedDate":"2021-12-21T21:19:40.000+0000"}]}
// registerDevice . // // registerDevice .
func (c *Client) registerDevice() bool { // func (c *Client) registerDevice() bool {
// c.httpClient. // // c.httpClient.
// SetBaseURL(WEB_API_SERVER[c.country]). // // SetBaseURL(WEB_API_SERVER[c.country]).
// R(). // // R().
// SetFormData(map[string]string{ // // SetFormData(map[string]string{
// "username": c.credentials.username, // // "username": c.credentials.username,
// "password": c.credentials.password, // // "password": c.credentials.password,
// "deviceId": c.credentials.deviceId, // // "deviceId": c.credentials.deviceId,
// }). // // }).
// Post(apiURLs["WEB_API_LOGIN"]) // // Post(apiURLs["WEB_API_LOGIN"])
params := map[string]string{ // params := map[string]string{
"username": c.credentials.Username, // "username": c.credentials.Username,
"password": c.credentials.Password, // "password": c.credentials.Password,
"deviceId": c.credentials.DeviceID} // "deviceId": c.credentials.DeviceID}
reqURL := WEB_API_SERVER[c.country] + apiURLs["WEB_API_LOGIN"] // reqURL := WEB_API_SERVER[c.country] + apiURLs["WEB_API_LOGIN"]
c.execute(reqURL, POST, params, "", true) // resp, _ := c.execute(POST, reqURL, params, true)
// Authorizing device via web API // // Authorizing device via web API
// c.httpClient. // // c.httpClient.
// SetBaseURL(WEB_API_SERVER[c.country]). // // SetBaseURL(WEB_API_SERVER[c.country]).
// R(). // // R().
// SetQueryParams(map[string]string{ // // SetQueryParams(map[string]string{
// "deviceId": c.credentials.deviceId, // // "deviceId": c.credentials.deviceId,
// }). // // }).
// Get(apiURLs["WEB_API_AUTHORIZE_DEVICE"]) // // Get(apiURLs["WEB_API_AUTHORIZE_DEVICE"])
params = map[string]string{ // params = map[string]string{
"deviceId": c.credentials.DeviceID} // "deviceId": c.credentials.DeviceID}
reqURL = WEB_API_SERVER[c.country] + apiURLs["WEB_API_AUTHORIZE_DEVICE"] // reqURL = WEB_API_SERVER[c.country] + apiURLs["WEB_API_AUTHORIZE_DEVICE"]
c.execute(reqURL, GET, params, "", false) // c.execute(reqURL, GET, params, "", false)
return c.setDeviceName() // return c.setDeviceName()
} // }
// setDeviceName . // // setDeviceName .
func (c *Client) setDeviceName() bool { // func (c *Client) setDeviceName() bool {
params := map[string]string{ // params := map[string]string{
"deviceId": c.credentials.DeviceID, // "deviceId": c.credentials.DeviceID,
"deviceName": c.credentials.DeviceName} // "deviceName": c.credentials.DeviceName}
reqURL := WEB_API_SERVER[c.country] + apiURLs["WEB_API_NAME_DEVICE"] // reqURL := WEB_API_SERVER[c.country] + apiURLs["WEB_API_NAME_DEVICE"]
c.execute(reqURL, GET, params, "", false) // c.execute(reqURL, GET, params, "", false)
return true // return true
} // }
// // listDevices . // // listDevices .
// func (c *Client) listDevices() { // func (c *Client) listDevices() {
@ -457,3 +418,45 @@ func (c *Client) setDeviceName() bool {
// // logger.Debugf("LIST DEVICES OUTPUT >> %v\n", string(resp)) // // logger.Debugf("LIST DEVICES OUTPUT >> %v\n", string(resp))
// // } // // }
// } // }
// c.logger.Debug("parsed http request output", "data", r.Data)
// ERROR
// {"httpCode":500,"errorCode":"error","errorMessage":"org.springframework.web.HttpMediaTypeNotSupportedException - Content type 'application/x-www-form-urlencoded' not supported"}
// {"success":true,"errorCode":null,"dataName":"remoteServiceStatus","data":{"serviceRequestId":"4S4BTGND8L3137058_1640203129607_19_@NGTP","success":false,"cancelled":false,"remoteServiceType":"unlock","remoteServiceState":"started","subState":null,"errorCode":null,"result":null,"updateTime":null,"vin":"4S4BTGND8L3137058"}}
// API_REMOTE_SVC_STATUS
// {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}}
// if js_resp["errorCode"] == sc.ERROR_SOA_403:
// raise RemoteServiceFailure("Backend session expired, please try again")
// if js_resp["data"]["remoteServiceState"] == "finished":
// if js_resp["data"]["success"]:
// _LOGGER.info("Remote service request completed successfully: %s", req_id)
// return True, js_resp
// _LOGGER.error(
// "Remote service request completed but failed: %s Error: %s",
// req_id,
// js_resp["data"]["errorCode"],
// )
// raise RemoteServiceFailure(
// "Remote service request completed but failed: %s" % js_resp["data"]["errorCode"]
// )
// if js_resp["data"].get("remoteServiceState") == "started":
// _LOGGER.info(
// "Subaru API reports remote service request is in progress: %s",
// req_id,
// )
// attempts_left -= 1
// await asyncio.sleep(2)
// 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")
// }

View File

@ -102,6 +102,8 @@ var API_ERRORS = map[string]string{
"passwordWarning": "ERROR_PASSWORD_WARNING", "passwordWarning": "ERROR_PASSWORD_WARNING",
"accountLocked": "ERROR_ACCOUNT_LOCKED", "accountLocked": "ERROR_ACCOUNT_LOCKED",
"noVehiclesOnAccount": "ERROR_NO_VEHICLES", "noVehiclesOnAccount": "ERROR_NO_VEHICLES",
"noVehiclesAvailable": "ERROR_NO_VEHICLE_AVAILABLE",
"VEHICLESETUPERROR": "ERROR_VEHICLE_SETUP_ERROR", // Vehicle Select
"accountNotFound": "ERROR_NO_ACCOUNT", "accountNotFound": "ERROR_NO_ACCOUNT",
"tooManyAttempts": "ERROR_TOO_MANY_ATTEMPTS", "tooManyAttempts": "ERROR_TOO_MANY_ATTEMPTS",
"vehicleNotInAccount": "ERROR_VEHICLE_NOT_IN_ACCOUNT", "vehicleNotInAccount": "ERROR_VEHICLE_NOT_IN_ACCOUNT",
@ -112,6 +114,10 @@ var API_ERRORS = map[string]string{
"SXM40017": "ERROR_G1_PIN_LOCKED", "SXM40017": "ERROR_G1_PIN_LOCKED",
} }
var APP_ERRORS = map[string]string{
"SUBSCRIBTION_REQUIRED": "active STARLINK Security Plus subscription required",
}
// TODO: Get back and add error wrapper // TODO: Get back and add error wrapper
// var apiErrors = map[string]string{ // var apiErrors = map[string]string{
// "ERROR_SOA_403": "403-soa-unableToParseResponseBody", // G2 Error Codes // "ERROR_SOA_403": "403-soa-unableToParseResponseBody", // G2 Error Codes

1
go.mod
View File

@ -11,6 +11,7 @@ require (
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/neilotoole/slogt v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.41.0 // indirect golang.org/x/net v0.41.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect

View File

@ -25,6 +25,27 @@ type Response struct {
// Unmarshal . // Unmarshal .
// func (r *Response) Unmarshal(b []byte) {} // func (r *Response) Unmarshal(b []byte) {}
// Request .
type Request struct {
Vin string `json:"vin"` //
Pin string `json:"pin"` //
Delay int `json:"delay,string,omitempty"` //
ForceKeyInCar *bool `json:"forceKeyInCar,string,omitempty"` //
UnlockDoorType *string `json:"unlockDoorType,omitempty"` // [ ALL_DOORS_CMD | FRONT_LEFT_DOOR_CMD | ALL_DOORS_CMD ]
Horn *string `json:"horn,omitempty"` //
ClimateSettings *string `json:"climateSettings,omitempty"` //
ClimateZoneFrontTemp *string `json:"climateZoneFrontTemp,omitempty"` //
ClimateZoneFrontAirMode *string `json:"climateZoneFrontAirMode,omitempty"` //
ClimateZoneFrontAirVolume *string `json:"climateZoneFrontAirVolume,omitempty"` //
HeatedSeatFrontLeft *string `json:"heatedSeatFrontLeft,omitempty"` //
HeatedSeatFrontRight *string `json:"heatedSeatFrontRight,omitempty"` //
HeatedRearWindowActive *string `json:"heatedRearWindowActive,omitempty"` //
OuterAirCirculation *string `json:"outerAirCirculation,omitempty"` //
AirConditionOn *string `json:"airConditionOn,omitempty"` //
RunTimeMinutes *string `json:"runTimeMinutes,omitempty"` //
StartConfiguration *string `json:"startConfiguration,omitempty"` //
}
// Account . // Account .
type Account struct { type Account struct {
MarketID int `json:"marketId"` MarketID int `json:"marketId"`
@ -277,17 +298,6 @@ type ServiceRequest struct {
Vin string `json:"vin"` // 4S4BTGND8L3137058 Vin string `json:"vin"` // 4S4BTGND8L3137058
} }
// ErrorResponse .
// "dataName":"errorResponse"
// {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}}
// {"success":false,"errorCode":"vehicleNotInAccount","dataName":null,"data":null}
// {"httpCode":500,"errorCode":"error","errorMessage":"java.lang.NullPointerException - null"}
// {"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"}}
type ErrorResponse struct {
ErrorLabel string `json:"errorLabel"` // "404-soa-unableToParseResponseBody"
ErrorDescription string `json:"errorDescription,omitempty"` // null
}
// climateSettings: [ climateSettings ] // climateSettings: [ climateSettings ]
// climateZoneFrontTempCelsius: [for _ in range(15, 30 + 1)] // climateZoneFrontTempCelsius: [for _ in range(15, 30 + 1)]
// climateZoneFrontTemp: [for _ in range(60, 85 + 1)] // climateZoneFrontTemp: [for _ in range(60, 85 + 1)]
@ -313,3 +323,14 @@ type VehicleHealthItem struct {
OnDates []int64 `json:"onDates,omitempty"` // List of the timestamps OnDates []int64 `json:"onDates,omitempty"` // List of the timestamps
WarningCode int `json:"warningCode"` WarningCode int `json:"warningCode"`
} }
// ErrorResponse .
// "dataName":"errorResponse"
// {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}}
// {"success":false,"errorCode":"vehicleNotInAccount","dataName":null,"data":null}
// {"httpCode":500,"errorCode":"error","errorMessage":"java.lang.NullPointerException - null"}
// {"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"}}
type ErrorResponse struct {
ErrorLabel string `json:"errorLabel"` // "404-soa-unableToParseResponseBody"
ErrorDescription string `json:"errorDescription,omitempty"` // null
}

File diff suppressed because it is too large Load Diff