Refactor session validation and enhance error handling in vehicle commands
Some checks failed
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Failing after 24s
Some checks failed
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Failing after 24s
This commit is contained in:
30
client.go
30
client.go
@ -437,8 +437,8 @@ func (c *Client) parseResponse(b []byte) (Response, bool) {
|
||||
}
|
||||
|
||||
// ValidateSession checks if the current session is valid by making a request to the vehicle status API.
|
||||
func (c *Client) ValidateSession() bool {
|
||||
reqURL := MOBILE_API_VERSION + apiURLs["API_VEHICLE_STATUS"]
|
||||
func (c *Client) validateSession() bool {
|
||||
reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"]
|
||||
resp, err := c.execute(GET, reqURL, map[string]string{}, false)
|
||||
if err != nil {
|
||||
c.logger.Error("error while executing validateSession request", "request", "validateSession", "error", err.Error())
|
||||
@ -446,9 +446,35 @@ func (c *Client) ValidateSession() bool {
|
||||
}
|
||||
c.logger.Debug("http request output", "request", "validateSession", "body", resp)
|
||||
|
||||
if resp.Success {
|
||||
_, err := c.SelectVehicle(c.currentVin)
|
||||
if err != nil {
|
||||
c.logger.Error("error while selecting vehicle", "request", "validateSession", "error", err.Error())
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !resp.Success {
|
||||
_, err := c.auth()
|
||||
if err != nil {
|
||||
c.logger.Error("error while re-authenticating", "request", "validateSession", "error", err.Error())
|
||||
return false
|
||||
}
|
||||
_, err = c.SelectVehicle(c.currentVin)
|
||||
if err != nil {
|
||||
c.logger.Error("error while selecting vehicle", "request", "validateSession", "error", err.Error())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// isPINRequired .
|
||||
// Return if a vehicle with an active remote service subscription exists.
|
||||
// func (v *Vehicle) isPINRequired() bool {
|
||||
// return v.getRemoteOptionsStatus()
|
||||
// }
|
||||
|
||||
// func isPINRequired() {}
|
||||
// func getEVStatus() {}
|
||||
// func getRemoteOptionsStatus() {}
|
||||
|
35
mysubaru.go
35
mysubaru.go
@ -377,6 +377,28 @@ type ServiceRequest struct {
|
||||
UpdateTime UnixTime `json:"updateTime,omitempty"` // timestamp // is empty if the request is started
|
||||
}
|
||||
|
||||
// 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 ]
|
||||
// climateZoneFrontTempCelsius: [for _ in range(15, 30 + 1)]
|
||||
// climateZoneFrontTemp: [for _ in range(60, 85 + 1)]
|
||||
@ -395,12 +417,12 @@ type VehicleHealth struct {
|
||||
LastUpdatedDate int64 `json:"lastUpdatedDate"`
|
||||
}
|
||||
type VehicleHealthItem struct {
|
||||
B2cCode string `json:"b2cCode"`
|
||||
FeatureCode string `json:"featureCode"`
|
||||
IsTrouble bool `json:"isTrouble"`
|
||||
OnDaiID int `json:"onDaiId"` // Has a number, probably id, but I couldn't find it purpose
|
||||
OnDates []int64 `json:"onDates,omitempty"` // List of the timestamps
|
||||
WarningCode int `json:"warningCode"`
|
||||
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 .
|
||||
@ -454,6 +476,7 @@ type CustomTime2 struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (ct *CustomTime2) UnmarshalJSON(b []byte) (err error) {
|
||||
const layout = "2006-01-02T15:04:05-0700"
|
||||
ct.Time, err = time.Parse(layout, string(b)) // Parse the string using the custom layout
|
||||
|
143
vehicle.go
143
vehicle.go
@ -214,11 +214,6 @@ func (v *Vehicle) String() string {
|
||||
// Lock
|
||||
// Sends a command to lock doors.
|
||||
func (v *Vehicle) Lock() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -239,11 +234,6 @@ func (v *Vehicle) Lock() (chan string, error) {
|
||||
// Unlock
|
||||
// Send command to unlock doors.
|
||||
func (v *Vehicle) Unlock() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -264,11 +254,6 @@ func (v *Vehicle) Unlock() (chan string, error) {
|
||||
// EngineStart
|
||||
// Sends a command to start engine and set climate control.
|
||||
func (v *Vehicle) EngineStart() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// TODO: Get Quick Climate Preset from the Currect Car
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
@ -302,11 +287,6 @@ func (v *Vehicle) EngineStart() (chan string, error) {
|
||||
// EngineStop
|
||||
// Sends a command to stop engine.
|
||||
func (v *Vehicle) EngineStop() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -326,11 +306,6 @@ func (v *Vehicle) EngineStop() (chan string, error) {
|
||||
// LightsStart
|
||||
// Sends a command to flash lights.
|
||||
func (v *Vehicle) LightsStart() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -353,11 +328,6 @@ func (v *Vehicle) LightsStart() (chan string, error) {
|
||||
// LightsStop
|
||||
// Sends a command to stop flash lights.
|
||||
func (v *Vehicle) LightsStop() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -380,11 +350,6 @@ func (v *Vehicle) LightsStop() (chan string, error) {
|
||||
// HornStart
|
||||
// Send command to sound horn.
|
||||
func (v *Vehicle) HornStart() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -407,11 +372,6 @@ func (v *Vehicle) HornStart() (chan string, error) {
|
||||
// HornStop
|
||||
// Send command to sound horn.
|
||||
func (v *Vehicle) HornStop() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
"vin": v.Vin,
|
||||
@ -433,10 +393,6 @@ func (v *Vehicle) HornStop() (chan string, error) {
|
||||
|
||||
// ChargeStart
|
||||
func (v *Vehicle) ChargeOn() (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
if v.isEV() {
|
||||
params := map[string]string{
|
||||
"delay": "0",
|
||||
@ -456,11 +412,6 @@ func (v *Vehicle) ChargeOn() (chan string, error) {
|
||||
|
||||
// GetLocation
|
||||
func (v *Vehicle) GetLocation(force bool) (chan string, error) {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return nil, errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
var reqUrl, pollingUrl string
|
||||
var params map[string]string
|
||||
if force { // Sends a locate command to the vehicle to get real time position
|
||||
@ -498,6 +449,13 @@ func (v *Vehicle) GetClimatePresets() error {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != (v.client).currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -557,6 +515,13 @@ func (v *Vehicle) GetClimateQuickPresets() error {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != (v.client).currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -593,6 +558,13 @@ func (v *Vehicle) GetClimateUserPresets() error {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != (v.client).currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -638,6 +610,13 @@ func (v *Vehicle) GetVehicleStatus() error {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != (v.client).currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -699,6 +678,13 @@ func (v *Vehicle) GetVehicleCondition() error {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != (v.client).currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -742,12 +728,20 @@ func (v *Vehicle) GetVehicleCondition() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetVehicleHealth .
|
||||
// GetVehicleHealth
|
||||
// Retrieves the vehicle health status from MySubaru API.
|
||||
func (v *Vehicle) GetVehicleHealth() error {
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != (v.client).currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -795,13 +789,24 @@ func (v *Vehicle) GetFeaturesList() {
|
||||
// Executes a service request to the Subaru API and handles the response.
|
||||
func (v *Vehicle) executeServiceRequest(params map[string]string, reqUrl, pollingUrl string, ch chan string, attempt int) error {
|
||||
var maxAttempts = 15
|
||||
|
||||
if attempt >= maxAttempts {
|
||||
v.client.logger.Error("maximum attempts reached for service request", "request", reqUrl, "attempts", attempt)
|
||||
ch <- "error"
|
||||
return errors.New("maximum attempts reached for service request")
|
||||
}
|
||||
|
||||
// Check if the vehicle has a valid subscription for remote services
|
||||
if !v.getRemoteOptionsStatus() {
|
||||
v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
return errors.New(APP_ERRORS["SUBSCRIBTION_REQUIRED"])
|
||||
}
|
||||
|
||||
// Validate session before executing the request
|
||||
if v.client.validateSession() {
|
||||
v.client.logger.Error(APP_ERRORS["SESSION_EXPIRED"])
|
||||
return errors.New(APP_ERRORS["SESSION_EXPIRED"])
|
||||
}
|
||||
|
||||
if v.Vin != v.client.currentVin {
|
||||
v.selectVehicle()
|
||||
}
|
||||
@ -836,16 +841,16 @@ func (v *Vehicle) executeServiceRequest(params map[string]string, reqUrl, pollin
|
||||
|
||||
case "started":
|
||||
time.Sleep(5 * time.Second)
|
||||
v.client.logger.Debug("Subaru API reports remote service request (started) is in progress", "id", sr.ServiceRequestID)
|
||||
v.client.logger.Debug("MySubaru API reports remote service request (started) is in progress", "id", sr.ServiceRequestID)
|
||||
v.executeServiceRequest(map[string]string{"serviceRequestId": sr.ServiceRequestID}, reqUrl, pollingUrl, ch, attempt+1)
|
||||
|
||||
case "stopping":
|
||||
time.Sleep(5 * time.Second)
|
||||
v.client.logger.Debug("Subaru API reports remote service request (stopping) is in progress", "id", sr.ServiceRequestID)
|
||||
v.client.logger.Debug("MySubaru API reports remote service request (stopping) is in progress", "id", sr.ServiceRequestID)
|
||||
v.executeServiceRequest(map[string]string{"serviceRequestId": sr.ServiceRequestID}, reqUrl, pollingUrl, ch, attempt+1)
|
||||
|
||||
default:
|
||||
v.client.logger.Debug("Subaru API reports remote service request (default)")
|
||||
v.client.logger.Debug("MySubaru API reports remote service request (default)")
|
||||
v.executeServiceRequest(map[string]string{"serviceRequestId": sr.ServiceRequestID}, reqUrl, pollingUrl, ch, attempt+1)
|
||||
}
|
||||
return nil
|
||||
@ -899,12 +904,6 @@ func (v *Vehicle) getAPIGen() string {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// isPINRequired .
|
||||
// Return if a vehicle with an active remote service subscription exists.
|
||||
// func (v *Vehicle) isPINRequired() bool {
|
||||
// return v.getRemoteOptionsStatus()
|
||||
// }
|
||||
|
||||
// isEV .
|
||||
// Get whether the specified car is an Electric Vehicle.
|
||||
func (v *Vehicle) isEV() bool {
|
||||
@ -1022,27 +1021,3 @@ func (v *Vehicle) parseParts(name string, value any) {
|
||||
// func (v *Vehicle) getSubscriptionStatus() bool {
|
||||
// return slices.Contains(v.SubscriptionFeatures, FEATURE_ACTIVE)
|
||||
// }
|
||||
|
||||
// // getVehicleName .
|
||||
// // Get the nickname of a specified VIN.
|
||||
// func (v *Vehicle) getVehicleName() string {
|
||||
// return v.CarName
|
||||
// }
|
||||
|
||||
// func getClimateData() {}
|
||||
// func saveClimateSettings() {}
|
||||
|
||||
// "vhsId": 914631252,
|
||||
|
||||
// "odometerValue": 23865,
|
||||
// "odometerValueKilometers": 38399,
|
||||
|
||||
// "tirePressureFrontLeft": "2344",
|
||||
// "tirePressureFrontRight": "2344",
|
||||
// "tirePressureRearLeft": "2413",
|
||||
// "tirePressureRearRight": "2344",
|
||||
|
||||
// "tirePressureFrontLeftPsi": "34",
|
||||
// "tirePressureFrontRightPsi": "34",
|
||||
// "tirePressureRearLeftPsi": "35",
|
||||
// "tirePressureRearRightPsi": "34",
|
||||
|
Reference in New Issue
Block a user