Updated config file and config struct
All checks were successful
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Successful in 23s

This commit is contained in:
2025-05-27 22:52:02 -04:00
parent c00d0d4abf
commit ea31ffb651
5 changed files with 266 additions and 659 deletions

395
client.go
View File

@ -13,19 +13,9 @@ import (
"resty.dev/v3"
)
// credentials .
type credentials struct {
username string
password string
pin string
deviceId string
deviceName string
}
// Client .
type Client struct {
baseURL string
credentials credentials
credentials config.Credentials
httpClient *resty.Client
country string // USA | CA
updateInterval int // 7200
@ -38,125 +28,14 @@ type Client struct {
sync.RWMutex
}
// Option .
type Option func(*Client) error
// BaseURL allows overriding of API client baseURL for testing
func BaseURL(baseURL string) Option {
return func(c *Client) error {
c.baseURL = baseURL
return nil
}
}
// Username .
func Username(username string) Option {
return func(c *Client) error {
c.credentials.username = username
return nil
}
}
// Password .
func Password(password string) Option {
return func(c *Client) error {
c.credentials.password = password
return nil
}
}
// auth .
func (c *Client) auth() []byte {
// {
// "success": true,
// "errorCode": null,
// "dataName": "sessionData",
// "data": {
// "sessionChanged": false,
// "vehicleInactivated": false,
// "account": {
// "marketId": 1,
// "createdDate": 1476984644000,
// "firstName": "Tatiana",
// "lastName": "Savin",
// "zipCode": "07974",
// "accountKey": 765268,
// "lastLoginDate": 1640539132000,
// "zipCode5": "07974"
// },
// "resetPassword": false,
// "deviceId": "JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ",
// "sessionId": "010A2C0C25A5D35AC3776DB6B7900A3B",
// "deviceRegistered": true,
// "passwordToken": null,
// "vehicles": [
// {
// "customer": {
// "sessionCustomer": null,
// "email": null,
// "firstName": null,
// "lastName": null,
// "zip": null,
// "oemCustId": null,
// "phone": null
// },
// "stolenVehicle": false,
// "vehicleName": "Subaru Outback LXT",
// "features": null,
// "vin": "4S4BTGND8L3137058",
// "modelYear": null,
// "modelCode": null,
// "engineSize": null,
// "nickname": "Subaru Outback LXT",
// "vehicleKey": 3832950,
// "active": true,
// "licensePlate": "",
// "licensePlateState": "",
// "email": null,
// "firstName": null,
// "lastName": null,
// "subscriptionFeatures": null,
// "accessLevel": -1,
// "zip": null,
// "oemCustId": "CRM-631-HQN48K",
// "vehicleMileage": null,
// "phone": null,
// "userOemCustId": "CRM-631-HQN48K",
// "subscriptionStatus": null,
// "authorizedVehicle": false,
// "preferredDealer": null,
// "cachedStateCode": "NJ",
// "subscriptionPlans": [],
// "needMileagePrompt": false,
// "phev": null,
// "remoteServicePinExist": true,
// "needEmergencyContactPrompt": false,
// "vehicleGeoPosition": null,
// "extDescrip": null,
// "intDescrip": null,
// "modelName": null,
// "transCode": null,
// "provisioned": true,
// "timeZone": "America/New_York"
// }
// ],
// "currentVehicleIndex": 0,
// "handoffToken": "$2a$08$99me3RRpB00MNMSFxrG7AOg1T5BaDVacqXhbdTii0eRXoEoeFvEPy$1640540934344",
// "enableXtime": true,
// "termsAndConditionsAccepted": true,
// "digitalGlobeConnectId": "0572e32b-2fcf-4bc8-abe0-1e3da8767132",
// "digitalGlobeImageTileService": "https://earthwatch.digitalglobe.com/earthservice/tmsaccess/tms/1.0.0/DigitalGlobe:ImageryTileService@EPSG:3857@png/{z}/{x}/{y}.png?connectId=0572e32b-2fcf-4bc8-abe0-1e3da8767132",
// "digitalGlobeTransparentTileService": "https://earthwatch.digitalglobe.com/earthservice/tmsaccess/tms/1.0.0/Digitalglobe:OSMTransparentTMSTileService@EPSG:3857@png/{z}/{x}/{-y}.png/?connectId=0572e32b-2fcf-4bc8-abe0-1e3da8767132",
// "tomtomKey": "DHH9SwEQ4MW55Hj2TfqMeldbsDjTdgAs",
// "satelliteViewEnabled": true
// }
// }
params := map[string]string{
"env": "cloudprod",
"deviceType": "android",
"loginUsername": c.credentials.username,
"password": c.credentials.password,
"deviceId": c.credentials.deviceId,
"loginUsername": c.credentials.Username,
"password": c.credentials.Password,
"deviceId": c.credentials.DeviceID,
"passwordToken": "",
"selectedVin": "",
"pushToken": ""}
@ -195,9 +74,9 @@ func (c *Client) registerDevice() bool {
// }).
// Post(apiURLs["WEB_API_LOGIN"])
params := map[string]string{
"username": c.credentials.username,
"password": c.credentials.password,
"deviceId": c.credentials.deviceId}
"username": c.credentials.Username,
"password": c.credentials.Password,
"deviceId": c.credentials.DeviceID}
reqURL := WEB_API_SERVER[c.country] + apiURLs["WEB_API_LOGIN"]
c.execute(reqURL, POST, params, "", true)
@ -210,7 +89,7 @@ func (c *Client) registerDevice() bool {
// }).
// Get(apiURLs["WEB_API_AUTHORIZE_DEVICE"])
params = map[string]string{
"deviceId": c.credentials.deviceId}
"deviceId": c.credentials.DeviceID}
reqURL = WEB_API_SERVER[c.country] + apiURLs["WEB_API_AUTHORIZE_DEVICE"]
c.execute(reqURL, GET, params, "", false)
@ -220,8 +99,8 @@ func (c *Client) registerDevice() bool {
// setDeviceName .
func (c *Client) setDeviceName() bool {
params := map[string]string{
"deviceId": c.credentials.deviceId,
"deviceName": c.credentials.deviceName}
"deviceId": c.credentials.DeviceID,
"deviceName": c.credentials.DeviceName}
reqURL := WEB_API_SERVER[c.country] + apiURLs["WEB_API_NAME_DEVICE"]
c.execute(reqURL, GET, params, "", false)
@ -315,111 +194,76 @@ func (c *Client) validateSession() bool {
// if await self._select_vehicle(vin):
// result = True
c.log.Debug("[DEBUG] Validate Session", "body", string([]byte(resp)))
c.log.Debug("session validation", "body", string([]byte(resp)))
return true
}
func (c *Client) SelectVehicle(vin string) VehicleData {
// {
// "success": true,
// "errorCode": null,
// "dataName": "vehicle",
// "data": {
// "customer": {
// "sessionCustomer": null,
// "email": null,
// "firstName": null,
// "lastName": null,
// "zip": null,
// "oemCustId": null,
// "phone": null
// },
// "stolenVehicle": false,
// "vehicleName": "Subaru Outback LXT",
// "features": [
// "ATF_MIL",
// "11.6MMAN",
// "ABS_MIL",
// "CEL_MIL",
// "ACCS",
// "RCC",
// "REARBRK",
// "TEL_MIL",
// "VDC_MIL",
// "TPMS_MIL",
// "WASH_MIL",
// "BSDRCT_MIL",
// "OPL_MIL",
// "EYESIGHT",
// "RAB_MIL",
// "SRS_MIL",
// "ESS_MIL",
// "RESCC",
// "EOL_MIL",
// "BSD",
// "EBD_MIL",
// "EPB_MIL",
// "RES",
// "RHSF",
// "AWD_MIL",
// "NAV_TOMTOM",
// "ISS_MIL",
// "RPOIA",
// "EPAS_MIL",
// "RPOI",
// "AHBL_MIL",
// "SRH_MIL",
// "g2"
// ],
// "vin": "4S4BTGND8L3137058",
// "modelYear": "2020",
// "modelCode": "LDJ",
// "engineSize": 2.4,
// "nickname": "Subaru Outback LXT",
// "vehicleKey": 3832950,
// "active": true,
// "licensePlate": "8KV8",
// "licensePlateState": "NJ",
// "email": null,
// "firstName": null,
// "lastName": null,
// "subscriptionFeatures": [
// "REMOTE",
// "SAFETY",
// "Retail"
// ],
// "accessLevel": -1,
// "zip": null,
// "oemCustId": "CRM-631-HQN48K",
// "vehicleMileage": null,
// "phone": null,
// "userOemCustId": "CRM-631-HQN48K",
// "subscriptionStatus": "ACTIVE",
// "authorizedVehicle": false,
// "preferredDealer": null,
// "cachedStateCode": "NJ",
// "subscriptionPlans": [],
// "needMileagePrompt": false,
// "phev": null,
// "remoteServicePinExist": true,
// "needEmergencyContactPrompt": false,
// "vehicleGeoPosition": {
// "latitude": 40.70019,
// "longitude": -74.401375,
// "speed": null,
// "heading": null,
// "timestamp": 1640494569000
// },
// "extDescrip": "Abyss Blue Pearl",
// "intDescrip": "Gray",
// "modelName": "Outback",
// "transCode": "CVT",
// "provisioned": true,
// "timeZone": "America/New_York"
// }
// }
// New function creates a New MySubaru client
func New(logger *slog.Logger, config *config.MySubaru) (*Client, error) {
client := &Client{
credentials: config.Credentials,
country: config.Region,
updateInterval: 7200,
fetchInterval: 360,
log: 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()
respParsed, err := gabs.ParseJSON(resp)
if err != nil {
panic(err)
}
if client.isResponseSuccessfull(resp) {
logger.Debug("Client authentication successful", "isRegistered", respParsed.Path("data.deviceRegistered").Data().(bool))
client.isAuthenticated = true
client.isRegistered = respParsed.Path("data.deviceRegistered").Data().(bool)
} else {
error, _ := respParsed.Path("errorCode").Data().(string)
switch {
case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
logger.Debug("Invalid account")
case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
logger.Debug("Client authentication failed")
case error == apiErrors["ERROR_PASSWORD_WARNING"]:
logger.Debug("Multiple Password Failures.")
default:
logger.Debug("Uknown error")
}
}
if !client.isRegistered {
client.registerDevice()
}
// TODO
fmt.Printf("Parcing cars: %d\n", len(respParsed.Path("data.vehicles").Children()))
for _, vehicle := range respParsed.Path("data.vehicles").Children() {
fmt.Printf("CAR: %s\n", vehicle.Path("vin").Data().(string))
client.listOfVins = append(client.listOfVins, vehicle.Path("vin").Data().(string))
}
client.currentVin = respParsed.Path("data.vehicles.0.vin").Data().(string)
return client, nil
}
func (c *Client) SelectVehicle(vin string) VehicleData {
// API > json > dataName > vehicle
if vin == "" {
vin = c.currentVin
}
@ -467,78 +311,6 @@ func (c *Client) SelectVehicle(vin string) VehicleData {
return vData
}
// New function creates a New MySubaru client
func New(logger *slog.Logger, config *config.MySubaru) (*Client, error) {
credentials := credentials{
username: config.Credentials.Username,
password: config.Credentials.Password,
pin: config.Credentials.PIN,
deviceId: config.Credentials.DeviceID,
deviceName: config.Credentials.DeviceName,
}
client := &Client{
credentials: credentials,
country: config.Region,
updateInterval: 7200,
fetchInterval: 360,
log: 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()
respParsed, err := gabs.ParseJSON(resp)
if err != nil {
panic(err)
}
if client.isResponseSuccessfull(resp) {
logger.Debug("Client authentication successful", "isRegistered", respParsed.Path("data.deviceRegistered").Data().(bool))
client.isAuthenticated = true
client.isRegistered = respParsed.Path("data.deviceRegistered").Data().(bool)
} else {
error, _ := respParsed.Path("errorCode").Data().(string)
switch {
case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
logger.Debug("Invalid account")
case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
logger.Debug("Client authentication failed")
case error == apiErrors["ERROR_PASSWORD_WARNING"]:
logger.Debug("Multiple Password Failures.")
default:
logger.Debug("Uknown error")
}
}
if !client.isRegistered {
client.registerDevice()
}
// TODO
fmt.Printf("Parcing cars: %d\n", len(respParsed.Path("data.vehicles").Children()))
for _, vehicle := range respParsed.Path("data.vehicles").Children() {
fmt.Printf("CAR: %s\n", vehicle.Path("vin").Data().(string))
client.listOfVins = append(client.listOfVins, vehicle.Path("vin").Data().(string))
}
client.currentVin = respParsed.Path("data.vehicles.0.vin").Data().(string)
return client, nil
}
// GetVehicles .
func (c *Client) GetVehicles() []*Vehicle {
var vehicles []*Vehicle
@ -551,7 +323,7 @@ func (c *Client) GetVehicles() []*Vehicle {
respParsed, err := gabs.ParseJSON([]byte(resp))
if err != nil {
panic(err)
c.log.Error("error which parsing json", "request", "GetVehicles", "error", err.Error())
}
vData := VehicleData{}
@ -603,7 +375,7 @@ func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
respParsed, err := gabs.ParseJSON([]byte(resp))
if err != nil {
panic(err)
c.log.Error("error which parsing json", "request", "GetVehicleByVIN", "error", err.Error())
}
vData := VehicleData{}
@ -682,7 +454,7 @@ func (c *Client) GetVehicleStatus() {
respParsed, err := gabs.ParseJSON(resp)
if err != nil {
panic(err)
c.log.Error("error which parsing json", "request", "GetVehicleStatus", "error", err.Error())
}
c.log.Debug("GET VEHICLE STATUS OUTPUT", "body", respParsed)
@ -718,7 +490,7 @@ func (c *Client) GetClimateSettings() {
respParsed, err := gabs.ParseJSON(resp)
if err != nil {
panic(err)
c.log.Error("error which parsing json", "request", "GetClimateSettings", "error", err.Error())
}
c.log.Debug("CLIMATE SETTINGS OUTPUT", "response", respParsed)
@ -755,12 +527,8 @@ func (c *Client) GetClimateSettings() {
func (c *Client) execute(requestUrl string, method string, params map[string]string, pollingUrl string, json bool) []byte {
defer timeTrack("[TIMETRK] Executing Get Request")
// if !isNil(resp.Cookies()) {
// log.Debugf("AUTH COOKIES OUTPUT >> %v\n", resp.Cookies())
// c.cookies = resp.Cookies()
// }
var resp *resty.Response
// GET Requests
if method == "GET" {
resp, _ = c.httpClient.
@ -791,9 +559,8 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
// }
respParsed, err := gabs.ParseJSON(resBytes)
if err != nil {
panic(err)
c.log.Error("error which parsing json", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
}
c.log.Debug("HTTP OUTPUT", "body", string(resBytes))
_, ok := respParsed.Path("success").Data().(bool)