143 lines
2.8 KiB
Go
143 lines
2.8 KiB
Go
package bluelink_us
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"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 (
|
|
ApiURL = "/ac/v2/"
|
|
EnrollmentURL = "enrollment/details/%s"
|
|
VehicleStatusURL = "rcs/rvs/vehicleStatus"
|
|
PositionURL = "rcs/rfc/findMyCar"
|
|
ChargeStartURL = "evc/charge/start"
|
|
ChargeStopURL = "evc/charge/stop"
|
|
)
|
|
|
|
type APIConfig struct {
|
|
User string
|
|
Pin string
|
|
RegistrationID string
|
|
VIN string
|
|
Generation string
|
|
}
|
|
|
|
type API struct {
|
|
*request.Helper
|
|
baseURI string
|
|
user string
|
|
}
|
|
|
|
func NewAPI(log *util.Logger, ts oauth2.TokenSource, cfg APIConfig) *API {
|
|
v := &API{
|
|
Helper: request.NewHelper(log),
|
|
baseURI: BaseURL + ApiURL,
|
|
user: cfg.User,
|
|
}
|
|
|
|
// API can be slow
|
|
v.Client.Timeout = 30 * time.Second
|
|
|
|
// Add transport decorator for auth headers
|
|
v.Client.Transport = &transport.Decorator{
|
|
Decorator: func(req *http.Request) error {
|
|
token, err := ts.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for k, v := range BaseHeaders() {
|
|
req.Header.Set(k, v)
|
|
}
|
|
req.Header.Set("username", cfg.User)
|
|
req.Header.Set("accessToken", token.AccessToken)
|
|
|
|
// Vehicle-specific headers
|
|
if cfg.Pin != "" {
|
|
req.Header.Set("blueLinkServicePin", cfg.Pin)
|
|
}
|
|
if cfg.RegistrationID != "" {
|
|
req.Header.Set("registrationId", cfg.RegistrationID)
|
|
}
|
|
if cfg.Generation != "" {
|
|
req.Header.Set("gen", cfg.Generation)
|
|
}
|
|
if cfg.VIN != "" {
|
|
req.Header.Set("vin", cfg.VIN)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
Base: v.Client.Transport,
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
func (v *API) Vehicles() ([]Vehicle, error) {
|
|
var res EnrollmentResponse
|
|
|
|
uri := v.baseURI + fmt.Sprintf(EnrollmentURL, v.user)
|
|
err := v.GetJSON(uri, &res)
|
|
|
|
vehicles := make([]Vehicle, 0, len(res.EnrolledVehicleDetails))
|
|
for _, entry := range res.EnrolledVehicleDetails {
|
|
vehicles = append(vehicles, entry.VehicleDetails)
|
|
}
|
|
|
|
return vehicles, err
|
|
}
|
|
|
|
func (v *API) Status() (VehicleStatus, error) {
|
|
var res VehicleStatusResponse
|
|
|
|
uri := v.baseURI + VehicleStatusURL
|
|
err := v.GetJSON(uri, &res)
|
|
|
|
return res.VehicleStatus, err
|
|
}
|
|
|
|
func (v *API) Position() (PositionResponse, error) {
|
|
var res PositionResponse
|
|
|
|
uri := v.baseURI + PositionURL
|
|
err := v.GetJSON(uri, &res)
|
|
|
|
return res, err
|
|
}
|
|
|
|
func (v *API) ChargeStart() error {
|
|
uri := v.baseURI + ChargeStartURL
|
|
|
|
req, err := request.New(http.MethodPost, uri, nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := v.Do(req)
|
|
if err == nil {
|
|
_ = resp.Body.Close()
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (v *API) ChargeStop() error {
|
|
uri := v.baseURI + ChargeStopURL
|
|
|
|
req, err := request.New(http.MethodPost, uri, nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := v.Do(req)
|
|
if err == nil {
|
|
_ = resp.Body.Close()
|
|
}
|
|
return err
|
|
}
|