Compare commits
194 Commits
455e885a2c
...
main
Author | SHA1 | Date | |
---|---|---|---|
143d100793 | |||
92d4266f8b | |||
d7944123dd | |||
0e5b9aae19 | |||
3809ed5883 | |||
32dfb8fb6e | |||
6604b8ccc3 | |||
20f7ab5aa2 | |||
d41a2a7618 | |||
cbca67a61d | |||
c3d0fa53f1 | |||
89b3d44d82 | |||
ebe98e685a | |||
ed6ee5aacc | |||
7f5b092c64 | |||
c018850e34 | |||
7ec4dc5f1a | |||
a455733f8b | |||
0b2ed38ca3 | |||
07e3005e9c | |||
e8a1d8f54e | |||
30bd0bde44 | |||
aec4b8435b | |||
1d8d175be0 | |||
ac19db1271 | |||
f850b55b52 | |||
699dc13118 | |||
3c927fd83b | |||
d8cf2c3fd7 | |||
152eb2c7b7 | |||
b61b5664b7 | |||
cff0624807 | |||
3f426d206d | |||
fffb194bf5 | |||
85ae2658a2 | |||
f06a46b3cc | |||
b2a76f83cc | |||
d616854f4e | |||
16af99d38e | |||
3afa650200 | |||
23e242be8a | |||
0ff98f1f1f | |||
0744d16401 | |||
21a928bf70 | |||
cb008f61e7 | |||
fd26de9b82 | |||
017d7de3a2 | |||
91ab2ddf00 | |||
af42a5971e | |||
a42ed54f38 | |||
c27e5adfe0 | |||
28f650dc7d | |||
57e0e7899d | |||
91c3c44b54 | |||
15a792ac9b | |||
849a544ffa | |||
af28d7b2ed | |||
25c551af1d | |||
9783b483c9 | |||
c353651287 | |||
934f1259d0 | |||
e25b6a67f3 | |||
b590e1593f | |||
1f5dee8052 | |||
1060248508 | |||
83e453cc7e | |||
f7c17b8bbf | |||
41b7e0184c | |||
e3dc297ad1 | |||
524ff1207b | |||
24bb305f65 | |||
1419634fdb | |||
68eb2a5911 | |||
ac8efafe29 | |||
b0d874ec94 | |||
b30adcf93b | |||
2cf1dfff1b | |||
c3820ce0af | |||
c2128f278e | |||
5aecbdd9ca | |||
b0385b15ab | |||
f4fa996d53 | |||
4e75390ac6 | |||
f959895824 | |||
ded46bb720 | |||
38d884e450 | |||
265fe89b03 | |||
2f86d769fc | |||
cec922f363 | |||
8cb2ae8835 | |||
472bcd0723 | |||
096d737b85 | |||
b477017411 | |||
ef145bdf9a | |||
df5ef6ed0d | |||
2542993b9f | |||
d66d529fa9 | |||
d3c2badfcb | |||
d07cfeb20c | |||
fcd7c00d7a | |||
447e66f2e8 | |||
6ac2a3c78b | |||
0a6e69159d | |||
325ecb8cde | |||
567e9a8650 | |||
16135d21f4 | |||
e94b769d61 | |||
d85017d432 | |||
0f7236106c | |||
28eedd8ead | |||
d6937621e6 | |||
fb8f151e05 | |||
cfcca3f61b | |||
2d9c6762fa | |||
94e8dd70c8 | |||
f62fc1f0f7 | |||
b2ae1e5c76 | |||
070f3704f0 | |||
5381f90e6b | |||
6e70d26347 | |||
4f08d3c820 | |||
bb16ae1487 | |||
fe1e3e75cb | |||
e295a17587 | |||
2dbab08d08 | |||
f2f1f8380e | |||
3b7c350826 | |||
193a60d1f1 | |||
421b80523d | |||
a83379c858 | |||
9fb7a6805b | |||
668260f3f5 | |||
e506b590b3 | |||
68866189fb | |||
2ab1070bce | |||
c94c97c561 | |||
9d16051385 | |||
3734003b70 | |||
54005c36e5 | |||
f3093d3002 | |||
500d2368a3 | |||
55e09bf2e7 | |||
0476228b96 | |||
177955b7e0 | |||
df26dc4a7a | |||
48b7379770 | |||
d6ad39b126 | |||
9d9e87be08 | |||
d2baab529c | |||
2f3840e01b | |||
d642b957d8 | |||
ed2495ee5c | |||
3d808eb3b9 | |||
cc3edf7e22 | |||
4b7af175ce | |||
59ee34b899 | |||
55f1960faa | |||
4e115dda7f | |||
913e129927 | |||
c3b7baa087 | |||
c371a14fc2 | |||
b51092b1a7 | |||
bea9e214b0 | |||
ebff67ae18 | |||
439d4dc15b | |||
5fe6cc55f5 | |||
9ec0ec5309 | |||
fa6104f140 | |||
c33afa67ef | |||
84501efa56 | |||
d2f8cfb4a6 | |||
300d90c00b | |||
f820b5f835 | |||
0fb4996334 | |||
ea31ffb651 | |||
c00d0d4abf | |||
19134aa756 | |||
67b9d218a7 | |||
a803671a3c | |||
b851468de2 | |||
988c7a1d20 | |||
a8e9fd3a3c | |||
dd2fd1532b | |||
f241375645 | |||
48f799567e | |||
0a0159dc20 | |||
6f058b176a | |||
4c13756bf5 | |||
7b1c9506eb | |||
136ccaed20 | |||
c6b96c6d24 | |||
5550d2bead | |||
0aec270bdb | |||
808034f099 |
48
.github/workflows/CI.yml
vendored
Normal file
48
.github/workflows/CI.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: Golan Testing
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- 'README.md'
|
||||
|
||||
jobs:
|
||||
testing:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version:
|
||||
- 1.24.x
|
||||
os:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Golang environment
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Go Get / Go Test
|
||||
run: |
|
||||
go get -u .
|
||||
go get -u github.com/stretchr/testify/require
|
||||
go test ./...
|
||||
|
||||
- name: Output Summary
|
||||
id: output-summary
|
||||
shell: bash
|
||||
run: |
|
||||
echo "CI pipeline has been compiled for ${{ github.repository }} with a new version ${{ steps.vars.outputs.COMMIT_SHORT_SHA }}" >> $GITHUB_STEP_SUMMARY
|
45
README.md
45
README.md
@ -20,16 +20,47 @@ The following samples will assist you to become as comfortable as possible with
|
||||
import "git.savin.nyc/alex/mysubaru"
|
||||
```
|
||||
|
||||
#### Create a new MySubaru connection and get a car by VIN
|
||||
#### Create a new MySubaru API Client
|
||||
```go
|
||||
// Create a MySubaru Client
|
||||
mysubaru, _ := New()
|
||||
outback := mysubaru.GetVehicleByVIN("VIN-CODE-HERE")
|
||||
msc, err := mysubaru.New(cfg)
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot create MySubaru client", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
#### Get a car by VIN
|
||||
```go
|
||||
outback, err := msc.GetVehicleByVIN("1HGCM82633A004352")
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot get a vehicle by VIN", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
#### Start/Stop Lights request
|
||||
```go
|
||||
outback.LightsStart()
|
||||
time.Sleep(30 * time.Second)
|
||||
outback.LightsStop()
|
||||
```
|
||||
// Execute a LightsStart command
|
||||
events, err := outback.LightsStart()
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot execute LightsStart command", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for event := range events {
|
||||
fmt.Printf("Lights Start Event: %+v\n", event)
|
||||
}
|
||||
|
||||
// Wait for a while to see the lights on
|
||||
time.Sleep(20 * time.Second)
|
||||
|
||||
// Execute a LightsStop command
|
||||
events, err = outback.LightsStop()
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot execute LightsStop command", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for event := range events {
|
||||
fmt.Printf("Lights Stop Event: %+v\n", event)
|
||||
}
|
||||
```
|
||||
|
372
client_test.go
Normal file
372
client_test.go
Normal file
File diff suppressed because one or more lines are too long
@ -1,20 +1,33 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
LoggingOutputJson = "JSON"
|
||||
LoggingOutputText = "TEXT"
|
||||
)
|
||||
|
||||
// Config .
|
||||
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"`
|
||||
Timezone string `json:"timezone" yaml:"timezone"`
|
||||
TimeZone string `json:"timezone" yaml:"timezone"`
|
||||
Logging *Logging `json:"logging" yaml:"logging"`
|
||||
}
|
||||
|
||||
// Emporia .
|
||||
// MySubaru .
|
||||
type MySubaru struct {
|
||||
Credentials Credentials `json:"credentials" yaml:"credentials"`
|
||||
Region string `json:"region" yaml:"region"`
|
||||
@ -37,36 +50,49 @@ type Logging struct {
|
||||
Source bool `json:"source,omitempty" yaml:"source,omitempty"`
|
||||
}
|
||||
|
||||
func New() (*Config, error) {
|
||||
viper.SetConfigName("config") // name of config file (without extension)
|
||||
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
|
||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||
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)
|
||||
func (l Logging) ToLogger() *slog.Logger {
|
||||
var level slog.Level
|
||||
if err := level.UnmarshalText([]byte(l.Level)); err != nil {
|
||||
level = slog.LevelInfo
|
||||
}
|
||||
|
||||
// viper.WatchConfig()
|
||||
// viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
// log.Println("Config file changed:", e.Name)
|
||||
// })
|
||||
|
||||
var config Config
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
slog.Error("cannot unmarshal config file", "error", err.Error())
|
||||
os.Exit(1)
|
||||
var handler slog.Handler
|
||||
switch l.Output {
|
||||
case LoggingOutputJson:
|
||||
handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: l.Source, Level: level})
|
||||
case LoggingOutputText:
|
||||
handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: l.Source, Level: level})
|
||||
default:
|
||||
handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: l.Source, Level: level})
|
||||
}
|
||||
|
||||
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
93
config/config_test.go
Normal 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)
|
||||
}
|
310
consts.go
310
consts.go
@ -1,11 +1,11 @@
|
||||
package mysubaru
|
||||
|
||||
// Lastest version /g2v27
|
||||
var MOBILE_API_VERSION = "/g2v30"
|
||||
var MOBILE_API_VERSION = "/g2v31"
|
||||
|
||||
var MOBILE_API_SERVER = map[string]string{
|
||||
"USA": "https://mobileapi.prod.subarucs.com",
|
||||
"CAN": "https://mobileapi.ca.prod.subarucs.com",
|
||||
"USA": "https://mobileapi.prod.subarucs.com",
|
||||
"CAN": "https://mobileapi.ca.prod.subarucs.com",
|
||||
"TEST": "http://127.0.0.1:56765",
|
||||
}
|
||||
|
||||
var MOBILE_APP = map[string]string{
|
||||
@ -19,104 +19,130 @@ var WEB_API_SERVER = map[string]string{
|
||||
}
|
||||
|
||||
var apiURLs = map[string]string{
|
||||
"WEB_API_LOGIN": "/login",
|
||||
"WEB_API_LIST_DEVICES": "/listMyDevices.json", // TODO
|
||||
"WEB_API_AUTHORIZE_DEVICE": "/profile/updateDeviceEntry.json",
|
||||
"WEB_API_NAME_DEVICE": "/profile/addDeviceName.json",
|
||||
"WEB_API_EDIT_NAME_DEVICE": "/profile/editDeviceName.json",
|
||||
"WEB_API_VERIFY_NAME_DEVICE": "/profile/verifyDeviceName.json",
|
||||
"API_LOGIN": "/login.json", // Same API for g1 and g2
|
||||
"API_REFRESH_VEHICLES": "/refreshVehicles.json",
|
||||
"API_SELECT_VEHICLE": "/selectVehicle.json",
|
||||
"API_VALIDATE_SESSION": "/validateSession.json",
|
||||
"API_VEHICLE_STATUS": "/vehicleStatus.json",
|
||||
"API_AUTHORIZE_DEVICE": "/authenticateDevice.json",
|
||||
"API_NAME_DEVICE": "/nameThisDevice.json",
|
||||
"API_VEHICLE_HEALTH": "/vehicleHealth.json",
|
||||
"API_LOCK": "/service/api_gen/lock/execute.json", // Similar API for g1 and g2 -- controller should replace 'api_gen' with either 'g1' or 'g2'
|
||||
"API_LOCK_CANCEL": "/service/api_gen/lock/cancel.json",
|
||||
"API_UNLOCK": "/service/api_gen/unlock/execute.json",
|
||||
"API_UNLOCK_CANCEL": "/service/api_gen/unlock/cancel.json",
|
||||
"API_HORN_LIGHTS": "/service/api_gen/hornLights/execute.json",
|
||||
"API_HORN_LIGHTS_CANCEL": "/service/api_gen/hornLights/cancel.json",
|
||||
"API_HORN_LIGHTS_STOP": "/service/api_gen/hornLights/stop.json",
|
||||
"API_LIGHTS": "/service/api_gen/lightsOnly/execute.json",
|
||||
"API_LIGHTS_CANCEL": "/service/api_gen/lightsOnly/cancel.json",
|
||||
"API_LIGHTS_STOP": "/service/api_gen/lightsOnly/stop.json",
|
||||
"API_CONDITION": "/service/api_gen/condition/execute.json",
|
||||
"API_LOCATE": "/service/api_gen/locate/execute.json", // Get the last location the vehicle has reported to Subaru
|
||||
"API_REMOTE_SVC_STATUS": "/service/g2/remoteService/status.json",
|
||||
"API_G1_LOCATE_UPDATE": "/service/g1/vehicleLocate/execute.json", // Different API for g1 and g2
|
||||
"API_G1_LOCATE_STATUS": "/service/g1/vehicleLocate/status.json",
|
||||
"API_G1_HORN_LIGHTS_STATUS": "/service/g1/hornLights/status.json", // g1-Only API
|
||||
"API_G2_LOCATE_UPDATE": "/service/g2/vehicleStatus/execute.json",
|
||||
"API_G2_LOCATE_STATUS": "/service/g2/vehicleStatus/locationStatus.json",
|
||||
"API_G2_SEND_POI": "/service/g2/sendPoi/execute.json", // g2-Only API
|
||||
"API_G2_SPEEDFENCE": "/service/g2/speedFence/execute.json",
|
||||
"API_G2_GEOFENCE": "/service/g2/geoFence/execute.json",
|
||||
"API_G2_CURFEW": "/service/g2/curfew/execute.json",
|
||||
"API_G2_REMOTE_ENGINE_START": "/service/g2/engineStart/execute.json",
|
||||
"API_G2_REMOTE_ENGINE_START_CANCEL": "/service/g2/engineStart/cancel.json",
|
||||
"API_G2_REMOTE_ENGINE_STOP": "/service/g2/engineStop/execute.json",
|
||||
|
||||
"API_G2_FETCH_CLIMATE_SETTINGS": "/service/g2/remoteEngineStart/fetch.json",
|
||||
"API_G2_SAVE_CLIMATE_SETTINGS": "/service/g2/remoteEngineStart/save.json",
|
||||
|
||||
"WEB_API_LOGIN": "/login",
|
||||
"WEB_API_LIST_DEVICES": "/listMyDevices.json", // TODO
|
||||
"WEB_API_AUTHORIZE_DEVICE": "/profile/updateDeviceEntry.json",
|
||||
"WEB_API_NAME_DEVICE": "/profile/addDeviceName.json",
|
||||
"WEB_API_EDIT_NAME_DEVICE": "/profile/editDeviceName.json",
|
||||
"WEB_API_VERIFY_NAME_DEVICE": "/profile/verifyDeviceName.json",
|
||||
"API_2FA_CONTACT": "/twoStepAuthContacts.json",
|
||||
"API_2FA_SEND_VERIFICATION": "/twoStepAuthSendVerification.json",
|
||||
"API_2FA_AUTH_VERIFY": "/twoStepAuthVerify.json",
|
||||
"API_LOGIN": "/login.json", // Same API for g1 and g2
|
||||
"API_REFRESH_VEHICLES": "/refreshVehicles.json",
|
||||
"API_SELECT_VEHICLE": "/selectVehicle.json", // Covered by test
|
||||
"API_VALIDATE_SESSION": "/validateSession.json", // Covered by test
|
||||
"API_VEHICLE_STATUS": "/vehicleStatus.json", // Covered by test
|
||||
"API_AUTHORIZE_DEVICE": "/authenticateDevice.json",
|
||||
"API_NAME_DEVICE": "/nameThisDevice.json",
|
||||
"API_VEHICLE_HEALTH": "/vehicleHealth.json", // Covered by test
|
||||
"API_CONDITION": "/service/api_gen/condition/execute.json", // Similar API for g1 and g2 -- controller should replace 'api_gen' with either 'g1' or 'g2'
|
||||
"API_LOCATE": "/service/api_gen/locate/execute.json", // Get the last location the vehicle has reported to Subaru
|
||||
"API_LOCK": "/service/api_gen/lock/execute.json",
|
||||
"API_LOCK_CANCEL": "/service/api_gen/lock/cancel.json",
|
||||
"API_UNLOCK": "/service/api_gen/unlock/execute.json",
|
||||
"API_UNLOCK_CANCEL": "/service/api_gen/unlock/cancel.json",
|
||||
"API_HORN_LIGHTS": "/service/api_gen/hornLights/execute.json",
|
||||
"API_HORN_LIGHTS_CANCEL": "/service/api_gen/hornLights/cancel.json",
|
||||
"API_HORN_LIGHTS_STOP": "/service/api_gen/hornLights/stop.json",
|
||||
"API_LIGHTS": "/service/api_gen/lightsOnly/execute.json",
|
||||
"API_LIGHTS_CANCEL": "/service/api_gen/lightsOnly/cancel.json",
|
||||
"API_LIGHTS_STOP": "/service/api_gen/lightsOnly/stop.json",
|
||||
"API_G1_LOCATE_UPDATE": "/service/g1/vehicleLocate/execute.json", // Different API for g1 and g2
|
||||
"API_G1_LOCATE_STATUS": "/service/g1/vehicleLocate/status.json",
|
||||
"API_G1_HORN_LIGHTS_STATUS": "/service/g1/hornLights/status.json", // g1-Only API
|
||||
"API_G2_LOCATE_UPDATE": "/service/g2/vehicleStatus/execute.json",
|
||||
"API_G2_LOCATE_STATUS": "/service/g2/vehicleStatus/locationStatus.json",
|
||||
"API_REMOTE_SVC_STATUS": "/service/g2/remoteService/status.json",
|
||||
"API_G2_SEND_POI": "/service/g2/sendPoi/execute.json", // g2-Only API
|
||||
"API_G2_SPEEDFENCE": "/service/g2/speedFence/execute.json",
|
||||
"API_G2_GEOFENCE": "/service/g2/geoFence/execute.json",
|
||||
"API_G2_CURFEW": "/service/g2/curfew/execute.json",
|
||||
"API_G2_REMOTE_ENGINE_START": "/service/g2/engineStart/execute.json",
|
||||
"API_G2_REMOTE_ENGINE_START_CANCEL": "/service/g2/engineStart/cancel.json",
|
||||
"API_G2_REMOTE_ENGINE_STOP": "/service/g2/engineStop/execute.json",
|
||||
"API_G2_FETCH_RES_QUICK_START_SETTINGS": "/service/g2/remoteEngineQuickStartSettings/fetch.json",
|
||||
"API_G2_FETCH_RES_USER_PRESETS": "/service/g2/remoteEngineStartSettings/fetch.json",
|
||||
"API_G2_FETCH_RES_SUBARU_PRESETS": "/service/g2/climatePresetSettings/fetch.json",
|
||||
"API_G2_SAVE_RES_SETTINGS": "/service/g2/remoteEngineStartSettings/save.json",
|
||||
"API_G2_SAVE_RES_QUICK_START_SETTINGS": "/service/g2/remoteEngineQuickStartSettings/save.json",
|
||||
|
||||
"API_EV_CHARGE_NOW": "/service/g2/phevChargeNow/execute.json", // EV-Only API
|
||||
"API_EV_FETCH_CHARGE_SETTINGS": "/service/g2/phevGetTimerSettings/execute.json",
|
||||
"API_EV_SAVE_CHARGE_SETTINGS": "/service/g2/phevSendTimerSetting/execute.json",
|
||||
"API_EV_DELETE_CHARGE_SCHEDULE": "/service/g2/phevDeleteTimerSetting/execute.json",
|
||||
"API_EV_CHARGE_NOW": "/service/g2/phevChargeNow/execute.json", // EV-Only API
|
||||
"API_EV_FETCH_CHARGE_SETTINGS": "/service/g2/phevGetTimerSettings/execute.json",
|
||||
"API_EV_SAVE_CHARGE_SETTINGS": "/service/g2/phevSendTimerSetting/execute.json",
|
||||
"API_EV_DELETE_CHARGE_SCHEDULE": "/service/g2/phevDeleteTimerSetting/execute.json",
|
||||
}
|
||||
|
||||
// TODO: Get back and add wrapper to support Feature List
|
||||
// API_VEHICLE_FEATURES items that determine available functionality
|
||||
// 11.6MMAN ABS_MIL ACCS AHBL_MIL ATF_MIL AWD_MIL BSD BSDRCT_MIL CEL_MIL EBD_MIL EOL_MIL EPAS_MIL EPB_MIL ESS_MIL EYESIGHT ISS_MIL NAV_TOMTOM OPL_MIL RAB_MIL RCC REARBRK RES RESCC RHSF RPOI RPOIA SRS_MIL TEL_MIL TIF_35 TIR_33 TPMS_MIL VDC_MIL WASH_MIL g2
|
||||
// ABS_MIL ACCS AHBL_MIL ATF_MIL AWD_MIL BSD BSDRCT_MIL CEL_MIL EBD_MIL EOL_MIL EPAS_MIL EPB_MIL ESS_MIL EYESIGHT ISS_MIL OPL_MIL PANPM-TUIRWAOC PWAAADWWAP RAB_MIL RCC REARBRK RES RESCC RHSF RPOI RPOIA RTGU RVFS SRH_MIL SRS_MIL TEL_MIL TIF_35 TIR_33 TLD TPMS_MIL VALET VDC_MIL WASH_MIL g3
|
||||
var apiFeatures = map[string]string{
|
||||
"API_FEATURE_PHEV": "PHEV",
|
||||
"API_FEATURE_REMOTE_START": "RES",
|
||||
"API_FEATURE_REMOTE": "REMOTE",
|
||||
"API_FEATURE_SAFETY": "SAFETY",
|
||||
"API_FEATURE_ACTIVE": "ACTIVE",
|
||||
"API_FEATURE_MOONROOF_PANORAMIC": "PANPM-DG2G",
|
||||
"API_FEATURE_MOONROOF_POWER": "PANPM-TUIRWAOC",
|
||||
"API_FEATURE_POWER_WINDOWS": "PWAAADWWAP",
|
||||
"API_FEATURE_FRONT_TIRE_RECOMMENDED_PRESSURE_PREFIX": "TIF_",
|
||||
"API_FEATURE_REAR_TIRE_RECOMMENDED_PRESSURE_PREFIX": "TIR_",
|
||||
"API_FEATURE_G1_TELEMATICS": "g1",
|
||||
"API_FEATURE_G2_TELEMATICS": "g2",
|
||||
"API_FEATURE_G3_TELEMATICS": "g3",
|
||||
// var apiFeatures = map[string]string{
|
||||
// "API_FEATURE_PHEV": "PHEV",
|
||||
// "API_FEATURE_REMOTE_START": "RES",
|
||||
// "API_FEATURE_REMOTE": "REMOTE",
|
||||
// "API_FEATURE_SAFETY": "SAFETY",
|
||||
// "API_FEATURE_ACTIVE": "ACTIVE",
|
||||
// "API_FEATURE_MOONROOF_PANORAMIC": "PANPM-DG2G",
|
||||
// "API_FEATURE_MOONROOF_POWER": "PANPM-TUIRWAOC",
|
||||
// "API_FEATURE_POWER_WINDOWS": "PWAAADWWAP",
|
||||
// "API_FEATURE_FRONT_TIRE_RECOMMENDED_PRESSURE_PREFIX": "TIF_",
|
||||
// "API_FEATURE_REAR_TIRE_RECOMMENDED_PRESSURE_PREFIX": "TIR_",
|
||||
// "API_FEATURE_G1_TELEMATICS": "g1",
|
||||
// "API_FEATURE_G2_TELEMATICS": "g2",
|
||||
// "API_FEATURE_G3_TELEMATICS": "g3",
|
||||
// }
|
||||
|
||||
var API_ERRORS = map[string]string{
|
||||
"API_ERROR_SOA_403": "403-soa-unableToParseResponseBody", // G2
|
||||
"API_ERROR_NO_ACCOUNT": "accountNotFound", // G2
|
||||
"API_ERROR_INVALID_ACCOUNT": "invalidAccount", // G2
|
||||
"API_ERROR_INVALID_CREDENTIALS": "InvalidCredentials", // G2
|
||||
"API_ERROR_INVALID_TOKEN": "InvalidToken", // G2
|
||||
"API_ERROR_PASSWORD_WARNING": "passwordWarning", // G2
|
||||
"API_ERROR_TOO_MANY_ATTEMPTS": "tooManyAttempts", // G2
|
||||
"API_ERROR_ACCOUNT_LOCKED": "accountLocked", // G2
|
||||
"API_ERROR_NO_VEHICLES": "noVehiclesOnAccount", // G2
|
||||
"API_ERROR_VEHICLE_SETUP": "VEHICLESETUPERROR", // G2
|
||||
"API_ERROR_VEHICLE_NOT_IN_ACCOUNT": "vehicleNotInAccount", // G2
|
||||
"API_ERROR_SERVICE_ALREADY_STARTED": "ServiceAlreadyStarted", // G2
|
||||
"API_ERROR_G1_NO_SUBSCRIPTION": "SXM40004", // G1
|
||||
"API_ERROR_G1_STOLEN_VEHICLE": "SXM40005", // G1
|
||||
"API_ERROR_G1_INVALID_PIN": "SXM40006", // G1
|
||||
"API_ERROR_G1_SERVICE_ALREADY_STARTED": "SXM40009", // G1
|
||||
"API_ERROR_G1_PIN_LOCKED": "SXM40017", // G1
|
||||
}
|
||||
|
||||
var apiErrors = map[string]string{
|
||||
"ERROR_SOA_403": "403-soa-unableToParseResponseBody", // G2 Error Codes
|
||||
"ERROR_INVALID_CREDENTIALS": "InvalidCredentials",
|
||||
"ERROR_SERVICE_ALREADY_STARTED": "ServiceAlreadyStarted",
|
||||
"ERROR_INVALID_ACCOUNT": "invalidAccount",
|
||||
"ERROR_PASSWORD_WARNING": "passwordWarning",
|
||||
"ERROR_ACCOUNT_LOCKED": "accountLocked",
|
||||
"ERROR_NO_VEHICLES": "noVehiclesOnAccount",
|
||||
"ERROR_NO_ACCOUNT": "accountNotFound",
|
||||
"ERROR_TOO_MANY_ATTEMPTS": "tooManyAttempts",
|
||||
"ERROR_VEHICLE_NOT_IN_ACCOUNT": "vehicleNotInAccount",
|
||||
"ERROR_G1_NO_SUBSCRIPTION": "SXM40004", // G1 Error Codes
|
||||
"ERROR_G1_STOLEN_VEHICLE": "SXM40005",
|
||||
"ERROR_G1_INVALID_PIN": "SXM40006",
|
||||
"ERROR_G1_SERVICE_ALREADY_STARTED": "SXM40009",
|
||||
"ERROR_G1_PIN_LOCKED": "SXM40017",
|
||||
var APP_ERRORS = map[string]string{
|
||||
"SUBSCRIBTION_REQUIRED": "active STARLINK Security Plus subscription required",
|
||||
}
|
||||
|
||||
// TODO: Get back and add error wrapper
|
||||
// var apiErrors = map[string]string{
|
||||
// "ERROR_SOA_403": "403-soa-unableToParseResponseBody", // G2 Error Codes
|
||||
// "ERROR_INVALID_CREDENTIALS": "InvalidCredentials",
|
||||
// "ERROR_SERVICE_ALREADY_STARTED": "ServiceAlreadyStarted",
|
||||
// "ERROR_INVALID_ACCOUNT": "invalidAccount",
|
||||
// "ERROR_PASSWORD_WARNING": "passwordWarning",
|
||||
// "ERROR_ACCOUNT_LOCKED": "accountLocked",
|
||||
// "ERROR_NO_VEHICLES": "noVehiclesOnAccount",
|
||||
// "ERROR_NO_ACCOUNT": "accountNotFound",
|
||||
// "ERROR_TOO_MANY_ATTEMPTS": "tooManyAttempts",
|
||||
// "ERROR_VEHICLE_NOT_IN_ACCOUNT": "vehicleNotInAccount",
|
||||
// "ERROR_G1_NO_SUBSCRIPTION": "SXM40004", // G1 Error Codes
|
||||
// "ERROR_G1_STOLEN_VEHICLE": "SXM40005",
|
||||
// "ERROR_G1_INVALID_PIN": "SXM40006",
|
||||
// "ERROR_G1_SERVICE_ALREADY_STARTED": "SXM40009",
|
||||
// "ERROR_G1_PIN_LOCKED": "SXM40017",
|
||||
// }
|
||||
|
||||
// ABS_MIL ACCS AHBL_MIL ATF_MIL AWD_MIL BSD BSDRCT_MIL CEL_MIL EBD_MIL EOL_MIL EPAS_MIL EPB_MIL ESS_MIL EYESIGHT ISS_MIL OPL_MIL PANPM-TUIRWAOC PWAAADWWAP RAB_MIL RCC REARBRK RES RESCC RHSF RPOI RPOIA RTGU RVFS SRH_MIL SRS_MIL TEL_MIL TIF_35 TIR_33 TLD TPMS_MIL VALET VDC_MIL WASH_MIL g3
|
||||
// 11.6MMAN ABS_MIL ACCS AHBL_MIL ATF_MIL AWD_MIL BSD BSDRCT_MIL CEL_MIL EBD_MIL EOL_MIL EPAS_MIL EPB_MIL ESS_MIL EYESIGHT ISS_MIL NAV_TOMTOM OPL_MIL RAB_MIL RCC REARBRK RES RESCC RHSF RPOI RPOIA SRS_MIL TEL_MIL TIF_35 TIR_33 TPMS_MIL VDC_MIL WASH_MIL g2
|
||||
var features = map[string]string{
|
||||
"g1": "Generation #1",
|
||||
"g2": "Generation #2",
|
||||
"g3": "Generation #3",
|
||||
"BSD": "Blind-Spot Detection",
|
||||
"RHSF": "Rear High-Speed Function / Reverse Automatic Braking / Rear Cross-Traffic Alert",
|
||||
"11.6MMAN": "11.6-inch Infotainment System",
|
||||
"EYESIGHT": "EyeSight Exclusive Advanced Driver-Assist System",
|
||||
"NAV_TOMTOM": "TomTom Navigation",
|
||||
@ -124,49 +150,84 @@ var features = map[string]string{
|
||||
"PANPM-TUIRWAOC": "Power Moonroof",
|
||||
"PANPM-DG2G": "Panoramic Moonroof",
|
||||
"PHEV": "Electric Vehicle",
|
||||
"RES": "Remote Start",
|
||||
"RES": "Remote Engine Start",
|
||||
"REARBRK": "Reverse Auto Braking",
|
||||
"TIF_35": "Tire Pressure Front 35",
|
||||
"TIR_33": "Tire Pressure Rear 35",
|
||||
"VALET": "Valet Parking",
|
||||
"RES_HVAC_HFS": "Heated Front Seats",
|
||||
"RES_HVAC_VFS": "Vented Front Seats",
|
||||
"RESCC": "Remote Engine Start with Climate Control",
|
||||
"RCC": "Remote Climate Control",
|
||||
"ACCS": "Adaptive Cruise Control",
|
||||
"SXM360L": "SiriusXM with 360L",
|
||||
"WDWSTAT": "Window Status",
|
||||
"MOONSTAT": "Moonroof Status",
|
||||
"RTGU": "Remote Trunk / Rear Gate Unlock",
|
||||
"RVFS": "Remote Vehicle Find System",
|
||||
"TLD": "Tire Pressure Low Detection",
|
||||
"DOOR_LU_STAT": "Door Lock/Unlock Status",
|
||||
"RPOI": "Remote Geo Point of Interest",
|
||||
}
|
||||
|
||||
var errors = map[string]string{
|
||||
var troubles = map[string]string{
|
||||
"SRS_MIL": "Airbag System",
|
||||
"AWD_MIL": "All-Wheel Drive / Symmetrical Full-Time ",
|
||||
"ABS_MIL": "Anti-Lock Braking System",
|
||||
"ISS_MIL": "Auto Start Stop (Idling Stop System)",
|
||||
"AHBL_MIL": "Automatic Headlight Beam Leveler",
|
||||
"ATF_MIL": "Automatic Transmission Oil Temperature",
|
||||
"AWD_MIL": "Symmetrical Full-Time AWD",
|
||||
"EBD_MIL": "Brake System / Electronic Brake Force Distribution",
|
||||
"BSDRCT_MIL": "Blind-Spot Detection",
|
||||
"CEL_MIL": "Check Engine Light",
|
||||
"EBD_MIL": "Electronic Brake Force Distribution",
|
||||
"EOL_MIL": "Engine Oil Level",
|
||||
"EPAS_MIL": "Electric Power Assisted Steering",
|
||||
"EPB_MIL": "Parking Brake",
|
||||
"ESS_MIL": "EyeSight Exclusive Advanced Driver-Assist System",
|
||||
"ISS_MIL": "iss",
|
||||
"TEL_MIL": "MySubau Emergency Services",
|
||||
"EPB_MIL": "Parking Brake",
|
||||
"OPL_MIL": "Oil Pressure",
|
||||
"EPAS_MIL": "Power Steering / Electric Power Assisted Steering",
|
||||
"RAB_MIL": "Reverse Auto Braking",
|
||||
"SRH_MIL": "Steering Responsive Headlights",
|
||||
"SRS_MIL": "Airbag System",
|
||||
"TEL_MIL": "telematics",
|
||||
"TPMS_MIL": "tpms",
|
||||
"SRH_MIL": "Steering Responsive Headlights (SRH)",
|
||||
"TPMS_MIL": "Tire Pressure",
|
||||
"VDC_MIL": "Vehicle Dynamics Control",
|
||||
"WASH_MIL": "Windshield Washer Fluid Level",
|
||||
}
|
||||
var modelCodes = map[string]string{
|
||||
"PDL": "Subaru Outback Touring XT",
|
||||
"LDJ": "Subaru Outback Limited XT",
|
||||
"KDF": "Outback 2.5i Limited",
|
||||
"LDD": "Outback Premium",
|
||||
"PDG": "Outback Touring",
|
||||
"KFB": "Forester Base Model",
|
||||
"HFJ": "Forester 2.5i Touring",
|
||||
"KFJ": "Forester Touring",
|
||||
"KAF": "Legacy 2.5i Limited",
|
||||
"KLF": "Impreza 2.0i Sport",
|
||||
"LRD": "Crosstrek Premium",
|
||||
"KRD": "Crosstrek 2.0i Premium",
|
||||
"PCL": "Ascent Limited 7-Passenger",
|
||||
}
|
||||
|
||||
var badValues = []any{
|
||||
"NOT_EQUIPPED",
|
||||
"UNKNOWN",
|
||||
"None",
|
||||
"16383",
|
||||
"65535",
|
||||
"-64",
|
||||
"",
|
||||
0,
|
||||
float64(0),
|
||||
nil}
|
||||
|
||||
// var modelCodes = map[string]string{
|
||||
// "PDL": "Subaru Outback Touring XT",
|
||||
// "LDJ": "Subaru Outback Limited XT",
|
||||
// "KDF": "Outback 2.5i Limited",
|
||||
// "LDD": "Outback Premium",
|
||||
// "PDG": "Outback Touring",
|
||||
// "KFB": "Forester Base Model",
|
||||
// "HFJ": "Forester 2.5i Touring",
|
||||
// "KFJ": "Forester Touring",
|
||||
// "KAF": "Legacy 2.5i Limited",
|
||||
// "KLF": "Impreza 2.0i Sport",
|
||||
// "LRD": "Crosstrek Premium",
|
||||
// "KRD": "Crosstrek 2.0i Premium",
|
||||
// "PCL": "Ascent Limited 7-Passenger",
|
||||
// }
|
||||
|
||||
// var badValues = map[string]any{
|
||||
// "remainingFuelPercent": 101,
|
||||
// "tyreStatusFrontLeft": "UNKNOWN",
|
||||
// "tyreStatusFrontRight": "UNKNOWN",
|
||||
// "tyreStatusRearLeft": "UNKNOWN",
|
||||
// "tyreStatusRearRight": "UNKNOWN",
|
||||
// }
|
||||
|
||||
const (
|
||||
GET = "GET"
|
||||
@ -211,7 +272,7 @@ const (
|
||||
REAR_AC_ON = "true"
|
||||
REAR_AC_OFF = "false"
|
||||
START_CONFIG = "startConfiguration"
|
||||
START_CONFIG_DEFAULT_EV = "start_Climate_Control_only_allow_key_in_ignition"
|
||||
START_CONFIG_DEFAULT_EV = "START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION"
|
||||
START_CONFIG_DEFAULT_RES = "START_ENGINE_ALLOW_KEY_IN_IGNITION"
|
||||
WHICH_DOOR = "unlockDoorType" // Unlock doors constants
|
||||
ALL_DOORS = "ALL_DOORS_CMD"
|
||||
@ -302,7 +363,7 @@ const (
|
||||
ERROR_G1_INVALID_PIN = "SXM40006"
|
||||
ERROR_G1_SERVICE_ALREADY_STARTED = "SXM40009"
|
||||
ERROR_G1_PIN_LOCKED = "SXM40017"
|
||||
VEHICLE_ATTRIBUTES = "attributes" //Controller Vehicle Data Dict Keys
|
||||
VEHICLE_ATTRIBUTES = "attributes" // Controller Vehicle Data Dict Keys
|
||||
VEHICLE_STATUS = "status"
|
||||
VEHICLE_ID = "id"
|
||||
VEHICLE_NAME = "nickname"
|
||||
@ -355,3 +416,32 @@ const (
|
||||
// ]
|
||||
// BAD_BINARY_SENSOR_VALUES = [UNKNOWN, VENTED, NOT_EQUIPPED]
|
||||
)
|
||||
|
||||
// RAW_API_FIELDS_TO_REDACT = [
|
||||
// "cachedStateCode",
|
||||
// "customer",
|
||||
// "email",
|
||||
// "firstName",
|
||||
// "lastName",
|
||||
// "latitude",
|
||||
// "licensePlate",
|
||||
// "licensePlateState",
|
||||
// "longitude",
|
||||
// "nickname",
|
||||
// "odometer",
|
||||
// "odometerValue",
|
||||
// "odometerValueKilometers",
|
||||
// "oemCustId",
|
||||
// "phone",
|
||||
// "preferredDealer",
|
||||
// "sessionCustomer",
|
||||
// "timeZone",
|
||||
// "userOemCustId",
|
||||
// "vehicleGeoPosition",
|
||||
// "vehicleKey",
|
||||
// "vehicleMileage",
|
||||
// "vehicleName",
|
||||
// "vhsId",
|
||||
// "vin",
|
||||
// "zip",
|
||||
// ]
|
||||
|
@ -1,15 +1,14 @@
|
||||
credentials:
|
||||
username: user@email.com
|
||||
password: "Secr#TPassW0rd"
|
||||
pin: "PIN"
|
||||
deviceId: GENERATE-DEVICE-ID
|
||||
deviceName: Golang Integration
|
||||
mysubaru:
|
||||
credentials:
|
||||
username: user@email.com
|
||||
password: "Secr#TPassW0rd"
|
||||
pin: "PIN"
|
||||
deviceid: GENERATE-DEVICE-ID
|
||||
devicename: Golang Integration
|
||||
region: USA
|
||||
mqtt:
|
||||
client_id: mysubaru
|
||||
username: mysubaru
|
||||
password:
|
||||
host: mqtt.hostname.com
|
||||
port: 1883
|
||||
log: info
|
||||
auto_reconnect: true
|
||||
timezone: "America/New_York"
|
||||
logging:
|
||||
level: INFO
|
||||
output: JSON
|
||||
source: false
|
||||
|
@ -2,62 +2,34 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
mysubaru "git.savin.nyc/alex/mysubaru"
|
||||
"git.savin.nyc/alex/mysubaru"
|
||||
"git.savin.nyc/alex/mysubaru/config"
|
||||
)
|
||||
|
||||
// var log = *slog.Logger
|
||||
var cfg = &config.Config{}
|
||||
|
||||
const (
|
||||
LoggingOutputJson = "JSON"
|
||||
LoggingOutputText = "TEXT"
|
||||
)
|
||||
|
||||
func configureLogging(config *config.Logging) *slog.Logger { //nolint:unparam
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var level slog.Level
|
||||
if err := level.UnmarshalText([]byte(config.Level)); err != nil {
|
||||
slog.Warn(err.Error())
|
||||
slog.Warn(fmt.Sprintf("logging level not recognized, defaulting to level %s", slog.LevelInfo.String()))
|
||||
level = slog.LevelInfo
|
||||
}
|
||||
|
||||
var handler slog.Handler
|
||||
switch config.Output {
|
||||
case LoggingOutputJson:
|
||||
handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: cfg.Logging.Source, Level: level})
|
||||
case LoggingOutputText:
|
||||
handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: cfg.Logging.Source, Level: level})
|
||||
default:
|
||||
handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: cfg.Logging.Source, Level: level})
|
||||
}
|
||||
|
||||
return slog.New(handler)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var err error
|
||||
cfg, err = config.New()
|
||||
if err != nil { // Handle errors reading the config file
|
||||
slog.Error("Fatal error config file", "error", err.Error())
|
||||
|
||||
configFile, err := os.ReadFile("config.yaml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cfg, err := config.FromBytes(configFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// cfg.Logger.Debug("printing config", "config", cfg)
|
||||
|
||||
msc, err := mysubaru.New(cfg)
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot create MySubaru client", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logger := configureLogging(cfg.Logging)
|
||||
|
||||
logger.Debug("printting config", "config", cfg)
|
||||
|
||||
ms, _ := mysubaru.New(logger, &cfg.MySubaru)
|
||||
// mysubaru.selectVehicle("4S4BTGPD0P3199198")
|
||||
|
||||
// 11.6MMAN ABS_MIL ACCS AHBL_MIL ATF_MIL AWD_MIL BSD BSDRCT_MIL CEL_MIL EBD_MIL EOL_MIL EPAS_MIL EPB_MIL ESS_MIL EYESIGHT ISS_MIL NAV_TOMTOM OPL_MIL RAB_MIL RCC REARBRK RES RESCC RHSF RPOI RPOIA SRS_MIL TEL_MIL TIF_35 TIR_33 TPMS_MIL VDC_MIL WASH_MIL g2
|
||||
// subaru1 := mysubaru.GetVehicleByVIN("4S4BTGND8L3137058")
|
||||
// fmt.Printf("SUBARU #2 (Vehicle Status):\n")
|
||||
@ -68,62 +40,84 @@ func main() {
|
||||
// fmt.Printf("GEN #1: %+v\n\n", subaru1.getAPIGen())
|
||||
|
||||
// ABS_MIL ACCS AHBL_MIL ATF_MIL AWD_MIL BSD BSDRCT_MIL CEL_MIL EBD_MIL EOL_MIL EPAS_MIL EPB_MIL ESS_MIL EYESIGHT ISS_MIL OPL_MIL PANPM-TUIRWAOC PWAAADWWAP RAB_MIL RCC REARBRK RES RESCC RHSF RPOI RPOIA RTGU RVFS SRH_MIL SRS_MIL TEL_MIL TIF_35 TIR_33 TLD TPMS_MIL VALET VDC_MIL WASH_MIL g3
|
||||
subaru := ms.GetVehicleByVIN("4S4BTGPD0P3199198")
|
||||
subaru.GetLocation(true)
|
||||
// subaru.EngineStart()
|
||||
fmt.Printf("SUBARU #1 (Vehicle Status):\n")
|
||||
subaru.GetVehicleStatus()
|
||||
// fmt.Printf("SUBARU #1 (Vehicle Condition):\n")
|
||||
// subaru.GetVehicleCondition()
|
||||
// fmt.Printf("SUBARU #1: %+v\n", subaru)
|
||||
// subaru.GetClimatePresets()
|
||||
// subaru.GetClimateUserPresets()
|
||||
// fmt.Printf("SUBARU #2: %+v\n", subaru)
|
||||
// subaru.GetVehicleHealth()
|
||||
// subaru.GetFeaturesList()
|
||||
outback, err := msc.GetVehicleByVin("4S4BTGPD0P3199198")
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot get a vehicle by VIN", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// subaru.LightsStart()
|
||||
// time.Sleep(10 * time.Second)
|
||||
// subaru.LightsStop()
|
||||
fmt.Printf("SUBARU #1 (Vehicle Status): %s\n", outback)
|
||||
|
||||
// subaru := mysubaru.GetVehicles()[0]
|
||||
// // Execute a Lock command
|
||||
// events, err := outback.EngineStart(5, 0, true)
|
||||
// if err != nil {
|
||||
// cfg.Logger.Error("cannot execute Lock command", "error", err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// for event := range events {
|
||||
// fmt.Printf("Lock Event: %+v\n", event)
|
||||
// }
|
||||
|
||||
// fmt.Printf("SUBARU: %+v\n", subaru)
|
||||
// Execute a LightsStart command
|
||||
events, err := outback.LightsStart()
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot execute LightsStart command", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for event := range events {
|
||||
fmt.Printf("Lights Start Event: %+v\n", event)
|
||||
}
|
||||
|
||||
// fmt.Printf("Subaru Gen: %+v\n\n", subaru.getAPIGen())
|
||||
// Wait for a while to see the lights on
|
||||
time.Sleep(20 * time.Second)
|
||||
|
||||
// subaru.EngineOn()
|
||||
// subaru.GetLocation(false)
|
||||
// Execute a LightsStop command
|
||||
events, err = outback.LightsStop()
|
||||
if err != nil {
|
||||
cfg.Logger.Error("cannot execute LightsStop command", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for event := range events {
|
||||
fmt.Printf("Lights Stop Event: %+v\n", event)
|
||||
}
|
||||
|
||||
// subaru.GetVehicleStatus()
|
||||
// subaru.GetVehicleCondition()
|
||||
// fmt.Printf("SUBARU: %+v\n", subaru)
|
||||
// // Execute a Unlock command
|
||||
// events, err := outback.Unlock()
|
||||
// if err != nil {
|
||||
// cfg.Logger.Error("cannot execute Unlock command", "error", err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// for event := range events {
|
||||
// fmt.Printf("Unlock Event: %+v\n", event)
|
||||
// }
|
||||
|
||||
// subaru.GetClimateQuickPresets()
|
||||
// subaru.GetClimatePresets()
|
||||
// subaru.GetClimateUserPresets()
|
||||
|
||||
// subaru.LightsStart()
|
||||
// time.Sleep(15 * time.Second)
|
||||
// subaru.LightsStop()
|
||||
|
||||
// subaru.listDevices()
|
||||
|
||||
// subaru.GetVehicleStatus()
|
||||
|
||||
// subaru.GetClimateSettings()
|
||||
|
||||
// subaru.EngineStart()
|
||||
// time.Sleep(15 * time.Second)
|
||||
// subaru.EngineStop()
|
||||
|
||||
// subaru.Unlock()
|
||||
// // Wait for a while to see the lights on
|
||||
// time.Sleep(20 * time.Second)
|
||||
// subaru.Lock()
|
||||
|
||||
// subaru.LightsStart()
|
||||
// time.Sleep(15 * time.Second)
|
||||
// subaru.LightsStop()
|
||||
// // Execute a Lock command
|
||||
// events, err = outback.Lock()
|
||||
// if err != nil {
|
||||
// cfg.Logger.Error("cannot execute Lock command", "error", err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// for event := range events {
|
||||
// fmt.Printf("Lock Event: %+v\n", event)
|
||||
// }
|
||||
|
||||
// subaru.GetLocation()
|
||||
// Execute a forced GetLocation command
|
||||
// events, err = outback.GetLocation(true)
|
||||
// if err != nil {
|
||||
// cfg.Logger.Error("cannot execute forced GetLocation command", "error", err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// for event := range events {
|
||||
// fmt.Printf("GeoLocation Event: %+v\n", event)
|
||||
// }
|
||||
}
|
||||
|
||||
// {"time":"2025-07-21T18:05:30.987314-04:00","level":"DEBUG","source":{"function":"git.savin.nyc/alex/mysubaru.(*Client).execute","file":"/Users/alex/go/pkg/mod/git.savin.nyc/alex/mysubaru@v0.0.0-20250721200300-3809ed5883b4/client.go","line":372},"msg":"executed GET request","method":"GET","url":"/g2v31/service/g2/remoteEngineQuickStartSettings/fetch.json","params":{}}
|
||||
// {"time":"2025-07-21T18:05:31.014014-04:00","level":"DEBUG","source":{"function":"git.savin.nyc/alex/mysubaru.(*Client).execute","file":"/Users/alex/go/pkg/mod/git.savin.nyc/alex/mysubaru@v0.0.0-20250721200300-3809ed5883b4/client.go","line":404},"msg":"parsed http request output","data":"{\"success\":true,\"errorCode\":null,\"dataName\":null,\"data\":\"{\\\"name\\\":\\\"Cooling\\\",\\\"runTimeMinutes\\\":\\\"10\\\",\\\"climateZoneFrontTemp\\\":\\\"65\\\",\\\"climateZoneFrontAirMode\\\":\\\"FEET_FACE_BALANCED\\\",\\\"climateZoneFrontAirVolume\\\":\\\"7\\\",\\\"outerAirCirculation\\\":\\\"outsideAir\\\",\\\"heatedRearWindowActive\\\":\\\"false\\\",\\\"heatedSeatFrontLeft\\\":\\\"HIGH_COOL\\\",\\\"heatedSeatFrontRight\\\":\\\"HIGH_COOL\\\",\\\"airConditionOn\\\":\\\"false\\\",\\\"canEdit\\\":\\\"true\\\",\\\"disabled\\\":\\\"false\\\",\\\"presetType\\\":\\\"userPreset\\\",\\\"startConfiguration\\\":\\\"START_ENGINE_ALLOW_KEY_IN_IGNITION\\\"}\"}"}
|
||||
|
||||
// {"time":"2025-07-21T18:03:44.461646-04:00","level":"DEBUG","source":{"function":"git.savin.nyc/alex/mysubaru.(*Client).execute","file":"/Users/alex/go/pkg/mod/git.savin.nyc/alex/mysubaru@v0.0.0-20250721200300-3809ed5883b4/client.go","line":372},"msg":"executed GET request","method":"GET","url":"/g2v31/service/g2/remoteEngineQuickStartSettings/fetch.json","params":{}}
|
||||
// {"time":"2025-07-21T18:03:44.475745-04:00","level":"DEBUG","source":{"function":"git.savin.nyc/alex/mysubaru.(*Client).execute","file":"/Users/alex/go/pkg/mod/git.savin.nyc/alex/mysubaru@v0.0.0-20250721200300-3809ed5883b4/client.go","line":404},"msg":"parsed http request output","data":"{\"success\":true,\"errorCode\":null,\"dataName\":null,\"data\":\"{\\\"airConditionOn\\\":\\\"false\\\",\\\"climateSettings\\\":\\\"climateSettings\\\",\\\"climateZoneFrontAirMode\\\":\\\"FEET_WINDOW\\\",\\\"climateZoneFrontAirVolume\\\":\\\"7\\\",\\\"climateZoneFrontTemp\\\":\\\"65\\\",\\\"heatedRearWindowActive\\\":\\\"false\\\",\\\"heatedSeatFrontLeft\\\":\\\"HIGH_COOL\\\",\\\"heatedSeatFrontRight\\\":\\\"HIGH_COOL\\\",\\\"name\\\":\\\"Cooling\\\",\\\"outerAirCirculation\\\":\\\"outsideAir\\\",\\\"runTimeMinutes\\\":\\\"10\\\",\\\"startConfiguration\\\":\\\"START_ENGINE_ALLOW_KEY_IN_IGNITION\\\"}\\n\"}"}
|
||||
// {"time":"2025-07-21T18:03:44.475885-04:00","level":"ERROR","source":{"function":"git.savin.nyc/alex/mysubaru.(*Vehicle).GetClimateQuickPresets","file":"/Users/alex/go/pkg/mod/git.savin.nyc/alex/mysubaru@v0.0.0-20250721200300-3809ed5883b4/vehicle.go","line":498},"msg":"error while parsing climate quick presets json","request":"GetClimateQuickPresets","error":"invalid character '\"' after top-level value"}
|
||||
|
@ -2,24 +2,12 @@ module example
|
||||
|
||||
go 1.24
|
||||
|
||||
require git.savin.nyc/alex/mysubaru v0.0.0-20250520200227-71e00595ca48
|
||||
require git.savin.nyc/alex/mysubaru v0.0.0-20250722205404-92d4266f8b5a
|
||||
|
||||
require (
|
||||
github.com/Jeffail/gabs/v2 v2.7.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // 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/spf13/viper v1.20.1 // 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/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
resty.dev/v3 v3.0.0-beta.3 // indirect
|
||||
)
|
||||
|
37
go.mod
37
go.mod
@ -3,36 +3,15 @@ module git.savin.nyc/alex/mysubaru
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
github.com/Jeffail/gabs v1.4.0
|
||||
github.com/Jeffail/gabs/v2 v2.7.0
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
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
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.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/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
)
|
||||
|
634
mysubaru.go
634
mysubaru.go
@ -1,54 +1,174 @@
|
||||
package mysubaru
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Jeffail/gabs/v2"
|
||||
)
|
||||
|
||||
// Response .
|
||||
// Response represents the structure of a response from the MySubaru API.
|
||||
type Response struct {
|
||||
Success bool `json:"success"` // true | false
|
||||
ErrorCode string `json:"errorCode,omitempty"` // string | Error message if Success is false
|
||||
DataName string `json:"dataName"` // string | Describes the structure which is incleded in Data field
|
||||
Data interface{} `json:"data"` // Data struct
|
||||
Success bool `json:"success"` // true | false
|
||||
ErrorCode string `json:"errorCode,omitempty"` // string | Error message if Success is false
|
||||
DataName string `json:"dataName,omitempty"` // string | Describes the structure which is included in Data field
|
||||
Data json.RawMessage `json:"data"` // Data struct
|
||||
}
|
||||
|
||||
// Account .
|
||||
type Account struct {
|
||||
MarketID int `json:"marketId"`
|
||||
CreatedDate int64 `json:"createdDate"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
ZipCode string `json:"zipCode"`
|
||||
AccountKey int `json:"accountKey"`
|
||||
LastLoginDate time.Time `json:"lastLoginDate"`
|
||||
ZipCode5 string `json:"zipCode5"`
|
||||
// parse parses the JSON response from the MySubaru API into a Response struct.
|
||||
func (r *Response) parse(b []byte, logger *slog.Logger) (*Response, error) {
|
||||
err := json.Unmarshal(b, &r)
|
||||
if err != nil {
|
||||
logger.Error("error while parsing json", "error", err.Error())
|
||||
return nil, errors.New("error while parsing json: " + err.Error())
|
||||
}
|
||||
|
||||
if !r.Success && r.ErrorCode != "" {
|
||||
logger.Error("error in response", "errorCode", r.ErrorCode, "dataName", r.DataName)
|
||||
switch r.ErrorCode {
|
||||
// G2 API errors
|
||||
case API_ERRORS["API_ERROR_NO_ACCOUNT"]:
|
||||
return r, errors.New("error in response: Account not found")
|
||||
case API_ERRORS["API_ERROR_INVALID_ACCOUNT"]:
|
||||
return r, errors.New("error in response: Invalid Account")
|
||||
case API_ERRORS["API_ERROR_INVALID_CREDENTIALS"]:
|
||||
return r, errors.New("error in response: Invalid Credentials")
|
||||
case API_ERRORS["API_ERROR_INVALID_TOKEN"]:
|
||||
return r, errors.New("error in response: Invalid Token")
|
||||
case API_ERRORS["API_ERROR_PASSWORD_WARNING"]:
|
||||
return r, errors.New("error in response: Mutiple failed login attempts, password warning")
|
||||
case API_ERRORS["API_ERROR_TOO_MANY_ATTEMPTS"]:
|
||||
return r, errors.New("error in response: Too many attempts, please try again later")
|
||||
case API_ERRORS["API_ERROR_ACCOUNT_LOCKED"]:
|
||||
return r, errors.New("error in response: Account Locked")
|
||||
case API_ERRORS["API_ERROR_NO_VEHICLES"]:
|
||||
return r, errors.New("error in response: No vehicles found for the account")
|
||||
case API_ERRORS["API_ERROR_VEHICLE_SETUP"]:
|
||||
return r, errors.New("error in response: Vehicle setup is not complete")
|
||||
case API_ERRORS["API_ERROR_VEHICLE_NOT_IN_ACCOUNT"]:
|
||||
return r, errors.New("error in response: Vehicle not in account")
|
||||
case API_ERRORS["API_ERROR_SERVICE_ALREADY_STARTED"]:
|
||||
return r, errors.New("error in response: Service already started")
|
||||
case API_ERRORS["API_ERROR_SOA_403"]:
|
||||
return r, errors.New("error in response: Unable to parse response body, SOA 403 error")
|
||||
// G1 API errors
|
||||
case API_ERRORS["API_ERROR_G1_NO_SUBSCRIPTION"]:
|
||||
return r, errors.New("error in response: No subscription found for the vehicle")
|
||||
case API_ERRORS["API_ERROR_G1_STOLEN_VEHICLE"]:
|
||||
return r, errors.New("error in response: Car is reported as stolen")
|
||||
case API_ERRORS["API_ERROR_G1_INVALID_PIN"]:
|
||||
return r, errors.New("error in response: Invalid PIN")
|
||||
case API_ERRORS["API_ERROR_G1_PIN_LOCKED"]:
|
||||
return r, errors.New("error in response: PIN is locked")
|
||||
case API_ERRORS["API_ERROR_G1_SERVICE_ALREADY_STARTED"]:
|
||||
return r, errors.New("error in response: Service already started")
|
||||
}
|
||||
return r, errors.New("error in response: " + r.ErrorCode)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Request represents the structure of a request to the MySubaru API.
|
||||
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 .
|
||||
type account struct {
|
||||
MarketID int `json:"marketId"`
|
||||
AccountKey int `json:"accountKey"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
ZipCode string `json:"zipCode"`
|
||||
ZipCode5 string `json:"zipCode5"`
|
||||
LastLoginDate UnixTime `json:"lastLoginDate"`
|
||||
CreatedDate UnixTime `json:"createdDate"`
|
||||
}
|
||||
|
||||
// Customer .
|
||||
type Customer struct {
|
||||
SessionCustomer string `json:"sessionCustomer"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Zip string `json:"zip"`
|
||||
OemCustID string `json:"oemCustId"`
|
||||
Phone string `json:"phone"`
|
||||
SessionCustomer SessionCustomer `json:"sessionCustomer,omitempty"` // struct | Only by performing a RefreshVehicles request
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Zip string `json:"zip"`
|
||||
OemCustID string `json:"oemCustId"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
// SessionCustomer .
|
||||
type SessionCustomer struct {
|
||||
FirstName string `json:"firstName,omitempty"`
|
||||
LastName string `json:"lastName,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Suffix string `json:"suffix,omitempty"`
|
||||
Email string `json:"email"`
|
||||
Address string `json:"address"`
|
||||
Address2 string `json:"address2,omitempty"`
|
||||
City string `json:"city"`
|
||||
State string `json:"state"`
|
||||
Zip string `json:"zip"`
|
||||
CellularPhone string `json:"cellularPhone,omitempty"`
|
||||
WorkPhone string `json:"workPhone,omitempty"`
|
||||
HomePhone string `json:"homePhone,omitempty"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
RelationshipType any `json:"relationshipType,omitempty"`
|
||||
Gender string `json:"gender,omitempty"`
|
||||
DealerCode any `json:"dealerCode,omitempty"`
|
||||
OemCustID string `json:"oemCustId"`
|
||||
CreateMysAccount any `json:"createMysAccount,omitempty"`
|
||||
SourceSystemCode string `json:"sourceSystemCode"`
|
||||
Vehicles []struct {
|
||||
Vin string `json:"vin"`
|
||||
SiebelVehicleRelationship string `json:"siebelVehicleRelationship"` // TM Subscriber | Previous TM Subscriber | Previous Owner
|
||||
Primary bool `json:"primary"` // true | false
|
||||
OemCustID string `json:"oemCustId"` // CRM-41PLM-5TYE | 1-8K7OBOJ | 1-8JY3UVS | CRM-44UFUA14-V
|
||||
Status string `json:"status,omitempty"` // "Active" | "Draft" | "Inactive"
|
||||
} `json:"vehicles"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Zip5Digits string `json:"zip5Digits"`
|
||||
PrimaryPersonalCountry string `json:"primaryPersonalCountry"`
|
||||
}
|
||||
|
||||
// DataMap .
|
||||
// "dataName": "dataMap"
|
||||
type dataMap struct {
|
||||
Username string `json:"userName"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// SessionData .
|
||||
// "dataName": "sessionData"
|
||||
type SessionData struct {
|
||||
SessionChanged bool `json:"sessionChanged"`
|
||||
VehicleInactivated bool `json:"vehicleInactivated"`
|
||||
Account Account `json:"account"`
|
||||
ResetPassword bool `json:"resetPassword"`
|
||||
DeviceID string `json:"deviceId"`
|
||||
SessionID string `json:"sessionId"`
|
||||
DeviceRegistered bool `json:"deviceRegistered"`
|
||||
Account account `json:"account"`
|
||||
PasswordToken string `json:"passwordToken"`
|
||||
ResetPassword bool `json:"resetPassword"`
|
||||
SessionID string `json:"sessionId"`
|
||||
SessionChanged bool `json:"sessionChanged"`
|
||||
DeviceID string `json:"deviceId"`
|
||||
DeviceRegistered bool `json:"deviceRegistered"`
|
||||
RegisteredDevicePermanent bool `json:"registeredDevicePermanent"`
|
||||
Vehicles []VehicleData `json:"vehicles"`
|
||||
VehicleInactivated bool `json:"vehicleInactivated"`
|
||||
RightToRepairEnabled bool `json:"rightToRepairEnabled"`
|
||||
RightToRepairStates string `json:"rightToRepairStates"`
|
||||
CurrentVehicleIndex int `json:"currentVehicleIndex"`
|
||||
@ -61,63 +181,63 @@ type SessionData struct {
|
||||
DigitalGlobeTransparentTileService string `json:"digitalGlobeTransparentTileService"`
|
||||
TomtomKey string `json:"tomtomKey"`
|
||||
SatelliteViewEnabled bool `json:"satelliteViewEnabled"`
|
||||
RegisteredDevicePermanent bool `json:"registeredDevicePermanent"`
|
||||
}
|
||||
|
||||
// Vehicle .
|
||||
// "dataName": "vehicle"
|
||||
type VehicleData struct {
|
||||
Customer Customer `json:"customer"` // Customer struct
|
||||
UserOemCustID string `json:"userOemCustId"` // CRM-631-HQN48K
|
||||
OemCustID string `json:"oemCustId"` // CRM-631-HQN48K
|
||||
Active bool `json:"active"` // true | false
|
||||
Email string `json:"email"` // null | email@address.com
|
||||
FirstName string `json:"firstName"` // null | First Name
|
||||
LastName string `json:"lastName"` // null | Last Name
|
||||
Zip string `json:"zip"` // 12345
|
||||
Phone string `json:"phone"` // null | 123-456-7890
|
||||
StolenVehicle bool `json:"stolenVehicle"` // true | false
|
||||
VehicleName string `json:"vehicleName"` // Subaru Outback LXT
|
||||
Features []string `json:"features"` // "11.6MMAN", "ABS_MIL", "ACCS", "AHBL_MIL", "ATF_MIL", "AWD_MIL", "BSD", "BSDRCT_MIL", "CEL_MIL", "EBD_MIL", "EOL_MIL", "EPAS_MIL", "EPB_MIL", "ESS_MIL", "EYESIGHT", "ISS_MIL", "NAV_TOMTOM", "OPL_MIL", "RAB_MIL", "RCC", "REARBRK", "RES", "RESCC", "RHSF", "RPOI", "RPOIA", "SRH_MIL", "SRS_MIL", "TEL_MIL", "TPMS_MIL", "VDC_MIL", "WASH_MIL", "g2"
|
||||
Vin string `json:"vin"` // 4Y1SL65848Z411439
|
||||
VehicleKey int64 `json:"vehicleKey"` // 3832950
|
||||
Nickname string `json:"nickname"` // Subaru Outback LXT
|
||||
ModelName string `json:"modelName"` // Outback
|
||||
ModelYear string `json:"modelYear"` // 2020
|
||||
ModelCode string `json:"modelCode"` // LDJ
|
||||
ExtDescrip string `json:"extDescrip"` // Abyss Blue Pearl (ext color)
|
||||
IntDescrip string `json:"intDescrip"` // Gray (int color)
|
||||
TransCode string `json:"transCode"` // CVT
|
||||
EngineSize float64 `json:"engineSize"` // 2.4
|
||||
Phev bool `json:"phev"` // null
|
||||
CachedStateCode string `json:"cachedStateCode"` // NJ
|
||||
LicensePlate string `json:"licensePlate"` // NJ
|
||||
LicensePlateState string `json:"licensePlateState"` // ABCDEF
|
||||
SubscriptionStatus string `json:"subscriptionStatus"` // ACTIVE
|
||||
SubscriptionFeatures []string `json:"subscriptionFeatures"` // "REMOTE", "SAFETY", "Retail"
|
||||
SubscriptionPlans []string `json:"subscriptionPlans"` // []
|
||||
VehicleGeoPosition GeoPosition `json:"vehicleGeoPosition"` // GeoPosition struct
|
||||
AccessLevel int `json:"accessLevel"` // -1
|
||||
VehicleMileage int `json:"vehicleMileage"` // null
|
||||
CrmRightToRepair bool `json:"crmRightToRepair"` // true | false
|
||||
AuthorizedVehicle bool `json:"authorizedVehicle"` // false | true
|
||||
NeedMileagePrompt bool `json:"needMileagePrompt"` // false | true
|
||||
RemoteServicePinExist bool `json:"remoteServicePinExist"` // true | false
|
||||
NeedEmergencyContactPrompt bool `json:"needEmergencyContactPrompt"` // false | true
|
||||
Show3GSunsetBanner bool `json:"show3gSunsetBanner"` // false | true
|
||||
Provisioned bool `json:"provisioned"` // true | false
|
||||
TimeZone time.Location `json:"timeZone"` // America/New_York
|
||||
SunsetUpgraded bool `json:"sunsetUpgraded"` // true | false
|
||||
PreferredDealer string `json:"preferredDealer"` // null |
|
||||
Customer Customer `json:"customer"` // Customer struct
|
||||
OemCustID string `json:"oemCustId"` // CRM-631-HQN48K
|
||||
UserOemCustID string `json:"userOemCustId"` // CRM-631-HQN48K
|
||||
Active bool `json:"active"` // true | false
|
||||
Email string `json:"email"` // null | email@address.com
|
||||
FirstName string `json:"firstName,omitempty"` // null | First Name
|
||||
LastName string `json:"lastName,omitempty"` // null | Last Name
|
||||
Zip string `json:"zip"` // 12345
|
||||
Phone string `json:"phone,omitempty"` // null | 123-456-7890
|
||||
StolenVehicle bool `json:"stolenVehicle"` // true | false
|
||||
VehicleName string `json:"vehicleName"` // Subaru Outback LXT
|
||||
Features []string `json:"features"` // "11.6MMAN", "ABS_MIL", "ACCS", "AHBL_MIL", "ATF_MIL", "AWD_MIL", "BSD", "BSDRCT_MIL", "CEL_MIL", "EBD_MIL", "EOL_MIL", "EPAS_MIL", "EPB_MIL", "ESS_MIL", "EYESIGHT", "ISS_MIL", "NAV_TOMTOM", "OPL_MIL", "RAB_MIL", "RCC", "REARBRK", "RES", "RESCC", "RHSF", "RPOI", "RPOIA", "SRH_MIL", "SRS_MIL", "TEL_MIL", "TPMS_MIL", "VDC_MIL", "WASH_MIL", "g2"
|
||||
Vin string `json:"vin"` // 4Y1SL65848Z411439
|
||||
VehicleKey int64 `json:"vehicleKey"` // 3832950
|
||||
Nickname string `json:"nickname"` // Subaru Outback LXT
|
||||
ModelName string `json:"modelName"` // Outback
|
||||
ModelYear string `json:"modelYear"` // 2020
|
||||
ModelCode string `json:"modelCode"` // LDJ
|
||||
ExtDescrip string `json:"extDescrip"` // Abyss Blue Pearl (ext color)
|
||||
IntDescrip string `json:"intDescrip"` // Gray (int color)
|
||||
TransCode string `json:"transCode"` // CVT
|
||||
EngineSize float64 `json:"engineSize"` // 2.4
|
||||
Phev bool `json:"phev"` // null
|
||||
CachedStateCode string `json:"cachedStateCode"` // NJ
|
||||
LicensePlate string `json:"licensePlate"` // NJ
|
||||
LicensePlateState string `json:"licensePlateState"` // ABCDEF
|
||||
SubscriptionStatus string `json:"subscriptionStatus"` // ACTIVE
|
||||
SubscriptionFeatures []string `json:"subscriptionFeatures"` // "[ REMOTE ], [ SAFETY ], [ Retail | Finance3 | RetailPHEV ]""
|
||||
SubscriptionPlans []string `json:"subscriptionPlans,omitempty"` // []
|
||||
VehicleGeoPosition GeoPosition `json:"vehicleGeoPosition"` // GeoPosition struct
|
||||
AccessLevel int `json:"accessLevel"` // -1
|
||||
VehicleMileage int `json:"vehicleMileage,omitempty"` // null
|
||||
CrmRightToRepair bool `json:"crmRightToRepair"` // true | false
|
||||
AuthorizedVehicle bool `json:"authorizedVehicle"` // false | true
|
||||
NeedMileagePrompt bool `json:"needMileagePrompt"` // false | true
|
||||
RemoteServicePinExist bool `json:"remoteServicePinExist"` // true | false
|
||||
NeedEmergencyContactPrompt bool `json:"needEmergencyContactPrompt"` // false | true
|
||||
Show3GSunsetBanner bool `json:"show3gSunsetBanner"` // false | true
|
||||
Provisioned bool `json:"provisioned"` // true | false
|
||||
TimeZone string `json:"timeZone"` // America/New_York
|
||||
SunsetUpgraded bool `json:"sunsetUpgraded"` // true | false
|
||||
PreferredDealer string `json:"preferredDealer,omitempty"` // null |
|
||||
VehicleBranded bool `json:"vehicleBranded"`
|
||||
}
|
||||
|
||||
// GeoPosition .
|
||||
type GeoPosition struct {
|
||||
Latitude float64 `json:"latitude"` // 40.700184
|
||||
Longitude float64 `json:"longitude"` // -74.401375
|
||||
Speed float64 `json:"speed,omitempty"` // 62
|
||||
Heading int `json:"heading,omitempty"` // 155
|
||||
Timestamp string `json:"timestamp,string"` // "2021-12-22T13:14:47"
|
||||
Latitude float64 `json:"latitude"` // 40.700184
|
||||
Longitude float64 `json:"longitude"` // -74.401375
|
||||
Speed int `json:"speed,omitempty"` // 62
|
||||
Heading int `json:"heading,omitempty"` // 155
|
||||
Timestamp CustomTime1 `json:"timestamp"` // "2021-12-22T13:14:47"
|
||||
}
|
||||
|
||||
// type GeoPositionTime time.Time
|
||||
@ -134,143 +254,166 @@ type GeoPosition struct {
|
||||
// }
|
||||
|
||||
// VehicleStatus .
|
||||
// "dataName":null
|
||||
// parts = []{"door", "tire", "tyre", "window"}
|
||||
// prefix = []{"pressure", "status"}
|
||||
// suffix = []{"status", "position", "unit", "psi"}
|
||||
// positions = []{"front", "rear", "sunroof", "boot", "enginehood"}
|
||||
// subPositions []{"left", "right"}
|
||||
|
||||
type VehicleStatus struct {
|
||||
VehicleId int64 `json:"vhsId"` // + 9969776690 5198812434
|
||||
OdometerValue int `json:"odometerValue"` // + 23787
|
||||
OdometerValueKm int `json:"odometerValueKilometers"` // + 38273
|
||||
EventDate int64 `json:"eventDate"` // + 1701896993000
|
||||
EventDateStr string `json:"eventDateStr"` // + 2023-12-06T21:09+0000
|
||||
EventDateCarUser int64 `json:"eventDateCarUser"` // + 1701896993000
|
||||
EventDateStrCarUser string `json:"eventDateStrCarUser"` // + 2023-12-06T21:09+0000
|
||||
Latitude float64 `json:"latitude"` // + 40.700183
|
||||
Longitude float64 `json:"longitude"` // + -74.401372
|
||||
Heading string `json:"positionHeadingDegree"` // + "154"
|
||||
DistanceToEmptyFuelMiles float64 `json:"distanceToEmptyFuelMiles"` // + 209.4
|
||||
DistanceToEmptyFuelKilometers int `json:"distanceToEmptyFuelKilometers"` // + 337
|
||||
DistanceToEmptyFuelMiles10s int `json:"distanceToEmptyFuelMiles10s"` // + 210
|
||||
DistanceToEmptyFuelKilometers10s int `json:"distanceToEmptyFuelKilometers10s"` // + 340
|
||||
AvgFuelConsumptionMpg float64 `json:"avgFuelConsumptionMpg"` // + 18.4
|
||||
AvgFuelConsumptionLitersPer100Kilometers float64 `json:"avgFuelConsumptionLitersPer100Kilometers"` // + 12.8
|
||||
RemainingFuelPercent int `json:"remainingFuelPercent"` // + 82
|
||||
TirePressureFrontLeft string `json:"tirePressureFrontLeft"` // + "2275"
|
||||
TirePressureFrontRight string `json:"tirePressureFrontRight"` // + "2344"
|
||||
TirePressureRearLeft string `json:"tirePressureRearLeft"` // + "2413"
|
||||
TirePressureRearRight string `json:"tirePressureRearRight"` // + "2344"
|
||||
TirePressureFrontLeftPsi string `json:"tirePressureFrontLeftPsi"` // + "33"
|
||||
TirePressureFrontRightPsi string `json:"tirePressureFrontRightPsi"` // + "34"
|
||||
TirePressureRearLeftPsi string `json:"tirePressureRearLeftPsi"` // + "35"
|
||||
TirePressureRearRightPsi string `json:"tirePressureRearRightPsi"` // + "34"
|
||||
TyreStatusFrontLeft string `json:"tyreStatusFrontLeft"` // + "UNKNOWN"
|
||||
TyreStatusFrontRight string `json:"tyreStatusFrontRight"` // + "UNKNOWN"
|
||||
TyreStatusRearLeft string `json:"tyreStatusRearLeft"` // + "UNKNOWN"
|
||||
TyreStatusRearRight string `json:"tyreStatusRearRight"` // + "UNKNOWN"
|
||||
EvStateOfChargePercent float64 `json:"evStateOfChargePercent,omitempty"` // + null
|
||||
EvDistanceToEmptyMiles int `json:"evDistanceToEmptyMiles,omitempty"` // + null
|
||||
EvDistanceToEmptyKilometers int `json:"evDistanceToEmptyKilometers,omitempty"` // + null
|
||||
EvDistanceToEmptyByStateMiles int `json:"evDistanceToEmptyByStateMiles,omitempty"` // + null
|
||||
EvDistanceToEmptyByStateKilometers int `json:"evDistanceToEmptyByStateKilometers,omitempty"` // + null
|
||||
VehicleStateType string `json:"vehicleStateType"` // + "IGNITION_OFF | IGNITION_ON"
|
||||
WindowFrontLeftStatus string `json:"windowFrontLeftStatus"` // + "CLOSE"
|
||||
WindowFrontRightStatus string `json:"windowFrontRightStatus"` // + "CLOSE"
|
||||
WindowRearLeftStatus string `json:"windowRearLeftStatus"` // + "CLOSE"
|
||||
WindowRearRightStatus string `json:"windowRearRightStatus"` // + "CLOSE"
|
||||
WindowSunroofStatus string `json:"windowSunroofStatus"` // + "UNKNOWN"
|
||||
DoorBootPosition string `json:"doorBootPosition"` // CLOSED
|
||||
DoorEngineHoodPosition string `json:"doorEngineHoodPosition"` // CLOSED
|
||||
DoorFrontLeftPosition string `json:"doorFrontLeftPosition"` // CLOSED
|
||||
DoorFrontRightPosition string `json:"doorFrontRightPosition"` // CLOSED
|
||||
DoorRearLeftPosition string `json:"doorRearLeftPosition"` // CLOSED
|
||||
DoorRearRightPosition string `json:"doorRearRightPosition"` // CLOSED
|
||||
DoorBootLockStatus string `json:"doorBootLockStatus"` // LOCKED
|
||||
DoorFrontLeftLockStatus string `json:"doorFrontLeftLockStatus"` // LOCKED
|
||||
DoorFrontRightLockStatus string `json:"doorFrontRightLockStatus"` // LOCKED
|
||||
DoorRearLeftLockStatus string `json:"doorRearLeftLockStatus"` // LOCKED
|
||||
DoorRearRightLockStatus string `json:"doorRearRightLockStatus"` // LOCKED
|
||||
VehicleId int64 `json:"vhsId"` // + 9969776690 5198812434
|
||||
OdometerValue int `json:"odometerValue"` // + 23787
|
||||
OdometerValueKm int `json:"odometerValueKilometers"` // + 38273
|
||||
EventDate UnixTime `json:"eventDate"` // + 1701896993000
|
||||
EventDateStr string `json:"eventDateStr"` // + 2023-12-06T21:09+0000
|
||||
EventDateCarUser UnixTime `json:"eventDateCarUser"` // + 1701896993000
|
||||
EventDateStrCarUser string `json:"eventDateStrCarUser"` // + 2023-12-06T21:09+0000
|
||||
Latitude float64 `json:"latitude"` // + 40.700183
|
||||
Longitude float64 `json:"longitude"` // + -74.401372
|
||||
Heading int `json:"positionHeadingDegree,string"` // + "154"
|
||||
DistanceToEmptyFuelMiles float64 `json:"distanceToEmptyFuelMiles"` // + 209.4
|
||||
DistanceToEmptyFuelKilometers int `json:"distanceToEmptyFuelKilometers"` // + 337
|
||||
DistanceToEmptyFuelMiles10s int `json:"distanceToEmptyFuelMiles10s"` // + 210
|
||||
DistanceToEmptyFuelKilometers10s int `json:"distanceToEmptyFuelKilometers10s"` // + 340
|
||||
AvgFuelConsumptionMpg float64 `json:"avgFuelConsumptionMpg"` // + 18.4
|
||||
AvgFuelConsumptionLitersPer100Kilometers float64 `json:"avgFuelConsumptionLitersPer100Kilometers"` // + 12.8
|
||||
RemainingFuelPercent int `json:"remainingFuelPercent"` // + 82
|
||||
TirePressureFrontLeft int `json:"tirePressureFrontLeft,string,omitempty"` // + "2275"
|
||||
TirePressureFrontRight int `json:"tirePressureFrontRight,string,omitempty"` // + "2344"
|
||||
TirePressureRearLeft int `json:"tirePressureRearLeft,string,omitempty"` // + "2413"
|
||||
TirePressureRearRight int `json:"tirePressureRearRight,string,omitempty"` // + "2344"
|
||||
TirePressureFrontLeftPsi float64 `json:"tirePressureFrontLeftPsi,string,omitempty"` // + "33"
|
||||
TirePressureFrontRightPsi float64 `json:"tirePressureFrontRightPsi,string,omitempty"` // + "34"
|
||||
TirePressureRearLeftPsi float64 `json:"tirePressureRearLeftPsi,string,omitempty"` // + "35"
|
||||
TirePressureRearRightPsi float64 `json:"tirePressureRearRightPsi,string,omitempty"` // + "34"
|
||||
TyreStatusFrontLeft string `json:"tyreStatusFrontLeft"` // + "UNKNOWN"
|
||||
TyreStatusFrontRight string `json:"tyreStatusFrontRight"` // + "UNKNOWN"
|
||||
TyreStatusRearLeft string `json:"tyreStatusRearLeft"` // + "UNKNOWN"
|
||||
TyreStatusRearRight string `json:"tyreStatusRearRight"` // + "UNKNOWN"
|
||||
EvStateOfChargePercent float64 `json:"evStateOfChargePercent,omitempty"` // + null
|
||||
EvDistanceToEmptyMiles int `json:"evDistanceToEmptyMiles,omitempty"` // + null
|
||||
EvDistanceToEmptyKilometers int `json:"evDistanceToEmptyKilometers,omitempty"` // + null
|
||||
EvDistanceToEmptyByStateMiles int `json:"evDistanceToEmptyByStateMiles,omitempty"` // + null
|
||||
EvDistanceToEmptyByStateKilometers int `json:"evDistanceToEmptyByStateKilometers,omitempty"` // + null
|
||||
VehicleStateType string `json:"vehicleStateType"` // + "IGNITION_OFF | IGNITION_ON"
|
||||
WindowFrontLeftStatus string `json:"windowFrontLeftStatus"` // CLOSE | VENTED | OPEN
|
||||
WindowFrontRightStatus string `json:"windowFrontRightStatus"` // CLOSE | VENTED | OPEN
|
||||
WindowRearLeftStatus string `json:"windowRearLeftStatus"` // CLOSE | VENTED | OPEN
|
||||
WindowRearRightStatus string `json:"windowRearRightStatus"` // CLOSE | VENTED | OPEN
|
||||
WindowSunroofStatus string `json:"windowSunroofStatus"` // CLOSE | SLIDE_PARTLY_OPEN | OPEN | TILT
|
||||
DoorBootPosition string `json:"doorBootPosition"` // CLOSED | OPEN
|
||||
DoorEngineHoodPosition string `json:"doorEngineHoodPosition"` // CLOSED | OPEN
|
||||
DoorFrontLeftPosition string `json:"doorFrontLeftPosition"` // CLOSED | OPEN
|
||||
DoorFrontRightPosition string `json:"doorFrontRightPosition"` // CLOSED | OPEN
|
||||
DoorRearLeftPosition string `json:"doorRearLeftPosition"` // CLOSED | OPEN
|
||||
DoorRearRightPosition string `json:"doorRearRightPosition"` // CLOSED | OPEN
|
||||
DoorBootLockStatus string `json:"doorBootLockStatus"` // LOCKED | UNLOCKED
|
||||
DoorFrontLeftLockStatus string `json:"doorFrontLeftLockStatus"` // LOCKED | UNLOCKED
|
||||
DoorFrontRightLockStatus string `json:"doorFrontRightLockStatus"` // LOCKED | UNLOCKED
|
||||
DoorRearLeftLockStatus string `json:"doorRearLeftLockStatus"` // LOCKED | UNLOCKED
|
||||
DoorRearRightLockStatus string `json:"doorRearRightLockStatus"` // LOCKED | UNLOCKED
|
||||
}
|
||||
|
||||
// VehicleCondition .
|
||||
// "dataName":"remoteServiceStatus"
|
||||
// "remoteServiceType":"condition"
|
||||
type VehicleCondition struct {
|
||||
VehicleStateType string `json:"vehicleStateType"` // "IGNITION_OFF",
|
||||
AvgFuelConsumption float64 `json:"avgFuelConsumption"` // null,
|
||||
AvgFuelConsumptionUnit string `json:"avgFuelConsumptionUnit"` // "MPG",
|
||||
DistanceToEmptyFuel int `json:"distanceToEmptyFuel"` // null,
|
||||
DistanceToEmptyFuelUnit string `json:"distanceToEmptyFuelUnit"` // "MILES",
|
||||
RemainingFuelPercent int `json:"remainingFuelPercent"` // "66",
|
||||
Odometer int `json:"odometer"` // 92,
|
||||
OdometerUnit string `json:"odometerUnit"` // "MILES",
|
||||
TirePressureFrontLeft float64 `json:"tirePressureFrontLeft"` // null,
|
||||
TirePressureFrontLeftUnit string `json:"tirePressureFrontLeftUnit"` // "PSI",
|
||||
TirePressureFrontRight float64 `json:"tirePressureFrontRight"` // null,
|
||||
TirePressureFrontRightUnit string `json:"tirePressureFrontRightUnit"` // "PSI",
|
||||
TirePressureRearLeft float64 `json:"tirePressureRearLeft"` // null,
|
||||
TirePressureRearLeftUnit string `json:"tirePressureRearLeftUnit"` // "PSI",
|
||||
TirePressureRearRight float64 `json:"tirePressureRearRight"` // null,
|
||||
TirePressureRearRightUnit string `json:"tirePressureRearRightUnit"` // "PSI",
|
||||
DoorBootPosition string `json:"doorBootPosition"` // "CLOSED",
|
||||
DoorEngineHoodPosition string `json:"doorEngineHoodPosition"` // "CLOSED",
|
||||
DoorFrontLeftPosition string `json:"doorFrontLeftPosition"` // "CLOSED",
|
||||
DoorFrontRightPosition string `json:"doorFrontRightPosition"` // "CLOSED",
|
||||
DoorRearLeftPosition string `json:"doorRearLeftPosition"` // "CLOSED",
|
||||
DoorRearRightPosition string `json:"doorRearRightPosition"` // "CLOSED"
|
||||
WindowFrontLeftStatus string `json:"windowFrontLeftStatus"` // "CLOSE",
|
||||
WindowFrontRightStatus string `json:"windowFrontRightStatus"` // "CLOSE",
|
||||
WindowRearLeftStatus string `json:"windowRearLeftStatus"` // "CLOSE",
|
||||
WindowRearRightStatus string `json:"windowRearRightStatus"` // "CLOSE",
|
||||
WindowSunroofStatus string `json:"windowSunroofStatus"` // "CLOSE",
|
||||
EvDistanceToEmpty int `json:"evDistanceToEmpty"` // null,
|
||||
EvDistanceToEmptyUnit string `json:"evDistanceToEmptyUnit"` // null,
|
||||
EvChargerStateType string `json:"evChargerStateType"` // null,
|
||||
EvIsPluggedIn bool `json:"evIsPluggedIn"` // null,
|
||||
EvStateOfChargeMode string `json:"evStateOfChargeMode"` // null,
|
||||
EvTimeToFullyCharged string `json:"evTimeToFullyCharged"` // null,
|
||||
EvStateOfChargePercent int `json:"evStateOfChargePercent"` // null,
|
||||
LastUpdatedTime string `json:"lastUpdatedTime,string"` // "2023-04-10T17:50:54+0000",
|
||||
VehicleStateType string `json:"vehicleStateType"` // "IGNITION_OFF | IGNITION_ON"
|
||||
AvgFuelConsumption float64 `json:"avgFuelConsumption,omitempty"` // null | 18.4
|
||||
AvgFuelConsumptionUnit string `json:"avgFuelConsumptionUnit"` // "MPG"
|
||||
DistanceToEmptyFuel int `json:"distanceToEmptyFuel,omitempty"` // null | 160
|
||||
DistanceToEmptyFuelUnit string `json:"distanceToEmptyFuelUnit"` // "MILES"
|
||||
RemainingFuelPercent int `json:"remainingFuelPercent,string"` // "66"
|
||||
Odometer int `json:"odometer"` // 92
|
||||
OdometerUnit string `json:"odometerUnit"` // "MILES"
|
||||
TirePressureFrontLeft float64 `json:"tirePressureFrontLeft,omitempty"` // null | 36
|
||||
TirePressureFrontLeftUnit string `json:"tirePressureFrontLeftUnit"` // "PSI"
|
||||
TirePressureFrontRight float64 `json:"tirePressureFrontRight,omitempty"` // null | 36
|
||||
TirePressureFrontRightUnit string `json:"tirePressureFrontRightUnit"` // "PSI",
|
||||
TirePressureRearLeft float64 `json:"tirePressureRearLeft,omitempty"` // null | 36
|
||||
TirePressureRearLeftUnit string `json:"tirePressureRearLeftUnit"` // "PSI"
|
||||
TirePressureRearRight float64 `json:"tirePressureRearRight,omitempty"` // null | 36
|
||||
TirePressureRearRightUnit string `json:"tirePressureRearRightUnit"` // "PSI"
|
||||
DoorBootPosition string `json:"doorBootPosition"` // "CLOSED | OPEN"
|
||||
DoorEngineHoodPosition string `json:"doorEngineHoodPosition"` // "CLOSED | OPEN"
|
||||
DoorFrontLeftPosition string `json:"doorFrontLeftPosition"` // "CLOSED | OPEN"
|
||||
DoorFrontRightPosition string `json:"doorFrontRightPosition"` // "CLOSED | OPEN"
|
||||
DoorRearLeftPosition string `json:"doorRearLeftPosition"` // "CLOSED | OPEN"
|
||||
DoorRearRightPosition string `json:"doorRearRightPosition"` // "CLOSED | OPEN"
|
||||
WindowFrontLeftStatus string `json:"windowFrontLeftStatus"` // "CLOSE | VENTED | OPEN"
|
||||
WindowFrontRightStatus string `json:"windowFrontRightStatus"` // "CLOSE | VENTED | OPEN"
|
||||
WindowRearLeftStatus string `json:"windowRearLeftStatus"` // "CLOSE | VENTED | OPEN"
|
||||
WindowRearRightStatus string `json:"windowRearRightStatus"` // "CLOSE | VENTED | OPEN"
|
||||
WindowSunroofStatus string `json:"windowSunroofStatus"` // "CLOSE | VENTED | OPEN"
|
||||
EvDistanceToEmpty int `json:"evDistanceToEmpty,omitempty"` // null,
|
||||
EvDistanceToEmptyUnit string `json:"evDistanceToEmptyUnit,omitempty"` // null,
|
||||
EvChargerStateType string `json:"evChargerStateType,omitempty"` // null,
|
||||
EvIsPluggedIn bool `json:"evIsPluggedIn,omitempty"` // null,
|
||||
EvStateOfChargeMode string `json:"evStateOfChargeMode,omitempty"` // null,
|
||||
EvTimeToFullyCharged string `json:"evTimeToFullyCharged,omitempty"` // null,
|
||||
EvStateOfChargePercent int `json:"evStateOfChargePercent,omitempty"` // null,
|
||||
LastUpdatedTime string `json:"lastUpdatedTime"` // "2023-04-10T17:50:54+0000",
|
||||
}
|
||||
|
||||
// ClimateSettings .
|
||||
// "dataName":null
|
||||
// type ClimateSettings struct {
|
||||
// RunTimeMinutes string `json:"runTimeMinutes"`
|
||||
// StartConfiguration string `json:"startConfiguration"`
|
||||
// AirConditionOn string `json:"airConditionOn"`
|
||||
// OuterAirCirculation string `json:"outerAirCirculation"`
|
||||
// ClimateZoneFrontAirMode string `json:"climateZoneFrontAirMode"`
|
||||
// ClimateZoneFrontTemp string `json:"climateZoneFrontTemp"`
|
||||
// ClimateZoneFrontAirVolume string `json:"climateZoneFrontAirVolume"`
|
||||
// HeatedSeatFrontLeft string `json:"heatedSeatFrontLeft"`
|
||||
// HeatedSeatFrontRight string `json:"heatedSeatFrontRight"`
|
||||
// HeatedRearWindowActive string `json:"heatedRearWindowActive"`
|
||||
// }
|
||||
// ClimateProfile represents a climate control profile for a Subaru vehicle.
|
||||
type ClimateProfile struct {
|
||||
Name string `json:"name"`
|
||||
VehicleType string `json:"vehicleType,omitempty"` // vehicleType [ gas | phev ]
|
||||
PresetType string `json:"presetType"` // presetType [ subaruPreset | userPreset ]
|
||||
StartConfiguration string `json:"startConfiguration"` // startConfiguration [ START_ENGINE_ALLOW_KEY_IN_IGNITION (gas) | START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION (phev) ]
|
||||
RunTimeMinutes int `json:"runTimeMinutes,string"` // runTimeMinutes [ 0 | 1 | 5 | 10 ]
|
||||
HeatedRearWindowActive string `json:"heatedRearWindowActive"` // heatedRearWindowActive: [ false | true ]
|
||||
HeatedSeatFrontRight string `json:"heatedSeatFrontRight"` // heatedSeatFrontRight: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ]
|
||||
HeatedSeatFrontLeft string `json:"heatedSeatFrontLeft"` // heatedSeatFrontLeft: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ]
|
||||
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 ]
|
||||
ClimateZoneFrontAirVolume string `json:"climateZoneFrontAirVolume"` // climateZoneFrontAirVolume: [ AUTO | 2 | 4 | 7 ]
|
||||
OuterAirCirculation string `json:"outerAirCirculation"` // airConditionOn: [ auto | outsideAir | true ]
|
||||
AirConditionOn string `json:"airConditionOn"` // airConditionOn: [ false | true ]
|
||||
CanEdit string `json:"canEdit"` // canEdit [ false | true ]
|
||||
Disabled string `json:"disabled"` // disabled [ false | true ]
|
||||
}
|
||||
|
||||
type ClimateProfiles map[string]ClimateProfile
|
||||
|
||||
// GeoLocation represents the geographical location of a Subaru vehicle.
|
||||
type GeoLocation struct {
|
||||
Latitude float64 `json:"latitude"` // 40.700184
|
||||
Longitude float64 `json:"longitude"` // -74.401375
|
||||
Heading int `json:"heading,omitempty"` // 189
|
||||
Speed int `json:"speed,omitempty"` // 0.00
|
||||
Updated CustomTime1 `json:"timestamp"` // "2025-07-08T19:05:07"
|
||||
}
|
||||
|
||||
// ServiceRequest .
|
||||
// "dataName": "remoteServiceStatus"
|
||||
type ServiceRequest struct {
|
||||
ServiceRequestID *string `json:"serviceRequestId,omitempty"` // 4S4BTGND8L3137058_1640294426029_19_@NGTP
|
||||
Success bool `json:"success"` // false | true
|
||||
Cancelled bool `json:"cancelled"` // false | true
|
||||
RemoteServiceType string `json:"remoteServiceType"` // unlock | lock | locate | vehicleStatus | lightsOnly | engineStart | engineStop | phevChargeNow | condition
|
||||
RemoteServiceState string `json:"remoteServiceState"` // started | finished | stopping
|
||||
SubState *string `json:"subState,omitempty"` // null
|
||||
ErrorCode *string `json:"errorCode,omitempty"` // null:null
|
||||
Result *gabs.Container `json:"result,omitempty"` // null
|
||||
UpdateTime *time.Time `json:"updateTime,omitempty"` // timestamp
|
||||
ServiceRequestID string `json:"serviceRequestId,omitempty"` // 4S4BTGND8L3137058_1640294426029_19_@NGTP
|
||||
Vin string `json:"vin"` // 4S4BTGND8L3137058
|
||||
Success bool `json:"success"` // false | true // Could be in the false state while the executed request in the progress
|
||||
Cancelled bool `json:"cancelled"` // false | true
|
||||
RemoteServiceType string `json:"remoteServiceType"` // vehicleStatus | condition | locate | unlock | lock | lightsOnly | engineStart | engineStop | phevChargeNow
|
||||
RemoteServiceState string `json:"remoteServiceState"` // started | finished | stopping
|
||||
SubState string `json:"subState,omitempty"` // null
|
||||
ErrorCode string `json:"errorCode,omitempty"` // null:null
|
||||
Result json.RawMessage `json:"result,omitempty"` // struct
|
||||
UpdateTime UnixTime `json:"updateTime,omitempty"` // timestamp // is empty if the request is started
|
||||
}
|
||||
|
||||
// ErrorResponse .
|
||||
// "dataName":"errorResponse"
|
||||
type ErrorResponse struct {
|
||||
ErrorLabel string `json:"errorLabel"` // "404-soa-unableToParseResponseBody"
|
||||
ErrorDescription *string `json:"errorDescription,omitempty"` //null
|
||||
// parse parses the JSON response from the MySubaru API into a ServiceRequest struct.
|
||||
func (sr *ServiceRequest) parse(b []byte, logger *slog.Logger) error {
|
||||
err := json.Unmarshal(b, &sr)
|
||||
if err != nil {
|
||||
logger.Error("error while parsing json", "request", "GetVehicleCondition", "error", err.Error())
|
||||
}
|
||||
if !sr.Success && sr.ErrorCode != "" {
|
||||
logger.Error("error in response", "request", "GetVehicleCondition", "errorCode", sr.ErrorCode, "remoteServiceType", sr.RemoteServiceType)
|
||||
switch sr.ErrorCode {
|
||||
case API_ERRORS["API_ERROR_SERVICE_ALREADY_STARTED"]:
|
||||
return errors.New("error in response: Service already started")
|
||||
case API_ERRORS["API_ERROR_VEHICLE_NOT_IN_ACCOUNT"]:
|
||||
return errors.New("error in response: Vehicle not in account")
|
||||
case API_ERRORS["API_ERROR_SOA_403"]:
|
||||
return errors.New("error in response: Unable to parse response body, SOA 403 error")
|
||||
default:
|
||||
return errors.New("error in response: " + sr.ErrorCode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// climateSettings: [ climateSettings ]
|
||||
@ -283,14 +426,87 @@ type ErrorResponse struct {
|
||||
// outerAirCirculation: [ outsideAir, recirculation ]
|
||||
// airConditionOn: [ false | true ]
|
||||
// heatedRearWindowActive: [ false | true ]
|
||||
// startConfiguration: [ start_Climate_Control_only_allow_key_in_ignition | START_ENGINE_ALLOW_KEY_IN_IGNITION ]
|
||||
// runTimeMinutes: [ 10 ],
|
||||
// startConfiguration: [ START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION | START_ENGINE_ALLOW_KEY_IN_IGNITION ]
|
||||
// runTimeMinutes: [ 10 ]
|
||||
|
||||
type VehicleHealthItem struct {
|
||||
B2cCode string `json:"b2cCode"`
|
||||
FeatureCode string `json:"featureCode"`
|
||||
IsTrouble bool `json:"isTrouble"`
|
||||
OnDaiID int `json:"onDaiId"`
|
||||
OnDates []string `json:"onDates"`
|
||||
WarningCode int `json:"warningCode"`
|
||||
type VehicleHealth struct {
|
||||
VehicleHealthItems []VehicleHealthItem `json:"vehicleHealthItems"`
|
||||
LastUpdatedDate int64 `json:"lastUpdatedDate"`
|
||||
}
|
||||
type VehicleHealthItem struct {
|
||||
WarningCode int `json:"warningCode"` // internal code used by MySubaru, not documented
|
||||
B2cCode string `json:"b2cCode"` // oilTemp | airbag | oilLevel | etc.
|
||||
FeatureCode string `json:"featureCode"` // SRS_MIL | CEL_MIL | ATF_MIL | etc.
|
||||
IsTrouble bool `json:"isTrouble"` // false | true
|
||||
OnDaiID int `json:"onDaiId"` // Has a number, probably internal record id
|
||||
OnDates []UnixTime `json:"onDates,omitempty"` // List of the timestamps
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// UnixTime is a wrapper around time.Time that allows us to marshal and unmarshal Unix timestamps
|
||||
type UnixTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON is the method that satisfies the Unmarshaller interface
|
||||
// Note that it uses a pointer receiver. It needs this because it will be modifying the embedded time.Time instance
|
||||
func (u *UnixTime) UnmarshalJSON(b []byte) error {
|
||||
var timestamp int64
|
||||
err := json.Unmarshal(b, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Time = time.Unix(timestamp, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON turns our time.Time back into an int
|
||||
func (u UnixTime) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%d", (u.Time.Unix()))), nil
|
||||
}
|
||||
|
||||
// CustomTime1 "2021-12-22T13:14:47" is a custom type for unmarshalling time strings
|
||||
type CustomTime1 struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (ct *CustomTime1) UnmarshalJSON(b []byte) (err error) {
|
||||
// Use the correct layout string for the desired format
|
||||
const layout = "2006-01-02T15:04:05"
|
||||
s := strings.Trim(string(b), "\\\"") // Remove surrounding escapes and quotes (parsing time \"\\\"2025-07-09T00:23:19\\\"\" as \"2006-01-02T15:04:05\")
|
||||
if string(b) == "null" {
|
||||
ct.Time = time.Time{}
|
||||
return nil
|
||||
}
|
||||
ct.Time, err = time.Parse(layout, s) // Parse the string with your custom layout
|
||||
return
|
||||
}
|
||||
|
||||
// CustomTime2 "2023-04-10T17:50:54+0000" is a custom type for unmarshalling time strings
|
||||
type CustomTime2 struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (ct *CustomTime2) UnmarshalJSON(b []byte) (err error) {
|
||||
// Use the correct layout string for the desired format
|
||||
const layout = "2006-01-02T15:04:05-0700"
|
||||
s := strings.Trim(string(b), "\\\"") // Remove surrounding escapes and quotes ((parsing time \"\\\"2025-07-09T00:23:19\\\"\" as \"2006-01-02T15:04:05\"))
|
||||
if string(b) == "null" {
|
||||
ct.Time = time.Time{}
|
||||
return nil
|
||||
}
|
||||
ct.Time, err = time.Parse(layout, s) // Parse the string with your custom layout
|
||||
return
|
||||
}
|
||||
|
204
mysubaru_test.go
Normal file
204
mysubaru_test.go
Normal file
@ -0,0 +1,204 @@
|
||||
package mysubaru
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUnixTime_UnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantTime time.Time
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "valid unix timestamp",
|
||||
input: "1700000000",
|
||||
wantTime: time.Unix(1700000000, 0),
|
||||
},
|
||||
{
|
||||
name: "invalid string",
|
||||
input: "\"notanumber\"",
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: "",
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "float value",
|
||||
input: "1700000000.123",
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "zero timestamp",
|
||||
input: "0",
|
||||
wantTime: time.Unix(0, 0),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var ut UnixTime
|
||||
err := ut.UnmarshalJSON([]byte(tt.input))
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("UnmarshalJSON() error = %v, wantError %v", err, tt.wantError)
|
||||
return
|
||||
}
|
||||
if !tt.wantError && !ut.Time.Equal(tt.wantTime) {
|
||||
t.Errorf("UnmarshalJSON() got = %v, want %v", ut.Time, tt.wantTime)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_UnmarshalJSON_withJSONUnmarshal(t *testing.T) {
|
||||
type testStruct struct {
|
||||
Time UnixTime `json:"time"`
|
||||
}
|
||||
input := `{"time":1700000000}`
|
||||
var ts testStruct
|
||||
err := json.Unmarshal([]byte(input), &ts)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||||
}
|
||||
want := time.Unix(1700000000, 0)
|
||||
if !ts.Time.Time.Equal(want) {
|
||||
t.Errorf("UnmarshalJSON() got = %v, want %v", ts.Time.Time, want)
|
||||
}
|
||||
}
|
||||
func TestUnixTime_MarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input time.Time
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "epoch",
|
||||
input: time.Unix(0, 0),
|
||||
want: "0",
|
||||
},
|
||||
{
|
||||
name: "positive unix time",
|
||||
input: time.Unix(1700000000, 0),
|
||||
want: "1700000000",
|
||||
},
|
||||
{
|
||||
name: "negative unix time",
|
||||
input: time.Unix(-100, 0),
|
||||
want: "-100",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ut := UnixTime{Time: tt.input}
|
||||
got, err := ut.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("MarshalJSON() error = %v", err)
|
||||
}
|
||||
if string(got) != tt.want {
|
||||
t.Errorf("MarshalJSON() = %s, want %s", string(got), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_MarshalJSON_withJSONMarshal(t *testing.T) {
|
||||
type testStruct struct {
|
||||
Time UnixTime `json:"time"`
|
||||
}
|
||||
ts := testStruct{Time: UnixTime{Time: time.Unix(1700000000, 0)}}
|
||||
b, err := json.Marshal(ts)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal failed: %v", err)
|
||||
}
|
||||
want := `{"time":1700000000}`
|
||||
if string(b) != want {
|
||||
t.Errorf("json.Marshal() = %s, want %s", string(b), want)
|
||||
}
|
||||
}
|
||||
|
||||
// func TestResponse_parse(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// input string
|
||||
// wantErr error
|
||||
// wantCode string
|
||||
// wantLog string
|
||||
// }{
|
||||
// {
|
||||
// name: "success response",
|
||||
// input: `{"success":true,"dataName":"foo","data":{}}`,
|
||||
// wantErr: nil,
|
||||
// },
|
||||
// {
|
||||
// name: "invalid json",
|
||||
// input: `{"success":tru`,
|
||||
// wantErr: errors.New("error while parsing json:"),
|
||||
// wantLog: "error while parsing json",
|
||||
// },
|
||||
// {
|
||||
// name: "API_ERROR_NO_ACCOUNT",
|
||||
// input: `{"success":false,"errorCode":"noAccount","dataName":"errorResponse","data":{}}`,
|
||||
// wantErr: errors.New("error in response: Account not found"),
|
||||
// wantCode: "noAccount",
|
||||
// wantLog: "error in response",
|
||||
// },
|
||||
// {
|
||||
// name: "API_ERROR_INVALID_CREDENTIALS",
|
||||
// input: `{"success":false,"errorCode":"invalidCredentials","dataName":"errorResponse","data":{}}`,
|
||||
// wantErr: errors.New("error in response: Invalid Credentials"),
|
||||
// wantCode: "invalidCredentials",
|
||||
// wantLog: "error in response",
|
||||
// },
|
||||
// {
|
||||
// name: "API_ERROR_SOA_403",
|
||||
// input: `{"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{}}`,
|
||||
// wantErr: errors.New("error in response: Unable to parse response body, SOA 403 error"),
|
||||
// wantCode: "404-soa-unableToParseResponseBody",
|
||||
// wantLog: "error in response",
|
||||
// },
|
||||
// {
|
||||
// name: "unknown error code",
|
||||
// input: `{"success":false,"errorCode":"somethingElse","dataName":"errorResponse","data":{}}`,
|
||||
// wantErr: errors.New("error in response: somethingElse"),
|
||||
// wantCode: "somethingElse",
|
||||
// wantLog: "error in response",
|
||||
// },
|
||||
// {
|
||||
// name: "no errorCode but not success",
|
||||
// input: `{"success":false,"dataName":"errorResponse","data":{}}`,
|
||||
// wantErr: nil,
|
||||
// },
|
||||
// }
|
||||
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// var resp Response
|
||||
// logger := slog.New(slog.NewTextHandler(nil, nil))
|
||||
// got, err := resp.parse([]byte(tt.input), logger)
|
||||
// if tt.wantErr != nil {
|
||||
// if err == nil {
|
||||
// t.Fatalf("expected error, got nil")
|
||||
// }
|
||||
// if !contains(err.Error(), tt.wantErr.Error()) {
|
||||
// t.Errorf("parse() error = %v, want %v", err, tt.wantErr)
|
||||
// }
|
||||
// } else if err != nil {
|
||||
// t.Errorf("parse() unexpected error: %v", err)
|
||||
// }
|
||||
// if tt.wantCode != "" && got != nil && got.ErrorCode != tt.wantCode {
|
||||
// t.Errorf("parse() got.ErrorCode = %v, want %v", got.ErrorCode, tt.wantCode)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// // contains is a helper for substring matching.
|
||||
// func contains(s, substr string) bool {
|
||||
// return bytes.Contains([]byte(s), []byte(substr))
|
||||
// }
|
110
utils.go
110
utils.go
@ -3,12 +3,31 @@ package mysubaru
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/mail"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// timestamp is a function
|
||||
func timestamp() string {
|
||||
return strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
}
|
||||
|
||||
// urlToGen .
|
||||
func urlToGen(url string, gen string) string {
|
||||
var re = regexp.MustCompile(`api_gen`)
|
||||
// dirty trick for current G3
|
||||
if gen == "g3" {
|
||||
gen = "g2"
|
||||
}
|
||||
url = re.ReplaceAllString(url, gen)
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
// VinCheck - Vehicle Identification Number check digit validation
|
||||
// Parameter: string - 17 digit VIN
|
||||
// Return:
|
||||
@ -42,6 +61,7 @@ func vinCheck(vin string) (bool, string) {
|
||||
return valid, retVin
|
||||
}
|
||||
|
||||
// transcodeDigits transcodes VIN digits to a numeric value
|
||||
func transcodeDigits(vin string) int {
|
||||
var digitSum = 0
|
||||
var code int
|
||||
@ -97,47 +117,67 @@ func transcodeDigits(vin string) int {
|
||||
return digitSum
|
||||
}
|
||||
|
||||
// isNilFixed .
|
||||
// func isNil(i interface{}) bool {
|
||||
// if i == nil {
|
||||
// return true
|
||||
// }
|
||||
// switch reflect.TypeOf(i).Kind() {
|
||||
// case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice:
|
||||
// return reflect.ValueOf(i).IsNil()
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
// emailMasking takes an email address as input and returns a version of the email
|
||||
// with the username part partially hidden for privacy. Only the first and last
|
||||
// characters of the username are visible, with the middle characters replaced by asterisks.
|
||||
// The function validates the email format before processing.
|
||||
// Returns the obfuscated email or an error if the input is not a valid email address.
|
||||
func emailMasking(email string) (string, error) {
|
||||
_, err := mail.ParseAddress(email)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid email address: %s", email)
|
||||
}
|
||||
|
||||
// timeTrack .
|
||||
func timeTrack(name string) {
|
||||
start := time.Now()
|
||||
fmt.Printf("%s took %v\n", name, time.Since(start))
|
||||
re1 := regexp.MustCompile(`^(.*?)@(.*)$`)
|
||||
matches := re1.FindStringSubmatch(email)
|
||||
|
||||
var username, domain string
|
||||
if len(matches) == 3 { // Expecting the full match, username, and domain
|
||||
username = matches[1]
|
||||
domain = matches[2]
|
||||
} else {
|
||||
return "", fmt.Errorf("invalid email format: %s", email)
|
||||
}
|
||||
|
||||
re2 := regexp.MustCompile(`(.)(.*)(.)`)
|
||||
|
||||
replacedString := re2.ReplaceAllStringFunc(username, func(s string) string {
|
||||
firstChar := string(s[0])
|
||||
lastChar := string(s[len(s)-1])
|
||||
middleCharsCount := len(s) - 2
|
||||
|
||||
if middleCharsCount < 0 { // Should not happen with the length check above, but for robustness
|
||||
return s
|
||||
}
|
||||
return firstChar + strings.Repeat("*", middleCharsCount) + lastChar
|
||||
})
|
||||
|
||||
return replacedString + "@" + domain, nil
|
||||
}
|
||||
|
||||
// timestamp is a function
|
||||
func timestamp() string {
|
||||
return strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
}
|
||||
// containsValueInStruct checks if any string field in the given struct 's' contains the specified 'search' substring (case-insensitive).
|
||||
// It returns true if at least one string field contains the substring, and false otherwise.
|
||||
// If 's' is not a struct, it returns false.
|
||||
func containsValueInStruct(s any, search string) bool {
|
||||
val := reflect.ValueOf(s)
|
||||
if val.Kind() != reflect.Struct {
|
||||
return false // Not a struct
|
||||
}
|
||||
|
||||
// contains .
|
||||
func contains(s []string, str string) bool {
|
||||
for _, v := range s {
|
||||
if v == str {
|
||||
return true
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := val.Field(i)
|
||||
if field.Kind() == reflect.String {
|
||||
if strings.Contains(strings.ToLower(field.String()), strings.ToLower(search)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// urlToGen .
|
||||
func urlToGen(url string, gen string) string {
|
||||
var re = regexp.MustCompile(`api_gen`)
|
||||
// dirty trick for current G3
|
||||
if gen == "g3" {
|
||||
gen = "g2"
|
||||
}
|
||||
url = re.ReplaceAllString(url, gen)
|
||||
|
||||
return url
|
||||
}
|
||||
// timeTrack .
|
||||
// func timeTrack(name string) {
|
||||
// start := time.Now()
|
||||
// fmt.Printf("%s took %v\n", name, time.Since(start))
|
||||
// }
|
||||
|
216
utils_test.go
Normal file
216
utils_test.go
Normal file
@ -0,0 +1,216 @@
|
||||
package mysubaru
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// timestamp returns the current time in milliseconds since epoch as a string.
|
||||
func TestTimestamp(t *testing.T) {
|
||||
ts1 := timestamp()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
ts2 := timestamp()
|
||||
|
||||
// Should be numeric
|
||||
if _, err := strconv.ParseInt(ts1, 10, 64); err != nil {
|
||||
t.Errorf("timestamp() returned non-numeric string: %s", ts1)
|
||||
}
|
||||
// Should be increasing
|
||||
if ts1 >= ts2 {
|
||||
t.Errorf("timestamp() not increasing: %s >= %s", ts1, ts2)
|
||||
}
|
||||
}
|
||||
|
||||
// timestamp returns the current time in milliseconds since epoch as a string.
|
||||
func TestTimestamp_Format(t *testing.T) {
|
||||
ts := timestamp()
|
||||
matched, err := regexp.MatchString(`^\d+$`, ts)
|
||||
if err != nil {
|
||||
t.Fatalf("regexp error: %v", err)
|
||||
}
|
||||
if !matched {
|
||||
t.Errorf("timestamp() = %q, want only digits", ts)
|
||||
}
|
||||
}
|
||||
|
||||
// urlToGen replaces "api_gen" in the URL with the specified generation.
|
||||
func TestUrlToGen(t *testing.T) {
|
||||
tests := []struct {
|
||||
url, gen, want string
|
||||
}{
|
||||
{"https://host/api_gen/endpoint", "g1", "https://host/g1/endpoint"},
|
||||
{"https://host/api_gen/endpoint", "g2", "https://host/g2/endpoint"},
|
||||
{"https://host/api_gen/endpoint", "g3", "https://host/g2/endpoint"}, // g3 special case
|
||||
{"https://host/api_gen/api_gen", "g1", "https://host/g1/g1"},
|
||||
{"https://host/other/endpoint", "g1", "https://host/other/endpoint"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := urlToGen(tt.url, tt.gen)
|
||||
if got != tt.want {
|
||||
t.Errorf("urlToGen(%q, %q) = %q, want %q", tt.url, tt.gen, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vinCheck validates the VIN check digit and returns the corrected VIN.
|
||||
func TestVinCheck_Valid(t *testing.T) {
|
||||
// Example valid VIN: 1HGCM82633A004352 (check digit is '3')
|
||||
vin := "1HGCM82633A004352"
|
||||
valid, corrected := vinCheck(vin)
|
||||
if !valid {
|
||||
t.Errorf("vinCheck(%q) = false, want true", vin)
|
||||
}
|
||||
if corrected != vin {
|
||||
t.Errorf("vinCheck(%q) corrected VIN = %q, want %q", vin, corrected, vin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestVinCheck_InvalidCheckDigit tests a VIN with an incorrect check digit.
|
||||
func TestVinCheck_InvalidCheckDigit(t *testing.T) {
|
||||
vin := "1HGCM82633A004352"
|
||||
// Change check digit (9th char) to '9'
|
||||
badVin := vin[:8] + "9" + vin[9:]
|
||||
valid, corrected := vinCheck(badVin)
|
||||
if valid {
|
||||
t.Errorf("vinCheck(%q) = true, want false", badVin)
|
||||
}
|
||||
// Should correct to original VIN
|
||||
if corrected != vin {
|
||||
t.Errorf("vinCheck(%q) corrected VIN = %q, want %q", badVin, corrected, vin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestVinCheck_WrongLength tests a VIN that is not 17 characters long.
|
||||
func TestVinCheck_WrongLength(t *testing.T) {
|
||||
vin := "1234567890123456" // 16 chars
|
||||
valid, corrected := vinCheck(vin)
|
||||
if valid {
|
||||
t.Errorf("vinCheck(%q) = true, want false", vin)
|
||||
}
|
||||
if corrected != "" {
|
||||
t.Errorf("vinCheck(%q) corrected VIN = %q, want empty string", vin, corrected)
|
||||
}
|
||||
}
|
||||
|
||||
// transcodeDigits computes the sum of the VIN digits according to the VIN rules.
|
||||
func TestTranscodeDigits(t *testing.T) {
|
||||
// Use a known VIN and manually compute the sum
|
||||
vin := "1HGCM82633A004352"
|
||||
sum := transcodeDigits(vin)
|
||||
// Precomputed sum for this VIN is 311 (from online VIN calculator)
|
||||
want := 311
|
||||
if sum != want {
|
||||
t.Errorf("transcodeDigits(%q) = %d, want %d", vin, sum, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestVinCheck_XCheckDigit tests a VIN with 'X' as the check digit.
|
||||
func TestVinCheck_XCheckDigit(t *testing.T) {
|
||||
// VIN with check digit 'X'
|
||||
vin := "1M8GDM9AXKP042788"
|
||||
valid, corrected := vinCheck(vin)
|
||||
if !valid {
|
||||
t.Errorf("vinCheck(%q) = false, want true", vin)
|
||||
}
|
||||
if corrected != vin {
|
||||
t.Errorf("vinCheck(%q) corrected VIN = %q, want %q", vin, corrected, vin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUrlToGen_NoApiGen tests the case where the URL does not contain "api_gen".
|
||||
func TestUrlToGen_NoApiGen(t *testing.T) {
|
||||
url := "https://host/endpoint"
|
||||
gen := "g1"
|
||||
got := urlToGen(url, gen)
|
||||
if got != url {
|
||||
t.Errorf("urlToGen(%q, %q) = %q, want %q", url, gen, got, url)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailHidder(t *testing.T) {
|
||||
tests := []struct {
|
||||
email string
|
||||
expected string
|
||||
wantErr bool
|
||||
}{
|
||||
{"alex@example.com", "a**x@example.com", false},
|
||||
{"a@example.com", "a@example.com", false},
|
||||
{"ab@example.com", "ab@example.com", false},
|
||||
{"", "", true},
|
||||
{"notanemail", "", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got, err := emailMasking(tt.email)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("emailHidder(%q) error = %v, wantErr %v", tt.email, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
if got != tt.expected {
|
||||
t.Errorf("emailHidder(%q) = %q, want %q", tt.email, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestContainsValueInStruct(t *testing.T) {
|
||||
type TestStruct struct {
|
||||
Name string
|
||||
Address string
|
||||
Age int
|
||||
Note string
|
||||
}
|
||||
tests := []struct {
|
||||
s any
|
||||
search string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
s: TestStruct{Name: "Alice", Address: "123 Main St", Age: 30, Note: "VIP customer"},
|
||||
search: "alice",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
s: TestStruct{Name: "Bob", Address: "456 Elm St", Age: 25, Note: "Regular"},
|
||||
search: "elm",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
s: TestStruct{Name: "Charlie", Address: "789 Oak St", Age: 40, Note: "VIP"},
|
||||
search: "vip",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
s: TestStruct{Name: "Diana", Address: "101 Pine St", Age: 22, Note: ""},
|
||||
search: "xyz",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
s: TestStruct{Name: "", Address: "", Age: 0, Note: ""},
|
||||
search: "",
|
||||
want: true, // empty string is contained in all strings
|
||||
},
|
||||
{
|
||||
s: struct{ Foo int }{Foo: 42},
|
||||
search: "42",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
s: "not a struct",
|
||||
search: "struct",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
s: struct{ S string }{S: "CaseInsensitive"},
|
||||
search: "caseinsensitive",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := containsValueInStruct(tt.s, tt.search)
|
||||
if got != tt.want {
|
||||
t.Errorf("Test %d: containsStringInStruct(%#v, %q) = %v, want %v", i, tt.s, tt.search, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
1905
vehicle.go
1905
vehicle.go
File diff suppressed because it is too large
Load Diff
307
vehicle_test.go
Normal file
307
vehicle_test.go
Normal file
@ -0,0 +1,307 @@
|
||||
package mysubaru
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestGetClimateQuickPresets_Success tests the retrieval of quick climate presets.
|
||||
func TestGetClimatePresets_Success(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle API_LOGIN endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_LOGIN"] && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":"BIOMETRICS_DISABLED","dataName":"sessionData","data":{"sessionChanged":false,"vehicleInactivated":false,"account":{"marketId":1,"createdDate":1476984644000,"firstName":"Tatiana","lastName":"Savin","zipCode":"07974","accountKey":765268,"lastLoginDate":1751738613000,"zipCode5":"07974"},"resetPassword":false,"deviceId":"JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ","sessionId":"9D7FCDF274794346689D3FA0D693CBBF","deviceRegistered":true,"passwordToken":null,"vehicles":[{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":null,"modelCode":null,"engineSize":null,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"","licensePlateState":"","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":null,"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":null,"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":null,"authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":null,"subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":null,"sunsetUpgraded":true,"intDescrip":null,"transCode":null,"provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}],"rightToRepairEnabled":true,"rightToRepairStartYear":2022,"rightToRepairStates":"MA","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","currentVehicleIndex":0,"handoffToken":"$2a$08$rOb/uqhm8I3QtSel2phOCOxNM51w43eqXDDksMkJ.1a5KsaQuLvEu$1751745334477","satelliteViewEnabled":true,"registeredDevicePermanent":true}}`)
|
||||
}
|
||||
// Handle API_VALIDATE_SESSION endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VALIDATE_SESSION"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":null}`)
|
||||
}
|
||||
// Handle SELECT_VEHICLE endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_SELECT_VEHICLE"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"vehicle","data":{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":"2023","modelCode":"PDL","engineSize":2.4,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"8KV8","licensePlateState":"NJ","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":["REMOTE","SAFETY","Retail3"],"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":["ABS_MIL","ACCS","AHBL_MIL","ATF_MIL","AWD_MIL","BSD","BSDRCT_MIL","CEL_MIL","CP1_5HHU","EBD_MIL","EOL_MIL","EPAS_MIL","EPB_MIL","ESS_MIL","EYESIGHT","ISS_MIL","MOONSTAT","OPL_MIL","PANPM-TUIRWAOC","PWAAADWWAP","RAB_MIL","RCC","REARBRK","RES","RESCC","RES_HVAC_HFS","RES_HVAC_VFS","RHSF","RPOI","RPOIA","RTGU","RVFS","SRH_MIL","SRS_MIL","SXM360L","T23DCM","TEL_MIL","TIF_35","TIR_33","TLD","TPMS_MIL","VALET","VDC_MIL","WASH_MIL","WDWSTAT","g3"],"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":"ACTIVE","authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":"Outback","subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":"Cosmic Blue Pearl","sunsetUpgraded":true,"intDescrip":"Black","transCode":"CVT","provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}}`)
|
||||
}
|
||||
// Handle API_G2_FETCH_RES_SUBARU_PRESETS endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_G2_FETCH_RES_SUBARU_PRESETS"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":["{\"name\": \"Auto\", \"runTimeMinutes\": \"10\", \"climateZoneFrontTemp\": \"74\", \"climateZoneFrontAirMode\": \"AUTO\", \"climateZoneFrontAirVolume\": \"AUTO\", \"outerAirCirculation\": \"auto\", \"heatedRearWindowActive\": \"false\", \"airConditionOn\": \"false\", \"heatedSeatFrontLeft\": \"off\", \"heatedSeatFrontRight\": \"off\", \"startConfiguration\": \"START_ENGINE_ALLOW_KEY_IN_IGNITION\", \"canEdit\": \"true\", \"disabled\": \"false\", \"vehicleType\": \"gas\", \"presetType\": \"subaruPreset\" }","{\"name\":\"Full Cool\",\"runTimeMinutes\":\"10\",\"climateZoneFrontTemp\":\"60\",\"climateZoneFrontAirMode\":\"feet_face_balanced\",\"climateZoneFrontAirVolume\":\"7\",\"airConditionOn\":\"true\",\"heatedSeatFrontLeft\":\"high_cool\",\"heatedSeatFrontRight\":\"high_cool\",\"heatedRearWindowActive\":\"false\",\"outerAirCirculation\":\"outsideAir\",\"startConfiguration\":\"START_ENGINE_ALLOW_KEY_IN_IGNITION\",\"canEdit\":\"true\",\"disabled\":\"true\",\"vehicleType\":\"gas\",\"presetType\":\"subaruPreset\"}","{\"name\": \"Full Heat\", \"runTimeMinutes\": \"10\", \"climateZoneFrontTemp\": \"85\", \"climateZoneFrontAirMode\": \"feet_window\", \"climateZoneFrontAirVolume\": \"7\", \"airConditionOn\": \"false\", \"heatedSeatFrontLeft\": \"high_heat\", \"heatedSeatFrontRight\": \"high_heat\", \"heatedRearWindowActive\": \"true\", \"outerAirCirculation\": \"outsideAir\", \"startConfiguration\": \"START_ENGINE_ALLOW_KEY_IN_IGNITION\", \"canEdit\": \"true\", \"disabled\": \"true\", \"vehicleType\": \"gas\", \"presetType\": \"subaruPreset\" }","{\"name\": \"Full Cool\", \"runTimeMinutes\": \"10\", \"climateZoneFrontTemp\": \"60\", \"climateZoneFrontAirMode\": \"feet_face_balanced\", \"climateZoneFrontAirVolume\": \"7\", \"airConditionOn\": \"true\", \"heatedSeatFrontLeft\": \"OFF\", \"heatedSeatFrontRight\": \"OFF\", \"heatedRearWindowActive\": \"false\", \"outerAirCirculation\": \"outsideAir\", \"startConfiguration\": \"START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION\", \"canEdit\": \"true\", \"disabled\": \"true\", \"vehicleType\": \"phev\", \"presetType\": \"subaruPreset\" }","{\"name\": \"Full Heat\", \"runTimeMinutes\": \"10\", \"climateZoneFrontTemp\": \"85\", \"climateZoneFrontAirMode\": \"feet_window\", \"climateZoneFrontAirVolume\": \"7\", \"airConditionOn\": \"false\", \"heatedSeatFrontLeft\": \"high_heat\", \"heatedSeatFrontRight\": \"high_heat\", \"heatedRearWindowActive\": \"true\", \"outerAirCirculation\": \"outsideAir\", \"startConfiguration\": \"START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION\", \"canEdit\": \"true\", \"disabled\": \"true\", \"vehicleType\": \"phev\", \"presetType\": \"subaruPreset\" }"]}`)
|
||||
}
|
||||
}
|
||||
|
||||
ts := mockMySubaruApi(t, handler)
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
cfg := mockConfig(t)
|
||||
|
||||
msc, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if msc == nil {
|
||||
t.Fatalf("expected MySubaru API client, got nil")
|
||||
}
|
||||
if !msc.isAuthenticated || !msc.isRegistered {
|
||||
t.Errorf("expected authenticated and registered true, got %v %v", msc.isAuthenticated, msc.isRegistered)
|
||||
}
|
||||
if msc.currentVin != "1HGCM82633A004352" {
|
||||
t.Errorf("expected currentVin 1HGCM82633A004352, got %v", msc.currentVin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetClimateQuickPresets_Success tests the retrieval of quick climate presets.
|
||||
func TestGetClimateQuickPresets_Success(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle API_LOGIN endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_LOGIN"] && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":"BIOMETRICS_DISABLED","dataName":"sessionData","data":{"sessionChanged":false,"vehicleInactivated":false,"account":{"marketId":1,"createdDate":1476984644000,"firstName":"Tatiana","lastName":"Savin","zipCode":"07974","accountKey":765268,"lastLoginDate":1751738613000,"zipCode5":"07974"},"resetPassword":false,"deviceId":"JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ","sessionId":"9D7FCDF274794346689D3FA0D693CBBF","deviceRegistered":true,"passwordToken":null,"vehicles":[{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":null,"modelCode":null,"engineSize":null,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"","licensePlateState":"","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":null,"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":null,"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":null,"authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":null,"subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":null,"sunsetUpgraded":true,"intDescrip":null,"transCode":null,"provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}],"rightToRepairEnabled":true,"rightToRepairStartYear":2022,"rightToRepairStates":"MA","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","currentVehicleIndex":0,"handoffToken":"$2a$08$rOb/uqhm8I3QtSel2phOCOxNM51w43eqXDDksMkJ.1a5KsaQuLvEu$1751745334477","satelliteViewEnabled":true,"registeredDevicePermanent":true}}`)
|
||||
}
|
||||
// Handle API_VALIDATE_SESSION endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VALIDATE_SESSION"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":null}`)
|
||||
}
|
||||
// Handle SELECT_VEHICLE endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_SELECT_VEHICLE"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"vehicle","data":{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":"2023","modelCode":"PDL","engineSize":2.4,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"8KV8","licensePlateState":"NJ","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":["REMOTE","SAFETY","Retail3"],"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":["ABS_MIL","ACCS","AHBL_MIL","ATF_MIL","AWD_MIL","BSD","BSDRCT_MIL","CEL_MIL","CP1_5HHU","EBD_MIL","EOL_MIL","EPAS_MIL","EPB_MIL","ESS_MIL","EYESIGHT","ISS_MIL","MOONSTAT","OPL_MIL","PANPM-TUIRWAOC","PWAAADWWAP","RAB_MIL","RCC","REARBRK","RES","RESCC","RES_HVAC_HFS","RES_HVAC_VFS","RHSF","RPOI","RPOIA","RTGU","RVFS","SRH_MIL","SRS_MIL","SXM360L","T23DCM","TEL_MIL","TIF_35","TIR_33","TLD","TPMS_MIL","VALET","VDC_MIL","WASH_MIL","WDWSTAT","g3"],"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":"ACTIVE","authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":"Outback","subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":"Cosmic Blue Pearl","sunsetUpgraded":true,"intDescrip":"Black","transCode":"CVT","provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}}`)
|
||||
}
|
||||
// Handle API_G2_FETCH_RES_QUICK_START_SETTINGS endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_G2_FETCH_RES_QUICK_START_SETTINGS"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":"{\"name\":\"Cooling\",\"runTimeMinutes\":\"10\",\"climateZoneFrontTemp\":\"65\",\"climateZoneFrontAirMode\":\"FEET_FACE_BALANCED\",\"climateZoneFrontAirVolume\":\"7\",\"outerAirCirculation\":\"outsideAir\",\"heatedRearWindowActive\":\"false\",\"heatedSeatFrontLeft\":\"HIGH_COOL\",\"airConditionOn\":\"false\",\"canEdit\":\"true\",\"disabled\":\"false\",\"presetType\":\"userPreset\",\"startConfiguration\":\"START_ENGINE_ALLOW_KEY_IN_IGNITION\"}"}`)
|
||||
}
|
||||
}
|
||||
|
||||
ts := mockMySubaruApi(t, handler)
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
cfg := mockConfig(t)
|
||||
|
||||
msc, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if msc == nil {
|
||||
t.Fatalf("expected MySubaru API client, got nil")
|
||||
}
|
||||
if !msc.isAuthenticated || !msc.isRegistered {
|
||||
t.Errorf("expected authenticated and registered true, got %v %v", msc.isAuthenticated, msc.isRegistered)
|
||||
}
|
||||
if msc.currentVin != "1HGCM82633A004352" {
|
||||
t.Errorf("expected currentVin 1HGCM82633A004352, got %v", msc.currentVin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetClimateUserPresets_Success tests the retrieval of user-defined climate presets.
|
||||
func TestGetClimateUserPresets_Success(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle API_LOGIN endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_LOGIN"] && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":"BIOMETRICS_DISABLED","dataName":"sessionData","data":{"sessionChanged":false,"vehicleInactivated":false,"account":{"marketId":1,"createdDate":1476984644000,"firstName":"Tatiana","lastName":"Savin","zipCode":"07974","accountKey":765268,"lastLoginDate":1751738613000,"zipCode5":"07974"},"resetPassword":false,"deviceId":"JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ","sessionId":"9D7FCDF274794346689D3FA0D693CBBF","deviceRegistered":true,"passwordToken":null,"vehicles":[{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":null,"modelCode":null,"engineSize":null,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"","licensePlateState":"","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":null,"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":null,"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":null,"authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":null,"subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":null,"sunsetUpgraded":true,"intDescrip":null,"transCode":null,"provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}],"rightToRepairEnabled":true,"rightToRepairStartYear":2022,"rightToRepairStates":"MA","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","currentVehicleIndex":0,"handoffToken":"$2a$08$rOb/uqhm8I3QtSel2phOCOxNM51w43eqXDDksMkJ.1a5KsaQuLvEu$1751745334477","satelliteViewEnabled":true,"registeredDevicePermanent":true}}`)
|
||||
}
|
||||
// Handle API_VALIDATE_SESSION endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VALIDATE_SESSION"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":null}`)
|
||||
}
|
||||
// Handle SELECT_VEHICLE endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_SELECT_VEHICLE"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"vehicle","data":{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":"2023","modelCode":"PDL","engineSize":2.4,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"8KV8","licensePlateState":"NJ","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":["REMOTE","SAFETY","Retail3"],"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":["ABS_MIL","ACCS","AHBL_MIL","ATF_MIL","AWD_MIL","BSD","BSDRCT_MIL","CEL_MIL","CP1_5HHU","EBD_MIL","EOL_MIL","EPAS_MIL","EPB_MIL","ESS_MIL","EYESIGHT","ISS_MIL","MOONSTAT","OPL_MIL","PANPM-TUIRWAOC","PWAAADWWAP","RAB_MIL","RCC","REARBRK","RES","RESCC","RES_HVAC_HFS","RES_HVAC_VFS","RHSF","RPOI","RPOIA","RTGU","RVFS","SRH_MIL","SRS_MIL","SXM360L","T23DCM","TEL_MIL","TIF_35","TIR_33","TLD","TPMS_MIL","VALET","VDC_MIL","WASH_MIL","WDWSTAT","g3"],"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":"ACTIVE","authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":"Outback","subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":"Cosmic Blue Pearl","sunsetUpgraded":true,"intDescrip":"Black","transCode":"CVT","provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}}`)
|
||||
}
|
||||
// Handle API_G2_FETCH_RES_USER_PRESETS endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_G2_FETCH_RES_USER_PRESETS"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":"[{\"name\":\"Cooling\",\"runTimeMinutes\":\"10\",\"climateZoneFrontTemp\":\"65\",\"climateZoneFrontAirMode\":\"FEET_FACE_BALANCED\",\"climateZoneFrontAirVolume\":\"7\",\"outerAirCirculation\":\"outsideAir\",\"heatedRearWindowActive\":\"false\",\"heatedSeatFrontLeft\":\"HIGH_COOL\",\"heatedSeatFrontRight\":\"HIGH_COOL\",\"airConditionOn\":\"false\",\"canEdit\":\"true\",\"disabled\":\"false\",\"presetType\":\"userPreset\",\"startConfiguration\":\"START_ENGINE_ALLOW_KEY_IN_IGNITION\"}]"}`)
|
||||
}
|
||||
}
|
||||
|
||||
ts := mockMySubaruApi(t, handler)
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
cfg := mockConfig(t)
|
||||
|
||||
msc, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if msc == nil {
|
||||
t.Fatalf("expected MySubaru API client, got nil")
|
||||
}
|
||||
if !msc.isAuthenticated || !msc.isRegistered {
|
||||
t.Errorf("expected authenticated and registered true, got %v %v", msc.isAuthenticated, msc.isRegistered)
|
||||
}
|
||||
if msc.currentVin != "1HGCM82633A004352" {
|
||||
t.Errorf("expected currentVin 1HGCM82633A004352, got %v", msc.currentVin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetVehicleCondition_Success tests the GetVehicleCondition method
|
||||
func TestGetVehicleStatus_Success(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle API_LOGIN endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_LOGIN"] && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":"BIOMETRICS_DISABLED","dataName":"sessionData","data":{"sessionChanged":false,"vehicleInactivated":false,"account":{"marketId":1,"createdDate":1476984644000,"firstName":"Tatiana","lastName":"Savin","zipCode":"07974","accountKey":765268,"lastLoginDate":1751738613000,"zipCode5":"07974"},"resetPassword":false,"deviceId":"JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ","sessionId":"9D7FCDF274794346689D3FA0D693CBBF","deviceRegistered":true,"passwordToken":null,"vehicles":[{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":null,"modelCode":null,"engineSize":null,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"","licensePlateState":"","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":null,"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":null,"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":null,"authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":null,"subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":null,"sunsetUpgraded":true,"intDescrip":null,"transCode":null,"provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}],"rightToRepairEnabled":true,"rightToRepairStartYear":2022,"rightToRepairStates":"MA","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","currentVehicleIndex":0,"handoffToken":"$2a$08$rOb/uqhm8I3QtSel2phOCOxNM51w43eqXDDksMkJ.1a5KsaQuLvEu$1751745334477","satelliteViewEnabled":true,"registeredDevicePermanent":true}}`)
|
||||
}
|
||||
// Handle API_VALIDATE_SESSION endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VALIDATE_SESSION"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":null}`)
|
||||
}
|
||||
// Handle SELECT_VEHICLE endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_SELECT_VEHICLE"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"vehicle","data":{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":"2023","modelCode":"PDL","engineSize":2.4,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"8KV8","licensePlateState":"NJ","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":["REMOTE","SAFETY","Retail3"],"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":["ABS_MIL","ACCS","AHBL_MIL","ATF_MIL","AWD_MIL","BSD","BSDRCT_MIL","CEL_MIL","CP1_5HHU","EBD_MIL","EOL_MIL","EPAS_MIL","EPB_MIL","ESS_MIL","EYESIGHT","ISS_MIL","MOONSTAT","OPL_MIL","PANPM-TUIRWAOC","PWAAADWWAP","RAB_MIL","RCC","REARBRK","RES","RESCC","RES_HVAC_HFS","RES_HVAC_VFS","RHSF","RPOI","RPOIA","RTGU","RVFS","SRH_MIL","SRS_MIL","SXM360L","T23DCM","TEL_MIL","TIF_35","TIR_33","TLD","TPMS_MIL","VALET","VDC_MIL","WASH_MIL","WDWSTAT","g3"],"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":"ACTIVE","authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":"Outback","subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":"Cosmic Blue Pearl","sunsetUpgraded":true,"intDescrip":"Black","transCode":"CVT","provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}}`)
|
||||
}
|
||||
// Handle API_VEHICLE_STATUS endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VEHICLE_STATUS"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":{"vhsId":14662115789,"odometerValue":31694,"odometerValueKilometers":50996,"eventDate":1751742945000,"eventDateStr":"2025-07-05T19:15+0000","eventDateCarUser":1751742945000,"eventDateStrCarUser":"2025-07-05T19:15+0000","latitude":40.700153,"longitude":-74.401405,"positionHeadingDegree":"154","tirePressureFrontLeft":"2482","tirePressureFrontRight":"2482","tirePressureRearLeft":"2413","tirePressureRearRight":"2482","tirePressureFrontLeftPsi":"36","tirePressureFrontRightPsi":"36","tirePressureRearLeftPsi":"35","tirePressureRearRightPsi":"36","doorBootPosition":"CLOSED","doorEngineHoodPosition":"CLOSED","doorFrontLeftPosition":"CLOSED","doorFrontRightPosition":"CLOSED","doorRearLeftPosition":"CLOSED","doorRearRightPosition":"CLOSED","doorBootLockStatus":"LOCKED","doorFrontLeftLockStatus":"LOCKED","doorFrontRightLockStatus":"LOCKED","doorRearLeftLockStatus":"LOCKED","doorRearRightLockStatus":"LOCKED","distanceToEmptyFuelMiles":259.73,"distanceToEmptyFuelKilometers":418,"avgFuelConsumptionMpg":102.2,"avgFuelConsumptionLitersPer100Kilometers":2.3,"evStateOfChargePercent":null,"evDistanceToEmptyMiles":null,"evDistanceToEmptyKilometers":null,"evDistanceToEmptyByStateMiles":null,"evDistanceToEmptyByStateKilometers":null,"vehicleStateType":"IGNITION_OFF","windowFrontLeftStatus":"CLOSE","windowFrontRightStatus":"CLOSE","windowRearLeftStatus":"CLOSE","windowRearRightStatus":"CLOSE","windowSunroofStatus":"CLOSE","tyreStatusFrontLeft":"UNKNOWN","tyreStatusFrontRight":"UNKNOWN","tyreStatusRearLeft":"UNKNOWN","tyreStatusRearRight":"UNKNOWN","remainingFuelPercent":90,"distanceToEmptyFuelMiles10s":260,"distanceToEmptyFuelKilometers10s":420}}`)
|
||||
}
|
||||
}
|
||||
|
||||
ts := mockMySubaruApi(t, handler)
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
cfg := mockConfig(t)
|
||||
|
||||
msc, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if msc == nil {
|
||||
t.Fatalf("expected MySubaru API client, got nil")
|
||||
}
|
||||
if !msc.isAuthenticated || !msc.isRegistered {
|
||||
t.Errorf("expected authenticated and registered true, got %v %v", msc.isAuthenticated, msc.isRegistered)
|
||||
}
|
||||
if msc.currentVin != "1HGCM82633A004352" {
|
||||
t.Errorf("expected currentVin 1HGCM82633A004352, got %v", msc.currentVin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetVehicleCondition_Success tests the GetVehicleCondition method
|
||||
func TestGetVehicleCondition_Success(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle API_LOGIN endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_LOGIN"] && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":"BIOMETRICS_DISABLED","dataName":"sessionData","data":{"sessionChanged":false,"vehicleInactivated":false,"account":{"marketId":1,"createdDate":1476984644000,"firstName":"Tatiana","lastName":"Savin","zipCode":"07974","accountKey":765268,"lastLoginDate":1751738613000,"zipCode5":"07974"},"resetPassword":false,"deviceId":"JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ","sessionId":"9D7FCDF274794346689D3FA0D693CBBF","deviceRegistered":true,"passwordToken":null,"vehicles":[{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":null,"modelCode":null,"engineSize":null,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"","licensePlateState":"","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":null,"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":null,"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":null,"authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":null,"subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":null,"sunsetUpgraded":true,"intDescrip":null,"transCode":null,"provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}],"rightToRepairEnabled":true,"rightToRepairStartYear":2022,"rightToRepairStates":"MA","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","currentVehicleIndex":0,"handoffToken":"$2a$08$rOb/uqhm8I3QtSel2phOCOxNM51w43eqXDDksMkJ.1a5KsaQuLvEu$1751745334477","satelliteViewEnabled":true,"registeredDevicePermanent":true}}`)
|
||||
}
|
||||
// Handle API_VALIDATE_SESSION endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VALIDATE_SESSION"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":null}`)
|
||||
}
|
||||
// Handle SELECT_VEHICLE endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_SELECT_VEHICLE"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"vehicle","data":{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":"2023","modelCode":"PDL","engineSize":2.4,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"8KV8","licensePlateState":"NJ","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":["REMOTE","SAFETY","Retail3"],"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":["ABS_MIL","ACCS","AHBL_MIL","ATF_MIL","AWD_MIL","BSD","BSDRCT_MIL","CEL_MIL","CP1_5HHU","EBD_MIL","EOL_MIL","EPAS_MIL","EPB_MIL","ESS_MIL","EYESIGHT","ISS_MIL","MOONSTAT","OPL_MIL","PANPM-TUIRWAOC","PWAAADWWAP","RAB_MIL","RCC","REARBRK","RES","RESCC","RES_HVAC_HFS","RES_HVAC_VFS","RHSF","RPOI","RPOIA","RTGU","RVFS","SRH_MIL","SRS_MIL","SXM360L","T23DCM","TEL_MIL","TIF_35","TIR_33","TLD","TPMS_MIL","VALET","VDC_MIL","WASH_MIL","WDWSTAT","g3"],"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":"ACTIVE","authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":"Outback","subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":"Cosmic Blue Pearl","sunsetUpgraded":true,"intDescrip":"Black","transCode":"CVT","provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}}`)
|
||||
}
|
||||
// Handle API_CONDITION endpoint
|
||||
if (r.URL.Path == MOBILE_API_VERSION+urlToGen(apiURLs["API_CONDITION"], "g1") || r.URL.Path == MOBILE_API_VERSION+urlToGen(apiURLs["API_CONDITION"], "g2")) && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"remoteServiceStatus","data":{"serviceRequestId":null,"success":true,"cancelled":false,"remoteServiceType":"condition","remoteServiceState":"finished","subState":null,"errorCode":null,"result":{"avgFuelConsumption":null,"avgFuelConsumptionUnit":"MPG","distanceToEmptyFuel":null,"distanceToEmptyFuelUnit":"MILES","odometer":31692,"odometerUnit":"MILES","tirePressureFrontLeft":null,"tirePressureFrontLeftUnit":"PSI","tirePressureFrontRight":null,"tirePressureFrontRightUnit":"PSI","tirePressureRearLeft":null,"tirePressureRearLeftUnit":"PSI","tirePressureRearRight":null,"tirePressureRearRightUnit":"PSI","lastUpdatedTime":"2025-07-05T19:15:45.000+0000","windowFrontLeftStatus":"CLOSE","windowFrontRightStatus":"CLOSE","windowRearLeftStatus":"CLOSE","windowRearRightStatus":"CLOSE","windowSunroofStatus":"CLOSE","remainingFuelPercent":"90","evDistanceToEmpty":null,"evDistanceToEmptyUnit":null,"evChargerStateType":null,"evIsPluggedIn":null,"evStateOfChargeMode":null,"evTimeToFullyCharged":null,"evStateOfChargePercent":null,"vehicleStateType":"IGNITION_OFF","doorBootLockStatus":"LOCKED","doorBootPosition":"CLOSED","doorEngineHoodPosition":"CLOSED","doorFrontLeftLockStatus":"LOCKED","doorFrontLeftPosition":"CLOSED","doorFrontRightLockStatus":"LOCKED","doorFrontRightPosition":"CLOSED","doorRearLeftLockStatus":"LOCKED","doorRearLeftPosition":"CLOSED","doorRearRightLockStatus":"LOCKED","doorRearRightPosition":"CLOSED"},"updateTime":null,"vin":"1HGCM82633A004352","errorDescription":null}}`)
|
||||
}
|
||||
}
|
||||
|
||||
ts := mockMySubaruApi(t, handler)
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
cfg := mockConfig(t)
|
||||
|
||||
msc, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if msc == nil {
|
||||
t.Fatalf("expected MySubaru API client, got nil")
|
||||
}
|
||||
if !msc.isAuthenticated || !msc.isRegistered {
|
||||
t.Errorf("expected authenticated and registered true, got %v %v", msc.isAuthenticated, msc.isRegistered)
|
||||
}
|
||||
if msc.currentVin != "1HGCM82633A004352" {
|
||||
t.Errorf("expected currentVin 1HGCM82633A004352, got %v", msc.currentVin)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetVehicleHealth_Success tests the successful retrieval of vehicle health data.
|
||||
func TestGetVehicleHealth_Success(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle API_LOGIN endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_LOGIN"] && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":"BIOMETRICS_DISABLED","dataName":"sessionData","data":{"sessionChanged":false,"vehicleInactivated":false,"account":{"marketId":1,"createdDate":1476984644000,"firstName":"Tatiana","lastName":"Savin","zipCode":"07974","accountKey":765268,"lastLoginDate":1751738613000,"zipCode5":"07974"},"resetPassword":false,"deviceId":"JddMBQXvAkgutSmEP6uFsThbq4QgEBBQ","sessionId":"9D7FCDF274794346689D3FA0D693CBBF","deviceRegistered":true,"passwordToken":null,"vehicles":[{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":null,"modelCode":null,"engineSize":null,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"","licensePlateState":"","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":null,"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":null,"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":null,"authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":null,"subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":null,"sunsetUpgraded":true,"intDescrip":null,"transCode":null,"provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}],"rightToRepairEnabled":true,"rightToRepairStartYear":2022,"rightToRepairStates":"MA","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","currentVehicleIndex":0,"handoffToken":"$2a$08$rOb/uqhm8I3QtSel2phOCOxNM51w43eqXDDksMkJ.1a5KsaQuLvEu$1751745334477","satelliteViewEnabled":true,"registeredDevicePermanent":true}}`)
|
||||
}
|
||||
// Handle API_VALIDATE_SESSION endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VALIDATE_SESSION"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":null}`)
|
||||
}
|
||||
// Handle SELECT_VEHICLE endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_SELECT_VEHICLE"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":"vehicle","data":{"customer":{"sessionCustomer":null,"email":null,"firstName":null,"lastName":null,"zip":null,"oemCustId":null,"phone":null},"vehicleName":"Subaru Outback TXT","stolenVehicle":false,"vin":"1HGCM82633A004352","modelYear":"2023","modelCode":"PDL","engineSize":2.4,"nickname":"Subaru Outback TXT","vehicleKey":8211380,"active":true,"licensePlate":"8KV8","licensePlateState":"NJ","email":null,"firstName":null,"lastName":null,"subscriptionFeatures":["REMOTE","SAFETY","Retail3"],"accessLevel":-1,"zip":null,"oemCustId":"CRM-41PLM-5TYE","vehicleMileage":null,"phone":null,"timeZone":"America/New_York","features":["ABS_MIL","ACCS","AHBL_MIL","ATF_MIL","AWD_MIL","BSD","BSDRCT_MIL","CEL_MIL","CP1_5HHU","EBD_MIL","EOL_MIL","EPAS_MIL","EPB_MIL","ESS_MIL","EYESIGHT","ISS_MIL","MOONSTAT","OPL_MIL","PANPM-TUIRWAOC","PWAAADWWAP","RAB_MIL","RCC","REARBRK","RES","RESCC","RES_HVAC_HFS","RES_HVAC_VFS","RHSF","RPOI","RPOIA","RTGU","RVFS","SRH_MIL","SRS_MIL","SXM360L","T23DCM","TEL_MIL","TIF_35","TIR_33","TLD","TPMS_MIL","VALET","VDC_MIL","WASH_MIL","WDWSTAT","g3"],"userOemCustId":"CRM-41PLM-5TYE","subscriptionStatus":"ACTIVE","authorizedVehicle":false,"preferredDealer":null,"cachedStateCode":"NJ","modelName":"Outback","subscriptionPlans":[],"crmRightToRepair":false,"needMileagePrompt":false,"phev":null,"extDescrip":"Cosmic Blue Pearl","sunsetUpgraded":true,"intDescrip":"Black","transCode":"CVT","provisioned":true,"remoteServicePinExist":true,"needEmergencyContactPrompt":false,"vehicleGeoPosition":null,"show3gSunsetBanner":false}}`)
|
||||
}
|
||||
// Handle API_VEHICLE_HEALTH endpoint
|
||||
if r.URL.Path == MOBILE_API_VERSION+apiURLs["API_VEHICLE_HEALTH"] && r.Method == http.MethodGet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, `{"success":true,"errorCode":null,"dataName":null,"data":{"lastUpdatedDate":1751742945000,"vehicleHealthItems":[{"warningCode":10,"b2cCode":"airbag","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"SRS_MIL"},{"warningCode":4,"b2cCode":"oilTemp","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"ATF_MIL"},{"warningCode":39,"b2cCode":"blindspot","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"BSDRCT_MIL"},{"warningCode":2,"b2cCode":"engineFail","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"CEL_MIL"},{"warningCode":44,"b2cCode":"pkgBrake","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"EPB_MIL"},{"warningCode":8,"b2cCode":"ebd","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"EBD_MIL"},{"warningCode":3,"b2cCode":"oilWarning","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"EOL_MIL"},{"warningCode":1,"b2cCode":"washer","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"WASH_MIL"},{"warningCode":50,"b2cCode":"iss","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"ISS_MIL"},{"warningCode":53,"b2cCode":"oilPres","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"OPL_MIL"},{"warningCode":11,"b2cCode":"epas","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"EPAS_MIL"},{"warningCode":69,"b2cCode":"revBrake","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"RAB_MIL"},{"warningCode":14,"b2cCode":"telematics","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"TEL_MIL"},{"warningCode":9,"b2cCode":"tpms","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"TPMS_MIL"},{"warningCode":7,"b2cCode":"vdc","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"VDC_MIL"},{"warningCode":6,"b2cCode":"abs","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"ABS_MIL"},{"warningCode":5,"b2cCode":"awd","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"AWD_MIL"},{"warningCode":12,"b2cCode":"eyesight","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"ESS_MIL"},{"warningCode":30,"b2cCode":"ahbl","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"AHBL_MIL"},{"warningCode":31,"b2cCode":"srh","isTrouble":false,"onDates":[],"onDaiId":0,"featureCode":"SRH_MIL"}]}}`)
|
||||
}
|
||||
}
|
||||
|
||||
ts := mockMySubaruApi(t, handler)
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
cfg := mockConfig(t)
|
||||
|
||||
msc, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if msc == nil {
|
||||
t.Fatalf("expected MySubaru API client, got nil")
|
||||
}
|
||||
if !msc.isAuthenticated || !msc.isRegistered {
|
||||
t.Errorf("expected authenticated and registered true, got %v %v", msc.isAuthenticated, msc.isRegistered)
|
||||
}
|
||||
if msc.currentVin != "1HGCM82633A004352" {
|
||||
t.Errorf("expected currentVin 1HGCM82633A004352, got %v", msc.currentVin)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user