162 lines
3.9 KiB
Go
162 lines
3.9 KiB
Go
package jlr
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"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 (
|
|
IF9_BASE_URL = "https://if9.prod-row.jlrmotor.com/if9/jlr"
|
|
IFOP_BASE_URL = "https://ifop.prod-row.jlrmotor.com/ifop/jlr"
|
|
)
|
|
|
|
// API is the Jaguar/Landrover api client
|
|
type API struct {
|
|
*request.Helper
|
|
}
|
|
|
|
// NewAPI creates a new api client
|
|
func NewAPI(log *util.Logger, device string, ts oauth2.TokenSource) *API {
|
|
v := &API{
|
|
Helper: request.NewHelper(log),
|
|
}
|
|
|
|
v.Client.Transport = &transport.Decorator{
|
|
Decorator: func(req *http.Request) error {
|
|
token, err := ts.Token()
|
|
if err == nil {
|
|
for k, v := range Headers(device, map[string]string{
|
|
"Authorization": "Bearer " + token.AccessToken,
|
|
"x-telematicsprogramtype": "jlrpy",
|
|
}) {
|
|
req.Header.Set(k, v)
|
|
}
|
|
}
|
|
return err
|
|
},
|
|
Base: v.Client.Transport,
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
func (v *API) User(name string) (User, error) {
|
|
var res User
|
|
|
|
uri := fmt.Sprintf("%s/users?loginName=%s", IF9_BASE_URL, url.QueryEscape(name))
|
|
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
|
|
"Content-Type": request.JSONContent,
|
|
"Accept": "application/vnd.wirelesscar.ngtp.if9.User-v3+json",
|
|
})
|
|
|
|
if err == nil {
|
|
err = v.DoJSON(req, &res)
|
|
}
|
|
|
|
return res, err
|
|
}
|
|
|
|
func (v *API) Vehicles(user string) ([]string, error) {
|
|
var vehicles []string
|
|
var resp VehiclesResponse
|
|
|
|
uri := fmt.Sprintf("%s/users/%s/vehicles?primaryOnly=true", IF9_BASE_URL, user)
|
|
|
|
err := v.GetJSON(uri, &resp)
|
|
if err == nil {
|
|
for _, v := range resp.Vehicles {
|
|
vehicles = append(vehicles, v.VIN)
|
|
}
|
|
}
|
|
|
|
return vehicles, nil
|
|
}
|
|
|
|
// Status returns the vehicle status
|
|
func (v *API) Status(vin string) (StatusResponse, error) {
|
|
var status StatusResponse
|
|
|
|
uri := fmt.Sprintf("%s/vehicles/%s/status?includeInactive=true", IF9_BASE_URL, vin)
|
|
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/vnd.ngtp.org.if9.healthstatus-v3+json",
|
|
})
|
|
|
|
if err == nil {
|
|
err = v.DoJSON(req, &status)
|
|
}
|
|
|
|
return status, err
|
|
}
|
|
|
|
// Position returns the vehicle position
|
|
func (v *API) Position(vin string) (PositionResponse, error) {
|
|
var status PositionResponse
|
|
|
|
uri := fmt.Sprintf("%s/vehicles/%s/position", IF9_BASE_URL, vin)
|
|
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
|
|
"Content-Type": "application/json",
|
|
})
|
|
|
|
if err == nil {
|
|
err = v.DoJSON(req, &status)
|
|
}
|
|
|
|
return status, err
|
|
}
|
|
|
|
func (v *API) AuthenticateVinService(vin, user, service string) (PinResponse, error) {
|
|
pin := vin[len(vin)-4:]
|
|
data := map[string]string{
|
|
"serviceName": service,
|
|
"pin": pin,
|
|
}
|
|
|
|
uri := fmt.Sprintf("%s/vehicles/%s/users/%s/authenticate", IF9_BASE_URL, vin, user)
|
|
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), map[string]string{
|
|
"Content-Type": "application/vnd.wirelesscar.ngtp.if9.AuthenticateRequest-v2+json",
|
|
})
|
|
|
|
var res PinResponse
|
|
if err == nil {
|
|
err = v.DoJSON(req, &res)
|
|
}
|
|
|
|
return res, err
|
|
}
|
|
|
|
func (v *API) ChargeAction(vin, user string, start bool) error {
|
|
var data map[string]interface{}
|
|
|
|
pin, err := v.AuthenticateVinService(vin, user, "CP")
|
|
if err == nil {
|
|
onoff := map[bool]string{false: "FORCE_OFF", true: "FORCE_ON"}
|
|
|
|
data = map[string]interface{}{
|
|
"token": pin.Token,
|
|
"serviceParameters": []KeyValue{{"CHARGE_NOW_SETTING", onoff[start]}},
|
|
}
|
|
}
|
|
|
|
uri := fmt.Sprintf("%s/vehicles/%s/chargeProfile", IF9_BASE_URL, vin)
|
|
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), map[string]string{
|
|
"Content-Type": "application/vnd.wirelesscar.ngtp.if9.PhevService-v1+json",
|
|
"Accept": "application/vnd.wirelesscar.ngtp.if9.ServiceStatus-v5+json",
|
|
})
|
|
|
|
var res ActionResponse
|
|
if err == nil {
|
|
if err = v.DoJSON(req, &res); err == nil && res.FailureDescription != "" {
|
|
err = errors.New(res.FailureDescription)
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|