116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package bluelink
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/evcc-io/evcc/util"
|
|
"github.com/evcc-io/evcc/util/request"
|
|
"github.com/evcc-io/evcc/util/transport"
|
|
)
|
|
|
|
const (
|
|
VehiclesURL = "vehicles"
|
|
StatusURL = "vehicles/%s/status"
|
|
StatusLatestURL = "vehicles/%s/status/latest"
|
|
StatusURLCCS2 = "vehicles/%s/ccs2/carstatus"
|
|
StatusLatestURLCCS2 = "vehicles/%s/ccs2/carstatus/latest"
|
|
)
|
|
|
|
const (
|
|
resOK = "S" // auth fail: F
|
|
)
|
|
|
|
// ErrAuthFail indicates authorization failure
|
|
var ErrAuthFail = errors.New("authorization failed")
|
|
|
|
// API implements the Kia/Hyundai bluelink api.
|
|
type API struct {
|
|
*request.Helper
|
|
baseURI string
|
|
}
|
|
|
|
// New creates a new BlueLink API
|
|
func NewAPI(log *util.Logger, baseURI string, decorator func(*http.Request) error) *API {
|
|
v := &API{
|
|
Helper: request.NewHelper(log),
|
|
baseURI: strings.TrimSuffix(baseURI, "/api/v1/spa") + "/api/v1/spa",
|
|
}
|
|
|
|
// api is unbelievably slow when retrieving status
|
|
v.Client.Timeout = 120 * time.Second
|
|
|
|
if transport, ok := v.Client.Transport.(*http.Transport); ok {
|
|
transport.TLSHandshakeTimeout = 30 * time.Second
|
|
}
|
|
|
|
v.Client.Transport = &transport.Decorator{
|
|
Decorator: decorator,
|
|
Base: v.Client.Transport,
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
type Vehicle struct {
|
|
VIN, VehicleName, VehicleID string
|
|
CcuCCS2ProtocolSupport int
|
|
}
|
|
|
|
func (v *API) Vehicles() ([]Vehicle, error) {
|
|
var res VehiclesResponse
|
|
|
|
uri := fmt.Sprintf("%s/%s", v.baseURI, VehiclesURL)
|
|
err := v.GetJSON(uri, &res)
|
|
|
|
return res.ResMsg.Vehicles, err
|
|
}
|
|
|
|
// StatusLatest retrieves the latest server-side status
|
|
func (v *API) StatusLatest(vehicle Vehicle) (BluelinkVehicleStatusLatest, error) {
|
|
vid := vehicle.VehicleID
|
|
|
|
if vehicle.CcuCCS2ProtocolSupport != 0 {
|
|
var res StatusLatestResponseCCS
|
|
uri := fmt.Sprintf("%s/%s", v.baseURI, fmt.Sprintf(StatusLatestURLCCS2, vid))
|
|
err := v.GetJSON(uri, &res)
|
|
if err == nil && res.RetCode != resOK {
|
|
err = fmt.Errorf("unexpected response: %s", res.RetCode)
|
|
}
|
|
return res, err
|
|
}
|
|
|
|
var res StatusLatestResponse
|
|
uri := fmt.Sprintf("%s/%s", v.baseURI, fmt.Sprintf(StatusLatestURL, vid))
|
|
err := v.GetJSON(uri, &res)
|
|
if err == nil && res.RetCode != resOK {
|
|
err = fmt.Errorf("unexpected response: %s", res.RetCode)
|
|
}
|
|
return res, err
|
|
}
|
|
|
|
// StatusPartial refreshes the status
|
|
func (v *API) StatusPartial(vehicle Vehicle) (BluelinkVehicleStatus, error) {
|
|
vid := vehicle.VehicleID
|
|
|
|
if vehicle.CcuCCS2ProtocolSupport != 0 {
|
|
var res StatusLatestResponseCCS
|
|
uri := fmt.Sprintf("%s/%s", v.baseURI, fmt.Sprintf(StatusLatestURLCCS2, vid))
|
|
err := v.GetJSON(uri, &res)
|
|
if err == nil && res.RetCode != resOK {
|
|
err = fmt.Errorf("unexpected response: %s", res.RetCode)
|
|
}
|
|
return res, err
|
|
}
|
|
|
|
var res StatusResponse
|
|
uri := fmt.Sprintf("%s/%s", v.baseURI, fmt.Sprintf(StatusURL, vid))
|
|
err := v.GetJSON(uri, &res)
|
|
if err == nil && res.RetCode != resOK {
|
|
err = fmt.Errorf("unexpected response: %s", res.RetCode)
|
|
}
|
|
return res.ResMsg, err
|
|
}
|