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" "resty.dev/v3"
) )
// credentials .
type credentials struct {
username string
password string
pin string
deviceId string
deviceName string
}
// Client . // Client .
type Client struct { type Client struct {
baseURL string credentials config.Credentials
credentials credentials
httpClient *resty.Client httpClient *resty.Client
country string // USA | CA country string // USA | CA
updateInterval int // 7200 updateInterval int // 7200
@ -38,125 +28,14 @@ type Client struct {
sync.RWMutex 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 . // auth .
func (c *Client) auth() []byte { 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{ params := map[string]string{
"env": "cloudprod", "env": "cloudprod",
"deviceType": "android", "deviceType": "android",
"loginUsername": c.credentials.username, "loginUsername": c.credentials.Username,
"password": c.credentials.password, "password": c.credentials.Password,
"deviceId": c.credentials.deviceId, "deviceId": c.credentials.DeviceID,
"passwordToken": "", "passwordToken": "",
"selectedVin": "", "selectedVin": "",
"pushToken": ""} "pushToken": ""}
@ -195,9 +74,9 @@ func (c *Client) registerDevice() bool {
// }). // }).
// 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) c.execute(reqURL, POST, params, "", true)
@ -210,7 +89,7 @@ func (c *Client) registerDevice() bool {
// }). // }).
// 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)
@ -220,8 +99,8 @@ func (c *Client) registerDevice() bool {
// 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)
@ -315,111 +194,76 @@ func (c *Client) validateSession() bool {
// if await self._select_vehicle(vin): // if await self._select_vehicle(vin):
// result = True // result = True
c.log.Debug("[DEBUG] Validate Session", "body", string([]byte(resp))) c.log.Debug("session validation", "body", string([]byte(resp)))
return true return true
} }
func (c *Client) SelectVehicle(vin string) VehicleData { // New function creates a New MySubaru client
// { func New(logger *slog.Logger, config *config.MySubaru) (*Client, error) {
// "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"
// }
// }
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 == "" { if vin == "" {
vin = c.currentVin vin = c.currentVin
} }
@ -467,78 +311,6 @@ func (c *Client) SelectVehicle(vin string) VehicleData {
return vData 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 . // GetVehicles .
func (c *Client) GetVehicles() []*Vehicle { func (c *Client) GetVehicles() []*Vehicle {
var vehicles []*Vehicle var vehicles []*Vehicle
@ -551,7 +323,7 @@ func (c *Client) GetVehicles() []*Vehicle {
respParsed, err := gabs.ParseJSON([]byte(resp)) respParsed, err := gabs.ParseJSON([]byte(resp))
if err != nil { if err != nil {
panic(err) c.log.Error("error which parsing json", "request", "GetVehicles", "error", err.Error())
} }
vData := VehicleData{} vData := VehicleData{}
@ -603,7 +375,7 @@ func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
respParsed, err := gabs.ParseJSON([]byte(resp)) respParsed, err := gabs.ParseJSON([]byte(resp))
if err != nil { if err != nil {
panic(err) c.log.Error("error which parsing json", "request", "GetVehicleByVIN", "error", err.Error())
} }
vData := VehicleData{} vData := VehicleData{}
@ -682,7 +454,7 @@ func (c *Client) GetVehicleStatus() {
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { 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) c.log.Debug("GET VEHICLE STATUS OUTPUT", "body", respParsed)
@ -718,7 +490,7 @@ func (c *Client) GetClimateSettings() {
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { 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) 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 { func (c *Client) execute(requestUrl string, method string, params map[string]string, pollingUrl string, json bool) []byte {
defer timeTrack("[TIMETRK] Executing Get Request") 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 var resp *resty.Response
// GET Requests // GET Requests
if method == "GET" { if method == "GET" {
resp, _ = c.httpClient. resp, _ = c.httpClient.
@ -791,9 +559,8 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
// } // }
respParsed, err := gabs.ParseJSON(resBytes) respParsed, err := gabs.ParseJSON(resBytes)
if err != nil { 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)) c.log.Debug("HTTP OUTPUT", "body", string(resBytes))
_, ok := respParsed.Path("success").Data().(bool) _, ok := respParsed.Path("success").Data().(bool)

View File

@ -1,20 +1,33 @@
package config package config
import ( import (
"encoding/json"
"log/slog" "log/slog"
"os" "os"
"github.com/spf13/viper" "gopkg.in/yaml.v3"
)
const (
LoggingOutputJson = "JSON"
LoggingOutputText = "TEXT"
) )
// Config . // Config .
type Config struct { type Config struct {
MySubaru MySubaru
TimeZone string
Logger *slog.Logger
}
// config defines the structure of configuration data to be parsed from a config source.
type config struct {
MySubaru MySubaru `json:"mysubaru" yaml:"mysubaru"` MySubaru MySubaru `json:"mysubaru" yaml:"mysubaru"`
Timezone string `json:"timezone" yaml:"timezone"` TimeZone string `json:"timezone" yaml:"timezone"`
Logging *Logging `json:"logging" yaml:"logging"` Logging *Logging `json:"logging" yaml:"logging"`
} }
// Emporia . // MySubaru .
type MySubaru struct { type MySubaru struct {
Credentials Credentials `json:"credentials" yaml:"credentials"` Credentials Credentials `json:"credentials" yaml:"credentials"`
Region string `json:"region" yaml:"region"` Region string `json:"region" yaml:"region"`
@ -37,36 +50,49 @@ type Logging struct {
Source bool `json:"source,omitempty" yaml:"source,omitempty"` Source bool `json:"source,omitempty" yaml:"source,omitempty"`
} }
func New() (*Config, error) { func (l Logging) ToLogger() *slog.Logger {
viper.SetConfigName("config") // name of config file (without extension) var level slog.Level
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name if err := level.UnmarshalText([]byte(l.Level)); err != nil {
viper.AddConfigPath(".") // optionally look for config in the working directory level = slog.LevelInfo
viper.AddConfigPath("/etc/mysubaru") // optionally look for config in the working directory
viper.AutomaticEnv()
viper.SetDefault("Timezone", "America/New_York")
viper.SetDefault("MySubaru.AutoReconnect", true)
viper.SetDefault("Logging.Level", "INFO")
viper.SetDefault("Logging.Output", "TEXT")
viper.SetDefault("Logging.Source", "false")
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
slog.Error("cannot open config file", "error", err.Error())
os.Exit(1)
} }
// viper.WatchConfig() var handler slog.Handler
// viper.OnConfigChange(func(e fsnotify.Event) { switch l.Output {
// log.Println("Config file changed:", e.Name) case LoggingOutputJson:
// }) handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: l.Source, Level: level})
case LoggingOutputText:
var config Config handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: l.Source, Level: level})
if err := viper.Unmarshal(&config); err != nil { default:
slog.Error("cannot unmarshal config file", "error", err.Error()) handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: l.Source, Level: level})
os.Exit(1)
} }
return &config, nil return slog.New(handler)
}
// FromBytes unmarshals a byte slice of JSON or YAML config data into a valid server options value.
func FromBytes(b []byte) (*Config, error) {
c := new(config)
o := Config{}
if len(b) == 0 {
return nil, nil
}
if b[0] == '{' {
err := json.Unmarshal(b, c)
if err != nil {
return nil, err
}
} else {
err := yaml.Unmarshal(b, c)
if err != nil {
return nil, err
}
}
o.MySubaru = c.MySubaru
o.TimeZone = c.TimeZone
o.Logger = c.Logging.ToLogger()
return &o, nil
} }

93
config/config_test.go Normal file
View File

@ -0,0 +1,93 @@
package config
import (
"log/slog"
"os"
"testing"
"github.com/stretchr/testify/require"
)
var (
yamlBytes = []byte(`
mysubaru:
credentials:
username: username@mysubaru.golang
password: "PASSWORD"
pin: "1234"
deviceid: AaBbCcDdEeFf0123456789
devicename: MySubaru Golang Client
region: USA
auto_reconnect: true
timezone: "America/New_York"
logging:
level: INFO
output: TEXT
source: false
`)
jsonBytes = []byte(`{
"mysubaru": {
"credentials": {
"username": "username@mysubaru.golang",
"password": "PASSWORD",
"pin": "1234",
"deviceid": "AaBbCcDdEeFf0123456789",
"devicename": "MySubaru Golang Client"
},
"region": "USA",
"auto_reconnect": true
},
"timezone": "America/New_York",
"logging": {
"level": "INFO",
"output": "TEXT",
"source": false
}
}
`)
parsedOptions = Config{
MySubaru: MySubaru{
Credentials: Credentials{
Username: "username@mysubaru.golang",
Password: "PASSWORD",
PIN: "1234",
DeviceID: "AaBbCcDdEeFf0123456789",
DeviceName: "MySubaru Golang Client",
},
Region: "USA",
AutoReconnect: true,
},
TimeZone: "America/New_York",
Logger: slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})),
}
)
func TestFromBytesEmptyL(t *testing.T) {
_, err := FromBytes([]byte{})
require.NoError(t, err)
}
func TestFromBytesYAML(t *testing.T) {
o, err := FromBytes(yamlBytes)
require.NoError(t, err)
require.Equal(t, parsedOptions, *o)
}
func TestFromBytesYAMLError(t *testing.T) {
_, err := FromBytes(append(yamlBytes, 'a'))
require.Error(t, err)
}
func TestFromBytesJSON(t *testing.T) {
o, err := FromBytes(jsonBytes)
require.NoError(t, err)
require.Equal(t, parsedOptions, *o)
}
func TestFromBytesJSONError(t *testing.T) {
_, err := FromBytes(append(jsonBytes, 'a'))
require.Error(t, err)
}

20
go.mod
View File

@ -4,23 +4,15 @@ go 1.24
require ( require (
github.com/Jeffail/gabs/v2 v2.7.0 github.com/Jeffail/gabs/v2 v2.7.0
github.com/spf13/viper v1.20.1 github.com/stretchr/testify v1.10.0
gopkg.in/yaml.v3 v3.0.1
resty.dev/v3 v3.0.0-beta.3 resty.dev/v3 v3.0.0-beta.3
) )
require ( require (
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.8.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.40.0 // indirect golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
golang.org/x/text v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@ -35,67 +35,6 @@ var parts = map[string]map[string][]string{
}, },
} }
// {
// "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.70018,
// "longitude": -74.40139,
// "speed": null,
// "heading": null,
// "timestamp": 1642538410000
// },
// "extDescrip": "Abyss Blue Pearl",
// "intDescrip": "Gray",
// "modelName": "Outback",
// "transCode": "CVT",
// "provisioned": true,
// "timeZone": "America/New_York"
// }
// }
// Vehicle . // Vehicle .
type Vehicle struct { type Vehicle struct {
CarId int64 CarId int64
@ -251,49 +190,14 @@ func (v *Vehicle) String() string {
} }
// Lock . // Lock .
// Send command to lock doors. // Sends a command to lock doors.
func (v *Vehicle) Lock() { func (v *Vehicle) Lock() {
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640454085449_20_@NGTP",
// "success": false,
// "cancelled": false,
// "remoteServiceType": "lock",
// "remoteServiceState": "started",
// "subState": null,
// "errorCode": null,
// "result": null,
// "updateTime": 1640454085000,
// "vin": "4S4BTGND8L3137058"
// }
// }
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640454085449_20_@NGTP",
// "success": true,
// "cancelled": false,
// "remoteServiceType": "lock",
// "remoteServiceState": "finished",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640454091000,
// "vin": "4S4BTGND8L3137058"
// }
// }
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin, "pin": v.client.credentials.PIN,
"forceKeyInCar": "false"} "forceKeyInCar": "false"}
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LOCK"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LOCK"], v.getAPIGen())
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
@ -306,47 +210,12 @@ func (v *Vehicle) Lock() {
// Unlock . // Unlock .
// Send command to unlock doors. // Send command to unlock doors.
func (v *Vehicle) Unlock() { func (v *Vehicle) Unlock() {
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640539133289_19_@NGTP",
// "success": false,
// "cancelled": false,
// "remoteServiceType": "unlock",
// "remoteServiceState": "started",
// "subState": null,
// "errorCode": null,
// "result": null,
// "updateTime": 1640539133000,
// "vin": "4S4BTGND8L3137058"
// }
// }
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640539133289_19_@NGTP",
// "success": true,
// "cancelled": false,
// "remoteServiceType": "unlock",
// "remoteServiceState": "finished",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640539140000,
// "vin": "4S4BTGND8L3137058"
// }
// }
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin, "pin": v.client.credentials.PIN,
"unlockDoorType": "ALL_DOORS_CMD"} // FRONT_LEFT_DOOR_CMD | ALL_DOORS_CMD "unlockDoorType": "ALL_DOORS_CMD"} // FRONT_LEFT_DOOR_CMD | ALL_DOORS_CMD
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_UNLOCK"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_UNLOCK"], v.getAPIGen())
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
@ -382,50 +251,15 @@ func (v *Vehicle) Unlock() {
// await asyncio.sleep(2) // await asyncio.sleep(2)
} }
// EngineOn . // EngineStart .
// Send command to start engine and set climate control. // Sends a command to start engine and set climate control.
func (v *Vehicle) EngineStart() { func (v *Vehicle) EngineStart() {
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640456287656_22_@NGTP",
// "success": false,
// "cancelled": false,
// "remoteServiceType": "engineStart",
// "remoteServiceState": "started",
// "subState": null,
// "errorCode": null,
// "result": null,
// "updateTime": 1640456287000,
// "vin": "4S4BTGND8L3137058"
// }
// }
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640456287656_22_@NGTP",
// "success": true,
// "cancelled": false,
// "remoteServiceType": "engineStart",
// "remoteServiceState": "finished",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640456302000,
// "vin": "4S4BTGND8L3137058"
// }
// }
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin, "pin": v.client.credentials.PIN,
"horn": "true", "horn": "true",
"climateSettings": "climateSettings", // climateSettings "climateSettings": "climateSettings", // climateSettings
"climateZoneFrontTemp": "65", // 60-86 "climateZoneFrontTemp": "65", // 60-86
@ -447,50 +281,15 @@ func (v *Vehicle) EngineStart() {
} }
} }
// EngineOff . // EngineStop .
// Send command to stop engine. // Sends a command to stop engine.
func (v *Vehicle) EngineStop() { func (v *Vehicle) EngineStop() {
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640456318773_23_@NGTP",
// "success": false,
// "cancelled": false,
// "remoteServiceType": "engineStop",
// "remoteServiceState": "started",
// "subState": null,
// "errorCode": null,
// "result": null,
// "updateTime": null,
// "vin": "4S4BTGND8L3137058"
// }
// }
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640456318773_23_@NGTP",
// "success": true,
// "cancelled": false,
// "remoteServiceType": "engineStop",
// "remoteServiceState": "finished",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640456321000,
// "vin": "4S4BTGND8L3137058"
// }
// }
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + apiURLs["API_G2_REMOTE_ENGINE_STOP"] reqURL := MOBILE_API_VERSION + apiURLs["API_G2_REMOTE_ENGINE_STOP"]
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
v.client.execute(reqURL, POST, params, pollingURL, true) v.client.execute(reqURL, POST, params, pollingURL, true)
@ -500,49 +299,14 @@ func (v *Vehicle) EngineStop() {
} }
// LightsStart . // LightsStart .
// Send command to flash lights. // Sends a command to flash lights.
func (v *Vehicle) LightsStart() { func (v *Vehicle) LightsStart() {
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640457256003_21_@NGTP",
// "success": false,
// "cancelled": false,
// "remoteServiceType": "lightsOnly",
// "remoteServiceState": "started",
// "subState": null,
// "errorCode": null,
// "result": null,
// "updateTime": 1640457256000,
// "vin": "4S4BTGND8L3137058"
// }
// }
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640457256003_21_@NGTP",
// "success": true,
// "cancelled": false,
// "remoteServiceType": "lightsOnly",
// "remoteServiceState": "finished",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640457262000,
// "vin": "4S4BTGND8L3137058"
// }
// }
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LIGHTS"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LIGHTS"], v.getAPIGen())
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
if v.getAPIGen() == FEATURE_G1_TELEMATICS { if v.getAPIGen() == FEATURE_G1_TELEMATICS {
@ -555,49 +319,14 @@ func (v *Vehicle) LightsStart() {
} }
// LightsStop . // LightsStop .
// Send command to stop flash lights. // Sends a command to stop flash lights.
func (v *Vehicle) LightsStop() { func (v *Vehicle) LightsStop() {
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640457280857_47_@NGTP",
// "success": false,
// "cancelled": false,
// "remoteServiceType": "lightsOnly",
// "remoteServiceState": "stopping",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640457262000,
// "vin": "4S4BTGND8L3137058"
// }
// }
// {
// "success": true,
// "errorCode": null,
// "dataName": "remoteServiceStatus",
// "data": {
// "serviceRequestId": "4S4BTGND8L3137058_1640457280857_47_@NGTP",
// "success": true,
// "cancelled": false,
// "remoteServiceType": "lightsOnly",
// "remoteServiceState": "finished",
// "subState": null,
// "errorCode": "null:null",
// "result": null,
// "updateTime": 1640457280000,
// "vin": "4S4BTGND8L3137058"
// }
// }
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LIGHTS_STOP"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LIGHTS_STOP"], v.getAPIGen())
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
if v.getAPIGen() == FEATURE_G1_TELEMATICS { if v.getAPIGen() == FEATURE_G1_TELEMATICS {
@ -617,7 +346,7 @@ func (v *Vehicle) HornStart() {
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_HORN_LIGHTS"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_HORN_LIGHTS"], v.getAPIGen())
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
if v.getAPIGen() == FEATURE_G1_TELEMATICS { if v.getAPIGen() == FEATURE_G1_TELEMATICS {
@ -637,7 +366,7 @@ func (v *Vehicle) HornStop() {
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_HORN_LIGHTS_STOP"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_HORN_LIGHTS_STOP"], v.getAPIGen())
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
if v.getAPIGen() == FEATURE_G1_TELEMATICS { if v.getAPIGen() == FEATURE_G1_TELEMATICS {
@ -691,7 +420,7 @@ func (v *Vehicle) ChargeOn() {
params := map[string]string{ params := map[string]string{
"delay": "0", "delay": "0",
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + apiURLs["API_EV_CHARGE_NOW"] reqURL := MOBILE_API_VERSION + apiURLs["API_EV_CHARGE_NOW"]
pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_REMOTE_SVC_STATUS"]
v.client.execute(reqURL, POST, params, pollingURL, true) v.client.execute(reqURL, POST, params, pollingURL, true)
@ -746,7 +475,7 @@ func (v *Vehicle) GetLocation(force bool) {
pollingURL := MOBILE_API_VERSION + apiURLs["API_G2_LOCATE_STATUS"] pollingURL := MOBILE_API_VERSION + apiURLs["API_G2_LOCATE_STATUS"]
params := map[string]string{ params := map[string]string{
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
if v.getAPIGen() == FEATURE_G1_TELEMATICS { if v.getAPIGen() == FEATURE_G1_TELEMATICS {
reqURL = MOBILE_API_VERSION + apiURLs["API_G1_LOCATE_UPDATE"] reqURL = MOBILE_API_VERSION + apiURLs["API_G1_LOCATE_UPDATE"]
pollingURL = MOBILE_API_VERSION + apiURLs["API_G1_LOCATE_STATUS"] pollingURL = MOBILE_API_VERSION + apiURLs["API_G1_LOCATE_STATUS"]
@ -779,7 +508,7 @@ func (v *Vehicle) GetLocation(force bool) {
v.selectVehicle() v.selectVehicle()
params := map[string]string{ params := map[string]string{
"vin": v.Vin, "vin": v.Vin,
"pin": v.client.credentials.pin} "pin": v.client.credentials.PIN}
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LOCATE"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_LOCATE"], v.getAPIGen())
v.client.execute(reqURL, GET, params, "", false) v.client.execute(reqURL, GET, params, "", false)
} }
@ -790,14 +519,14 @@ func (v *Vehicle) GetClimateQuickPresets() {
if v.getRemoteOptionsStatus() { if v.getRemoteOptionsStatus() {
// params := map[string]string{ // params := map[string]string{
// "vin": v.Vin, // "vin": v.Vin,
// "pin": v.Client.credentials.pin} // "pin": v.Client.credentials.PIN}
v.selectVehicle() v.selectVehicle()
reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_RES_QUICK_START_SETTINGS"] reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_RES_QUICK_START_SETTINGS"]
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false) resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { if err != nil {
panic(err) v.client.log.Error("error which parsing json", "request", "GetClimateQuickPresets", "error", err.Error())
} }
v.client.log.Debug("CLIMATE SETTINGS OUTPUT", "body", respParsed) v.client.log.Debug("CLIMATE SETTINGS OUTPUT", "body", respParsed)
@ -826,9 +555,10 @@ func (v *Vehicle) GetClimatePresets() {
v.selectVehicle() v.selectVehicle()
reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_RES_SUBARU_PRESETS"] reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_RES_SUBARU_PRESETS"]
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false) resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { if err != nil {
panic(err) v.client.log.Error("error which parsing json", "request", "GetClimatePresets", "error", err.Error())
} }
// ONLY FOR THAT REQUEST BECAUSE OF API SENDS BACK ESCAPED DATA IN DATA FIELD // ONLY FOR THAT REQUEST BECAUSE OF API SENDS BACK ESCAPED DATA IN DATA FIELD
@ -856,9 +586,10 @@ func (v *Vehicle) GetClimateUserPresets() {
v.selectVehicle() v.selectVehicle()
reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_RES_USER_PRESETS"] reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_RES_USER_PRESETS"]
resp := v.client.execute(reqURL, GET, map[string]string{}, "", false) resp := v.client.execute(reqURL, GET, map[string]string{}, "", false)
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { if err != nil {
panic(err) v.client.log.Error("error which parsing json", "request", "GetClimateUserPresets", "error", err.Error())
} }
v.client.log.Debug("CLIMATE USER SETTINGS OUTPUT", "body", respParsed) v.client.log.Debug("CLIMATE USER SETTINGS OUTPUT", "body", respParsed)
@ -904,7 +635,7 @@ func (v *Vehicle) GetVehicleStatus() {
if v.client.isResponseSuccessfull(resp) { if v.client.isResponseSuccessfull(resp) {
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { if err != nil {
v.client.log.Error("error which parsing json", "error", err.Error()) v.client.log.Error("error which parsing json", "request", "GetVehicleStatus", "error", err.Error())
} }
vSta := VehicleStatus{} vSta := VehicleStatus{}
@ -919,9 +650,7 @@ func (v *Vehicle) GetVehicleStatus() {
v.DistanceToEmpty.Kilometers = vSta.DistanceToEmptyFuelKilometers v.DistanceToEmpty.Kilometers = vSta.DistanceToEmptyFuelKilometers
v.DistanceToEmpty.Miles10s = vSta.DistanceToEmptyFuelMiles10s v.DistanceToEmpty.Miles10s = vSta.DistanceToEmptyFuelMiles10s
v.DistanceToEmpty.Kilometers10s = vSta.DistanceToEmptyFuelKilometers10s v.DistanceToEmpty.Kilometers10s = vSta.DistanceToEmptyFuelKilometers10s
if vSta.RemainingFuelPercent >= 0 && vSta.RemainingFuelPercent <= 100 {
v.DistanceToEmpty.Percentage = vSta.RemainingFuelPercent v.DistanceToEmpty.Percentage = vSta.RemainingFuelPercent
}
v.FuelConsumptionAvg.MPG = float64(vSta.AvgFuelConsumptionMpg) v.FuelConsumptionAvg.MPG = float64(vSta.AvgFuelConsumptionMpg)
v.FuelConsumptionAvg.LP100Km = float64(vSta.AvgFuelConsumptionLitersPer100Kilometers) v.FuelConsumptionAvg.LP100Km = float64(vSta.AvgFuelConsumptionLitersPer100Kilometers)
@ -1047,7 +776,7 @@ func (v *Vehicle) GetVehicleStatus() {
// } // }
} }
// GetVehicleStatus . // GetVehicleCondition .
func (v *Vehicle) GetVehicleCondition() { func (v *Vehicle) GetVehicleCondition() {
v.selectVehicle() v.selectVehicle()
reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_CONDITION"], v.getAPIGen()) reqURL := MOBILE_API_VERSION + urlToGen(apiURLs["API_CONDITION"], v.getAPIGen())
@ -1056,7 +785,7 @@ func (v *Vehicle) GetVehicleCondition() {
if v.client.isResponseSuccessfull(resp) { if v.client.isResponseSuccessfull(resp) {
respParsed, err := gabs.ParseJSON(resp) respParsed, err := gabs.ParseJSON(resp)
if err != nil { if err != nil {
v.client.log.Error("error which parsing json", "error", err.Error()) v.client.log.Error("error which parsing json", "request", "GetVehicleCondition", "error", err.Error())
} }
re := regexp.MustCompile(`[A-Z][^A-Z]*`) re := regexp.MustCompile(`[A-Z][^A-Z]*`)
@ -1166,7 +895,7 @@ func (v *Vehicle) GetVehicleHealth() {
if v.client.isResponseSuccessfull(resp) { if v.client.isResponseSuccessfull(resp) {
_, err := gabs.ParseJSON(resp) _, err := gabs.ParseJSON(resp)
if err != nil { if err != nil {
v.client.log.Error("error which parsing json", "error", err.Error()) v.client.log.Error("error which parsing json", "request", "GetVehicleHealth", "error", err.Error())
} }
// TODO: // TODO:
} }