Porsche: remove deprecated mobile api (#8349)
This commit is contained in:
parent
44c17a60f9
commit
2861266fd4
6 changed files with 26 additions and 276 deletions
|
|
@ -48,16 +48,8 @@ func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
}
|
||||
|
||||
api := porsche.NewAPI(log, identity.DefaultSource)
|
||||
mobile := porsche.NewMobileAPI(log, identity.MobileSource)
|
||||
|
||||
cc.VIN, err = ensureVehicle(cc.VIN, func() ([]string, error) {
|
||||
mobileVehicles, err := mobile.Vehicles()
|
||||
if err == nil {
|
||||
return lo.Map(mobileVehicles, func(v porsche.StatusResponseMobile, _ int) string {
|
||||
return v.VIN
|
||||
}), err
|
||||
}
|
||||
|
||||
vehicles, err := api.Vehicles()
|
||||
return lo.Map(vehicles, func(v porsche.Vehicle, _ int) string {
|
||||
return v.VIN
|
||||
|
|
@ -80,7 +72,7 @@ func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
provider := porsche.NewProvider(log, api, emobility, mobile, cc.VIN, capabilities.CarModel, cc.Cache)
|
||||
provider := porsche.NewProvider(log, api, emobility, cc.VIN, capabilities.CarModel, cc.Cache)
|
||||
|
||||
v := &Porsche{
|
||||
embed: &cc.embed,
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ func NewAPI(log *util.Logger, identity oauth2.TokenSource) *API {
|
|||
Base: v.Client.Transport,
|
||||
},
|
||||
Decorator: transport.DecorateHeaders(map[string]string{
|
||||
"apikey": OAuth2Config.ClientID,
|
||||
"x-client-id": "52064df8-6daa-46f7-bc9e-e3232622ab26",
|
||||
"apikey": OAuth2Config.ClientID,
|
||||
}),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func NewEmobilityAPI(log *util.Logger, identity oauth2.TokenSource) *EmobilityAP
|
|||
|
||||
func (v *EmobilityAPI) Capabilities(vin string) (CapabilitiesResponse, error) {
|
||||
var res CapabilitiesResponse
|
||||
uri := fmt.Sprintf("https://api.porsche.com/e-mobility/vcs/capabilities/%s", vin)
|
||||
uri := fmt.Sprintf("%s/e-mobility/vcs/capabilities/%s", ApiURI, vin)
|
||||
err := v.GetJSON(uri, &res)
|
||||
return res, err
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ func (v *EmobilityAPI) Capabilities(vin string) (CapabilitiesResponse, error) {
|
|||
func (v *EmobilityAPI) Status(vin, model string) (EmobilityResponse, error) {
|
||||
var res EmobilityResponse
|
||||
|
||||
uri := fmt.Sprintf("https://api.porsche.com/e-mobility/de/de_DE/%s/%s?timezone=Europe/Berlin", model, vin)
|
||||
uri := fmt.Sprintf("%s/e-mobility/de/de_DE/%s/%s?timezone=Europe/Berlin", ApiURI, model, vin)
|
||||
err := v.GetJSON(uri, &res)
|
||||
if err != nil && res.PcckErrorMessage != "" {
|
||||
err = errors.New(res.PcckErrorMessage)
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
package porsche
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/evcc-io/evcc/util/transport"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const (
|
||||
MobileApiURI = "https://api.ppa.porsche.com"
|
||||
|
||||
// vehicle status elements
|
||||
ACV_STATE = "ACV_STATE"
|
||||
BATTERY_CHARGING_STATE = "BATTERY_CHARGING_STATE"
|
||||
BATTERY_LEVEL = "BATTERY_LEVEL"
|
||||
BATTERY_TYPE = "BATTERY_TYPE"
|
||||
BLEID_DDADATA = "BLEID_DDADATA"
|
||||
CAR_ALARMS_HISTORY = "CAR_ALARMS_HISTORY"
|
||||
CHARGING_PROFILES = "CHARGING_PROFILES"
|
||||
CLIMATIZER_STATE = "CLIMATIZER_STATE"
|
||||
E_RANGE = "E_RANGE"
|
||||
FUEL_LEVEL = "FUEL_LEVEL"
|
||||
FUEL_RESERVE = "FUEL_RESERVE"
|
||||
GLOBAL_PRIVACY_MODE = "GLOBAL_PRIVACY_MODE"
|
||||
GPS_LOCATION = "GPS_LOCATION"
|
||||
HEATING_STATE = "HEATING_STATE"
|
||||
INTERMEDIATE_SERVICE_RANGE = "INTERMEDIATE_SERVICE_RANGE"
|
||||
INTERMEDIATE_SERVICE_TIME = "INTERMEDIATE_SERVICE_TIME"
|
||||
LOCATION_ALARMS = "LOCATION_ALARMS"
|
||||
LOCATION_ALARMS_HISTORY = "LOCATION_ALARMS_HISTORY"
|
||||
LOCK_STATE_VEHICLE = "LOCK_STATE_VEHICLE"
|
||||
MAIN_SERVICE_RANGE = "MAIN_SERVICE_RANGE"
|
||||
MAIN_SERVICE_TIME = "MAIN_SERVICE_TIME"
|
||||
MILEAGE = "MILEAGE"
|
||||
OIL_LEVEL_CURRENT = "OIL_LEVEL_CURRENT"
|
||||
OIL_LEVEL_MAX = "OIL_LEVEL_MAX"
|
||||
OIL_LEVEL_MIN_WARNING = "OIL_LEVEL_MIN_WARNING"
|
||||
OIL_SERVICE_RANGE = "OIL_SERVICE_RANGE"
|
||||
OIL_SERVICE_TIME = "OIL_SERVICE_TIME"
|
||||
OPEN_STATE_CHARGE_FLAP_LEFT = "OPEN_STATE_CHARGE_FLAP_LEFT"
|
||||
OPEN_STATE_CHARGE_FLAP_RIGHT = "OPEN_STATE_CHARGE_FLAP_RIGHT"
|
||||
OPEN_STATE_DOOR_FRONT_LEFT = "OPEN_STATE_DOOR_FRONT_LEFT"
|
||||
OPEN_STATE_DOOR_FRONT_RIGHT = "OPEN_STATE_DOOR_FRONT_RIGHT"
|
||||
OPEN_STATE_DOOR_REAR_LEFT = "OPEN_STATE_DOOR_REAR_LEFT"
|
||||
OPEN_STATE_DOOR_REAR_RIGHT = "OPEN_STATE_DOOR_REAR_RIGHT"
|
||||
OPEN_STATE_LID_FRONT = "OPEN_STATE_LID_FRONT"
|
||||
OPEN_STATE_LID_REAR = "OPEN_STATE_LID_REAR"
|
||||
OPEN_STATE_SERVICE_FLAP = "OPEN_STATE_SERVICE_FLAP"
|
||||
OPEN_STATE_SPOILER = "OPEN_STATE_SPOILER"
|
||||
OPEN_STATE_SUNROOF = "OPEN_STATE_SUNROOF"
|
||||
OPEN_STATE_TOP = "OPEN_STATE_TOP"
|
||||
OPEN_STATE_WINDOW_FRONT_LEFT = "OPEN_STATE_WINDOW_FRONT_LEFT"
|
||||
OPEN_STATE_WINDOW_FRONT_RIGHT = "OPEN_STATE_WINDOW_FRONT_RIGHT"
|
||||
OPEN_STATE_WINDOW_REAR_LEFT = "OPEN_STATE_WINDOW_REAR_LEFT"
|
||||
OPEN_STATE_WINDOW_REAR_RIGHT = "OPEN_STATE_WINDOW_REAR_RIGHT"
|
||||
PARKING_LIGHT = "PARKING_LIGHT"
|
||||
RANGE = "RANGE"
|
||||
REMOTE_ACCESS_AUTHORIZATION = "REMOTE_ACCESS_AUTHORIZATION"
|
||||
SERVICE_PREDICTIONS = "SERVICE_PREDICTIONS"
|
||||
SPEED_ALARMS = "SPEED_ALARMS"
|
||||
SPEED_ALARMS_HISTORY = "SPEED_ALARMS_HISTORY"
|
||||
THEFT_MODE = "THEFT_MODE"
|
||||
TIMERS = "TIMERS"
|
||||
TRIP_STATISTICS_CYCLIC = "TRIP_STATISTICS_CYCLIC"
|
||||
TRIP_STATISTICS_LONG_TERM = "TRIP_STATISTICS_LONG_TERM"
|
||||
TRIP_STATISTICS_SHORT_TERM = "TRIP_STATISTICS_SHORT_TERM"
|
||||
VALET_ALARM = "VALET_ALARM"
|
||||
VALET_ALARM_HISTORY = "VALET_ALARM_HISTORY"
|
||||
VTS_MODES = "VTS_MODES"
|
||||
)
|
||||
|
||||
// API is an api.Vehicle implementation for Porsche PHEV cars
|
||||
type MobileAPI struct {
|
||||
*request.Helper
|
||||
}
|
||||
|
||||
// NewAPI creates a new vehicle
|
||||
func NewMobileAPI(log *util.Logger, identity oauth2.TokenSource) *MobileAPI {
|
||||
v := &MobileAPI{
|
||||
Helper: request.NewHelper(log),
|
||||
}
|
||||
|
||||
v.Client.Transport = &transport.Decorator{
|
||||
Base: &oauth2.Transport{
|
||||
Source: identity,
|
||||
Base: v.Client.Transport,
|
||||
},
|
||||
Decorator: transport.DecorateHeaders(map[string]string{
|
||||
"apikey": OAuth2Config.ClientID,
|
||||
"x-client-id": "52064df8-6daa-46f7-bc9e-e3232622ab26",
|
||||
}),
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Vehicles implements the vehicle list response
|
||||
func (v *MobileAPI) Vehicles() ([]StatusResponseMobile, error) {
|
||||
var res []StatusResponseMobile
|
||||
uri := fmt.Sprintf("%s/app/connect/v1/vehicles", MobileApiURI)
|
||||
err := v.GetJSON(uri, &res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Status implements the vehicle status response
|
||||
func (v *MobileAPI) Status(vin string, items []string) (StatusResponseMobile, error) {
|
||||
var res StatusResponseMobile
|
||||
|
||||
mf := make([]string, 0, len(items))
|
||||
for _, i := range items {
|
||||
mf = append(mf, "mf="+i)
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/app/connect/v1/vehicles/%s?%s", MobileApiURI, vin, strings.Join(mf, "&"))
|
||||
err := v.GetJSON(uri, &res)
|
||||
return res, err
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/oauth"
|
||||
|
|
@ -39,21 +38,14 @@ var (
|
|||
Endpoint: OAuth2Config.Endpoint,
|
||||
Scopes: []string{"openid"},
|
||||
}
|
||||
|
||||
MobileOAuth2Config = &oauth2.Config{
|
||||
ClientID: "L20OiZ0kBgWt958NWbuCB8gb970y6V6U",
|
||||
RedirectURL: "One-Product-App://porsche-id/oauth2redirect",
|
||||
Endpoint: OAuth2Config.Endpoint,
|
||||
Scopes: []string{"openid", "magiclink", "mbb"},
|
||||
}
|
||||
)
|
||||
|
||||
// Identity is the Porsche Identity client
|
||||
type Identity struct {
|
||||
*request.Helper
|
||||
user, password string
|
||||
defaultToken, emobilityToken, mobileToken *oauth2.Token
|
||||
DefaultSource, EmobilitySource, MobileSource oauth2.TokenSource
|
||||
user, password string
|
||||
defaultToken, emobilityToken *oauth2.Token
|
||||
DefaultSource, EmobilitySource oauth2.TokenSource
|
||||
}
|
||||
|
||||
// NewIdentity creates Porsche identity
|
||||
|
|
@ -73,7 +65,6 @@ func (v *Identity) Login() error {
|
|||
if err == nil {
|
||||
v.DefaultSource = oauth.RefreshTokenSource(v.defaultToken, v)
|
||||
v.EmobilitySource = oauth.RefreshTokenSource(v.emobilityToken, &emobilityAdapter{v})
|
||||
v.MobileSource = oauth.RefreshTokenSource(v.mobileToken, &mobileAdapter{v})
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
@ -98,9 +89,19 @@ func (v *Identity) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error) {
|
|||
v.Client.CheckRedirect = nil
|
||||
}()
|
||||
|
||||
preLogin := url.Values{
|
||||
"sec": []string{""},
|
||||
"resume": []string{""},
|
||||
"thirdPartyId": []string{""},
|
||||
"state": []string{""},
|
||||
"username": []string{v.user},
|
||||
"password": []string{v.password},
|
||||
"keeploggedin": []string{"false"},
|
||||
}
|
||||
|
||||
// get the login page
|
||||
uri := fmt.Sprintf("%s/auth/api/v1/de/de_DE/public/login", OAuthURI)
|
||||
resp, err := v.Get(uri)
|
||||
resp, err := v.PostForm(uri, preLogin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -111,28 +112,19 @@ func (v *Identity) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
sec := query.Get("sec")
|
||||
resume := query.Get("resume")
|
||||
state := query.Get("state")
|
||||
thirdPartyID := query.Get("thirdPartyId")
|
||||
|
||||
dataLoginAuth := url.Values{
|
||||
"sec": []string{sec},
|
||||
"resume": []string{resume},
|
||||
"thirdPartyId": []string{thirdPartyID},
|
||||
"state": []string{state},
|
||||
"sec": []string{query.Get("sec")},
|
||||
"resume": []string{query.Get("resume")},
|
||||
"thirdPartyId": []string{query.Get("thirdPartyID")},
|
||||
"state": []string{query.Get("state")},
|
||||
"username": []string{v.user},
|
||||
"password": []string{v.password},
|
||||
"keeploggedin": []string{"false"},
|
||||
}
|
||||
|
||||
req, err := request.New(http.MethodPost, uri, strings.NewReader(dataLoginAuth.Encode()), request.URLEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// process the auth so the session is authenticated
|
||||
if resp, err = v.Client.Do(req); err != nil {
|
||||
resp, err = v.PostForm(uri, dataLoginAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
|
@ -145,10 +137,6 @@ func (v *Identity) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error) {
|
|||
if token, err = v.fetchToken(EmobilityOAuth2Config); err == nil {
|
||||
v.emobilityToken = token
|
||||
}
|
||||
|
||||
if token, err = v.fetchToken(MobileOAuth2Config); err == nil {
|
||||
v.mobileToken = token
|
||||
}
|
||||
}
|
||||
|
||||
return v.defaultToken, err
|
||||
|
|
@ -180,11 +168,7 @@ func (v *Identity) fetchToken(oc *oauth2.Config) (*oauth2.Token, error) {
|
|||
}
|
||||
resp.Body.Close()
|
||||
|
||||
rawQuery := resp.Request.URL.RawQuery
|
||||
if location, ok := strings.CutPrefix(resp.Header.Get("Location"), "One-Product-App://porsche-id/oauth2redirect?"); ok {
|
||||
rawQuery = location
|
||||
}
|
||||
query, err := url.ParseQuery(rawQuery)
|
||||
query, err := url.ParseQuery(resp.Request.URL.RawQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -218,15 +202,3 @@ func (v *emobilityAdapter) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error)
|
|||
}
|
||||
return token, err
|
||||
}
|
||||
|
||||
type mobileAdapter struct {
|
||||
tr *Identity
|
||||
}
|
||||
|
||||
func (v *mobileAdapter) RefreshToken(_ *oauth2.Token) (*oauth2.Token, error) {
|
||||
token, err := v.tr.RefreshToken(nil)
|
||||
if err == nil {
|
||||
token = v.tr.mobileToken
|
||||
}
|
||||
return token, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@ import (
|
|||
type Provider struct {
|
||||
statusG func() (StatusResponse, error)
|
||||
emobilityG func() (EmobilityResponse, error)
|
||||
mobileG func() (StatusResponseMobile, error)
|
||||
wakeup func() error
|
||||
}
|
||||
|
||||
// NewProvider creates a vehicle api provider
|
||||
func NewProvider(log *util.Logger, api *API, emobility *EmobilityAPI, mobile *MobileAPI, vin, carModel string, cache time.Duration) *Provider {
|
||||
func NewProvider(log *util.Logger, api *API, emobility *EmobilityAPI, vin, carModel string, cache time.Duration) *Provider {
|
||||
impl := &Provider{
|
||||
statusG: provider.Cached(func() (StatusResponse, error) {
|
||||
return api.Status(vin)
|
||||
|
|
@ -31,16 +30,11 @@ func NewProvider(log *util.Logger, api *API, emobility *EmobilityAPI, mobile *Mo
|
|||
return EmobilityResponse{}, errors.New("no car model")
|
||||
}, cache),
|
||||
|
||||
mobileG: provider.Cached(func() (StatusResponseMobile, error) {
|
||||
return mobile.Status(vin, []string{BATTERY_LEVEL, BATTERY_CHARGING_STATE, CLIMATIZER_STATE, E_RANGE, HEATING_STATE, MILEAGE})
|
||||
}, cache),
|
||||
|
||||
wakeup: func() error {
|
||||
_, _ = api.Status(vin)
|
||||
if carModel != "" {
|
||||
_, _ = emobility.Status(vin, carModel)
|
||||
}
|
||||
_, _ = mobile.Status(vin, []string{BATTERY_LEVEL})
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -52,17 +46,6 @@ var _ api.Battery = (*Provider)(nil)
|
|||
|
||||
// Soc implements the api.Vehicle interface
|
||||
func (v *Provider) Soc() (float64, error) {
|
||||
res, err := v.mobileG()
|
||||
if err == nil {
|
||||
m, err := res.MeasurementByKey("BATTERY_LEVEL")
|
||||
if err != nil && err != api.ErrNotAvailable {
|
||||
return 0, err
|
||||
}
|
||||
if err != api.ErrNotAvailable {
|
||||
return float64(m.Value.Percent), nil
|
||||
}
|
||||
}
|
||||
|
||||
res3, err := v.emobilityG()
|
||||
if err == nil && res3.BatteryChargeStatus != nil {
|
||||
return float64(res3.BatteryChargeStatus.StateOfChargeInPercentage), nil
|
||||
|
|
@ -80,17 +63,6 @@ var _ api.VehicleRange = (*Provider)(nil)
|
|||
|
||||
// Range implements the api.VehicleRange interface
|
||||
func (v *Provider) Range() (int64, error) {
|
||||
res, err := v.mobileG()
|
||||
if err == nil {
|
||||
m, err := res.MeasurementByKey("E_RANGE")
|
||||
if err != nil && err != api.ErrNotAvailable {
|
||||
return 0, err
|
||||
}
|
||||
if err != api.ErrNotAvailable {
|
||||
return int64(m.Value.Kilometers), nil
|
||||
}
|
||||
}
|
||||
|
||||
res3, err := v.emobilityG()
|
||||
if err == nil && res3.BatteryChargeStatus != nil {
|
||||
return res3.BatteryChargeStatus.RemainingERange.ValueInKilometers, nil
|
||||
|
|
@ -108,24 +80,6 @@ var _ api.VehicleFinishTimer = (*Provider)(nil)
|
|||
|
||||
// FinishTime implements the api.VehicleFinishTimer interface
|
||||
func (v *Provider) FinishTime() (time.Time, error) {
|
||||
res, err := v.mobileG()
|
||||
if err == nil {
|
||||
m, err := res.MeasurementByKey("BATTERY_CHARGING_STATE")
|
||||
if err != nil && err != api.ErrNotAvailable {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
if err != api.ErrNotAvailable {
|
||||
if m.Value.EndsAt == "" {
|
||||
if m.Value.LastModified != "" {
|
||||
return time.Parse(time.RFC3339, m.Value.LastModified)
|
||||
}
|
||||
return time.Time{}, nil
|
||||
}
|
||||
return time.Parse(time.RFC3339, m.Value.EndsAt)
|
||||
}
|
||||
}
|
||||
|
||||
res2, err := v.emobilityG()
|
||||
if err == nil {
|
||||
if res2.BatteryChargeStatus == nil {
|
||||
|
|
@ -142,30 +96,6 @@ var _ api.ChargeState = (*Provider)(nil)
|
|||
|
||||
// Status implements the api.ChargeState interface
|
||||
func (v *Provider) Status() (api.ChargeStatus, error) {
|
||||
res, err := v.mobileG()
|
||||
if err == nil {
|
||||
m, err := res.MeasurementByKey("BATTERY_CHARGING_STATE")
|
||||
if err != nil && err != api.ErrNotAvailable {
|
||||
return api.StatusNone, err
|
||||
}
|
||||
|
||||
if err != api.ErrNotAvailable {
|
||||
switch m.Value.Status {
|
||||
case "FAST_CHARGING", "NOT_PLUGGED", "UNKNOWN":
|
||||
return api.StatusA, nil
|
||||
case "CHARGING_COMPLETED", "CHARGING_PAUSED", "READY_TO_CHARGE", "SOC_REACHED",
|
||||
"INITIALISING", "STANDBY", "SUSPENDED", "PLUGGED_LOCKED", "PLUGGED_NOT_LOCKED":
|
||||
return api.StatusB, nil
|
||||
case "CHARGING":
|
||||
return api.StatusC, nil
|
||||
case "CHARGING_ERROR":
|
||||
return api.StatusF, nil
|
||||
default:
|
||||
return api.StatusNone, errors.New("mobile - unknown charging status: " + m.Value.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res2, err := v.emobilityG()
|
||||
if err == nil {
|
||||
if res2.BatteryChargeStatus == nil {
|
||||
|
|
@ -200,17 +130,6 @@ var _ api.VehicleClimater = (*Provider)(nil)
|
|||
|
||||
// Climater implements the api.VehicleClimater interface
|
||||
func (v *Provider) Climater() (bool, error) {
|
||||
res, err := v.mobileG()
|
||||
if err == nil {
|
||||
m, err := res.MeasurementByKey("CLIMATIZER_STATE")
|
||||
if err != nil && err != api.ErrNotAvailable {
|
||||
return false, err
|
||||
}
|
||||
if err != api.ErrNotAvailable {
|
||||
return m.Value.IsOn, err
|
||||
}
|
||||
}
|
||||
|
||||
res2, err := v.emobilityG()
|
||||
if err == nil {
|
||||
if res2.BatteryChargeStatus == nil {
|
||||
|
|
@ -234,17 +153,6 @@ var _ api.VehicleOdometer = (*Provider)(nil)
|
|||
|
||||
// Odometer implements the api.VehicleOdometer interface
|
||||
func (v *Provider) Odometer() (float64, error) {
|
||||
res, err := v.mobileG()
|
||||
if err == nil {
|
||||
m, err := res.MeasurementByKey("MILEAGE")
|
||||
if err != nil && err != api.ErrNotAvailable {
|
||||
return 0, err
|
||||
}
|
||||
if err != api.ErrNotAvailable {
|
||||
return float64(m.Value.Kilometers), nil
|
||||
}
|
||||
}
|
||||
|
||||
res2, err := v.statusG()
|
||||
if err == nil {
|
||||
return res2.Mileage.Value, nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue