-
-
Notifications
You must be signed in to change notification settings - Fork 641
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9c598e1
commit 9c47207
Showing
7 changed files
with
496 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package myskoda | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/evcc-io/evcc/util" | ||
"github.com/evcc-io/evcc/util/request" | ||
"golang.org/x/oauth2" | ||
) | ||
|
||
const BaseURI = "https://mysmob.api.connect.skoda-auto.cz/api" | ||
|
||
// API is the Skoda api client | ||
type API struct { | ||
*request.Helper | ||
} | ||
|
||
// NewAPI creates a new api client | ||
func NewAPI(log *util.Logger, ts oauth2.TokenSource) *API { | ||
v := &API{ | ||
Helper: request.NewHelper(log), | ||
} | ||
|
||
v.Client.Transport = &oauth2.Transport{ | ||
Source: ts, | ||
Base: v.Client.Transport, | ||
} | ||
|
||
return v | ||
} | ||
|
||
// Vehicles implements the /v2/garage response | ||
func (v *API) Vehicles() ([]Vehicle, error) { | ||
var res VehiclesResponse | ||
|
||
uri := fmt.Sprintf("%s/v2/garage", BaseURI) | ||
err := v.GetJSON(uri, &res) | ||
|
||
return res.Vehicles, err | ||
} | ||
|
||
func (v *API) VehicleDetails(vin string) (Vehicle, error) { | ||
var res Vehicle | ||
uri := fmt.Sprintf("%s/v2/garage/vehicles/%s", BaseURI, vin) | ||
err := v.GetJSON(uri, &res) | ||
return res, err | ||
} | ||
|
||
// Status implements the /v2/vehicle-status/<vin> response | ||
func (v *API) Status(vin string) (StatusResponse, error) { | ||
var res StatusResponse | ||
uri := fmt.Sprintf("%s/v1/vehicle-health-report/warning-lights/%s", BaseURI, vin) | ||
err := v.GetJSON(uri, &res) | ||
return res, err | ||
} | ||
|
||
// Charger implements the /v1/charging/<vin> response | ||
func (v *API) Charger(vin string) (ChargerResponse, error) { | ||
var res ChargerResponse | ||
uri := fmt.Sprintf("%s/v1/charging/%s", BaseURI, vin) | ||
err := v.GetJSON(uri, &res) | ||
return res, err | ||
} | ||
|
||
// Settings implements the /v1/charging/<vin>/settings response | ||
func (v *API) Settings(vin string) (SettingsResponse, error) { | ||
var res SettingsResponse | ||
|
||
chrgRes, err := v.Charger(vin) | ||
if err == nil { | ||
res = chrgRes.Settings | ||
} | ||
return res, err | ||
} | ||
|
||
// Climater implements the /v2/air-conditioning/<vin> response | ||
func (v *API) Climater(vin string) (ClimaterResponse, error) { | ||
var res ClimaterResponse | ||
uri := fmt.Sprintf("%s/v2/air-conditioning/%s", BaseURI, vin) | ||
err := v.GetJSON(uri, &res) | ||
return res, err | ||
} | ||
|
||
const ( | ||
ActionCharge = "charging" | ||
ActionChargeStart = "start" | ||
ActionChargeStop = "stop" | ||
) | ||
|
||
// Action executes a vehicle action | ||
func (v *API) Action(vin, action, value string) error { | ||
// @POST("api/v1/charging/{vin}/start") | ||
// @POST("api/v1/charging/{vin}/stop") | ||
uri := fmt.Sprintf("%s/v1/%s/%s/%s", BaseURI, action, vin, value) | ||
|
||
req, err := request.New(http.MethodPost, uri, nil, request.JSONEncoding) | ||
if err == nil { | ||
err = v.DoJSON(req, nil) | ||
} | ||
return err | ||
} | ||
|
||
func (v *API) WakeUp(vin string) error { | ||
// @POST("api/v1/vehicle-wakeup/{vin}") | ||
uri := fmt.Sprintf("%s/v1/vehicle-wakeup/%s", BaseURI, vin) | ||
|
||
req, err := request.New(http.MethodPost, uri, nil, request.JSONEncoding) | ||
if err == nil { | ||
err = v.DoJSON(req, nil) | ||
} | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package myskoda | ||
|
||
import "net/url" | ||
|
||
const ( | ||
Brand = "VW" | ||
Country = "CZ" | ||
|
||
// Authorization ClientID | ||
AuthClientID = "afb0473b-6d82-42b8-bfea-cead338c46ef" | ||
) | ||
|
||
// Skoda native api | ||
var AuthParams = url.Values{ | ||
"response_type": {"code id_token"}, | ||
"client_id": {"7f045eee-7003-4379-9968-9355ed2adb06@apps_vw-dilab_com"}, | ||
"redirect_uri": {"myskoda://redirect/login/"}, | ||
"scope": {"address badge birthdate cars driversLicense dealers email mileage mbb nationalIdentifier openid phone profession profile vin"}, | ||
} | ||
|
||
// TokenRefreshService parameters | ||
var TRSParams = url.Values{ | ||
"brand": {"skoda"}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package myskoda | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/evcc-io/evcc/api" | ||
"github.com/evcc-io/evcc/provider" | ||
) | ||
|
||
// Provider implements the vehicle api | ||
type Provider struct { | ||
statusG func() (StatusResponse, error) | ||
chargerG func() (ChargerResponse, error) | ||
settingsG func() (SettingsResponse, error) | ||
climateG func() (ClimaterResponse, error) | ||
action func(action, value string) error | ||
wakeup func() error | ||
} | ||
|
||
// NewProvider creates a vehicle api provider | ||
func NewProvider(api *API, vin string, cache time.Duration) *Provider { | ||
impl := &Provider{ | ||
statusG: provider.Cached(func() (StatusResponse, error) { | ||
return api.Status(vin) | ||
}, cache), | ||
chargerG: provider.Cached(func() (ChargerResponse, error) { | ||
return api.Charger(vin) | ||
}, cache), | ||
climateG: provider.Cached(func() (ClimaterResponse, error) { | ||
return api.Climater(vin) | ||
}, cache), | ||
settingsG: provider.Cached(func() (SettingsResponse, error) { | ||
return api.Settings(vin) | ||
}, cache), | ||
action: func(action, value string) error { | ||
return api.Action(vin, action, value) | ||
}, | ||
wakeup: func() error { | ||
return api.WakeUp(vin) | ||
}, | ||
} | ||
return impl | ||
} | ||
|
||
var _ api.Battery = (*Provider)(nil) | ||
|
||
// Soc implements the api.Vehicle interface | ||
func (v *Provider) Soc() (float64, error) { | ||
res, err := v.chargerG() | ||
if err == nil { | ||
return float64(res.Status.Battery.StateOfChargeInPercent), nil | ||
} | ||
|
||
return 0, err | ||
} | ||
|
||
var _ api.ChargeState = (*Provider)(nil) | ||
|
||
// Status implements the api.ChargeState interface | ||
func (v *Provider) Status() (api.ChargeStatus, error) { | ||
status := api.StatusA // disconnected | ||
|
||
res, err := v.climateG() | ||
if err == nil { | ||
if res.ChargerConnectionState == "CONNECTED" { | ||
status = api.StatusB | ||
} | ||
} | ||
|
||
resChrg, err := v.chargerG() | ||
if err == nil { | ||
if resChrg.Status.State == "CHARGING" { | ||
status = api.StatusC | ||
} | ||
} | ||
|
||
return status, err | ||
} | ||
|
||
var _ api.VehicleFinishTimer = (*Provider)(nil) | ||
|
||
// FinishTime implements the api.VehicleFinishTimer interface | ||
func (v *Provider) FinishTime() (time.Time, error) { | ||
res, err := v.chargerG() | ||
if err == nil { | ||
crg := res.Status | ||
|
||
// estimate not available | ||
if crg.State == "Error" || crg.ChargeType == "Invalid" { | ||
return time.Time{}, api.ErrNotAvailable | ||
} | ||
|
||
remaining := time.Duration(crg.RemainingTimeToFullyChargedInMinutes) * time.Minute | ||
return time.Now().Add(remaining), err | ||
} | ||
|
||
return time.Time{}, err | ||
} | ||
|
||
var _ api.VehicleRange = (*Provider)(nil) | ||
|
||
// Range implements the api.VehicleRange interface | ||
func (v *Provider) Range() (rng int64, err error) { | ||
res, err := v.chargerG() | ||
return res.Status.Battery.RemainingCruisingRangeInMeters / 1e3, err | ||
} | ||
|
||
var _ api.VehicleOdometer = (*Provider)(nil) | ||
|
||
// Odometer implements the api.VehicleOdometer interface | ||
func (v *Provider) Odometer() (odo float64, err error) { | ||
res, err := v.statusG() | ||
return res.MileageInKm, err | ||
} | ||
|
||
// var _ api.VehicleClimater = (*Provider)(nil) | ||
|
||
// // Climater implements the api.VehicleClimater interface | ||
// func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) { | ||
// res, err := v.climateG() | ||
// err == nil { | ||
// state := strings.ToLower(res.Climater.Status.ClimatisationStatusData.ClimatisationState.Content) | ||
// active := state != "off" && state != "invalid" && state != "error" | ||
|
||
// targetTemp = res.Climater.Settings.TargetTemperature.Content | ||
// outsideTemp = res.Climater.Status.TemperatureStatusData.OutdoorTemperature.Content | ||
// if math.IsNaN(outsideTemp) { | ||
// outsideTemp = targetTemp // cover "invalid" | ||
// } | ||
|
||
// return active, outsideTemp, targetTemp, nil | ||
// } | ||
|
||
// return active, outsideTemp, targetTemp, err | ||
// } | ||
|
||
var _ api.SocLimiter = (*Provider)(nil) | ||
|
||
// GetLimitSoc implements the api.SocLimiter interface | ||
func (v *Provider) GetLimitSoc() (int64, error) { | ||
res, err := v.chargerG() | ||
if err == nil { | ||
return int64(res.Settings.TargetStateOfChargeInPercent), nil | ||
} | ||
|
||
return 0, err | ||
} | ||
|
||
var _ api.ChargeController = (*Provider)(nil) | ||
|
||
// ChargeEnable implements the api.ChargeController interface | ||
func (v *Provider) ChargeEnable(enable bool) error { | ||
action := map[bool]string{true: ActionChargeStart, false: ActionChargeStop} | ||
return v.action(ActionCharge, action[enable]) | ||
} | ||
|
||
var _ api.Resurrector = (*Provider)(nil) | ||
|
||
// WakeUp implements the api.Resurrector interface | ||
func (v *Provider) WakeUp() error { | ||
return v.wakeup() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package service | ||
|
||
import ( | ||
"net/url" | ||
|
||
"github.com/evcc-io/evcc/util" | ||
"github.com/evcc-io/evcc/vehicle/skoda/myskoda/tokenrefreshservice" | ||
"github.com/evcc-io/evcc/vehicle/vag" | ||
"github.com/evcc-io/evcc/vehicle/vag/vwidentity" | ||
) | ||
|
||
func TokenRefreshServiceTokenSource(log *util.Logger, data, q url.Values, user, password string) (vag.TokenSource, error) { | ||
q, err := vwidentity.Login(log, q, user, password) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
trs := tokenrefreshservice.New(log, data) | ||
token, err := trs.Exchange(q) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return trs.TokenSource(token), nil | ||
} |
Oops, something went wrong.