Bluelink: refactor (#1903)
This commit is contained in:
parent
3363c34722
commit
0b1d474a74
5 changed files with 98 additions and 85 deletions
|
|
@ -4,22 +4,22 @@ 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 = "/api/v1/spa/vehicles"
|
||||
StatusURL = "/api/v1/spa/vehicles/%s/status"
|
||||
StatusLatestURL = "/api/v1/spa/vehicles/%s/status/latest"
|
||||
VehiclesURL = "vehicles"
|
||||
StatusURL = "vehicles/%s/status"
|
||||
StatusLatestURL = "vehicles/%s/status/latest"
|
||||
)
|
||||
|
||||
const (
|
||||
resOK = "S" // auth fail: F
|
||||
timeFormat = "20060102150405 -0700" // Note: must add timeOffset
|
||||
timeOffset = " +0100"
|
||||
resOK = "S" // auth fail: F
|
||||
)
|
||||
|
||||
// ErrAuthFail indicates authorization failure
|
||||
|
|
@ -29,96 +29,51 @@ var ErrAuthFail = errors.New("authorization failed")
|
|||
// Based on https://github.com/Hacksore/bluelinky.
|
||||
type API struct {
|
||||
*request.Helper
|
||||
log *util.Logger
|
||||
identity *Identity
|
||||
baseURI string
|
||||
}
|
||||
|
||||
type Requester interface {
|
||||
Request(*http.Request) error
|
||||
}
|
||||
|
||||
// New creates a new BlueLink API
|
||||
func NewAPI(log *util.Logger, identity *Identity, cache time.Duration) *API {
|
||||
func NewAPI(log *util.Logger, baseURI string, identity Requester, cache time.Duration) *API {
|
||||
v := &API{
|
||||
log: log,
|
||||
identity: identity,
|
||||
Helper: request.NewHelper(log),
|
||||
Helper: request.NewHelper(log),
|
||||
baseURI: strings.TrimSuffix(baseURI, "/api/v1/spa") + "/api/v1/spa",
|
||||
}
|
||||
|
||||
// api is unbelievably slow when retrieving status
|
||||
v.Helper.Client.Timeout = 120 * time.Second
|
||||
v.Client.Timeout = 120 * time.Second
|
||||
|
||||
v.Client.Transport = &transport.Decorator{
|
||||
Decorator: identity.Request,
|
||||
Base: v.Client.Transport,
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
type VehiclesResponse struct {
|
||||
RetCode string
|
||||
ResMsg struct {
|
||||
Vehicles []Vehicle
|
||||
}
|
||||
}
|
||||
|
||||
type Vehicle struct {
|
||||
VIN, VehicleName, VehicleID string
|
||||
}
|
||||
|
||||
func (v *API) Vehicles() ([]Vehicle, error) {
|
||||
req, err := v.identity.Request(http.MethodGet, VehiclesURL)
|
||||
var res VehiclesResponse
|
||||
|
||||
var resp VehiclesResponse
|
||||
if err == nil {
|
||||
err = v.DoJSON(req, &resp)
|
||||
}
|
||||
uri := fmt.Sprintf("%s/%s", v.baseURI, VehiclesURL)
|
||||
err := v.GetJSON(uri, &res)
|
||||
|
||||
return resp.ResMsg.Vehicles, err
|
||||
}
|
||||
|
||||
type StatusResponse struct {
|
||||
RetCode string
|
||||
ResCode string
|
||||
ResMsg StatusData
|
||||
}
|
||||
|
||||
type StatusLatestResponse struct {
|
||||
RetCode string
|
||||
ResCode string
|
||||
ResMsg struct {
|
||||
VehicleStatusInfo struct {
|
||||
VehicleStatus StatusData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type StatusData struct {
|
||||
Time string
|
||||
EvStatus struct {
|
||||
BatteryStatus float64
|
||||
RemainTime2 struct {
|
||||
Atc struct {
|
||||
Value, Unit int
|
||||
}
|
||||
}
|
||||
DrvDistance []DrivingDistance
|
||||
}
|
||||
Vehicles []Vehicle
|
||||
}
|
||||
|
||||
func (d *StatusData) Updated() (time.Time, error) {
|
||||
return time.Parse(timeFormat, d.Time+timeOffset)
|
||||
}
|
||||
|
||||
type DrivingDistance struct {
|
||||
RangeByFuel struct {
|
||||
EvModeRange struct {
|
||||
Value int
|
||||
}
|
||||
}
|
||||
return res.ResMsg.Vehicles, err
|
||||
}
|
||||
|
||||
func (v *API) Status(vid string) (StatusLatestResponse, error) {
|
||||
var res StatusLatestResponse
|
||||
|
||||
req, err := v.identity.Request(http.MethodGet, fmt.Sprintf(StatusLatestURL, vid))
|
||||
if err == nil {
|
||||
if err = v.DoJSON(req, &res); err == nil && res.RetCode != resOK {
|
||||
err = fmt.Errorf("unexpected response: %s", res.RetCode)
|
||||
}
|
||||
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
|
||||
|
|
@ -128,11 +83,10 @@ func (v *API) Status(vid string) (StatusLatestResponse, error) {
|
|||
func (v *API) StatusPartial(vid string) (StatusResponse, error) {
|
||||
var res StatusResponse
|
||||
|
||||
req, err := v.identity.Request(http.MethodGet, fmt.Sprintf(StatusURL, vid))
|
||||
if err == nil {
|
||||
if err = v.DoJSON(req, &res); err == nil && res.RetCode != resOK {
|
||||
err = fmt.Errorf("unexpected response: %s", res.RetCode)
|
||||
}
|
||||
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, err
|
||||
|
|
|
|||
|
|
@ -355,21 +355,23 @@ func (v *Identity) Login(user, password string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// Request creates authenticated request
|
||||
func (v *Identity) Request(method, path string) (*http.Request, error) {
|
||||
// Request decorates requests with authorization headers
|
||||
func (v *Identity) Request(req *http.Request) error {
|
||||
token, err := v.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
headers := map[string]string{
|
||||
for k, v := range map[string]string{
|
||||
"Authorization": "Bearer " + token.AccessToken,
|
||||
"ccsp-device-id": v.deviceID,
|
||||
"ccsp-application-id": v.config.CCSPApplicationID,
|
||||
"offset": "1",
|
||||
"User-Agent": "okhttp/3.10.0",
|
||||
"Stamp": v.stamp(),
|
||||
} {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
return request.New(method, v.config.URI+path, nil, headers)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
57
vehicle/bluelink/types.go
Normal file
57
vehicle/bluelink/types.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package bluelink
|
||||
|
||||
import "time"
|
||||
|
||||
type VehiclesResponse struct {
|
||||
RetCode string
|
||||
ResMsg struct {
|
||||
Vehicles []Vehicle
|
||||
}
|
||||
}
|
||||
|
||||
type StatusResponse struct {
|
||||
RetCode string
|
||||
ResCode string
|
||||
ResMsg StatusData
|
||||
}
|
||||
|
||||
type StatusLatestResponse struct {
|
||||
RetCode string
|
||||
ResCode string
|
||||
ResMsg struct {
|
||||
VehicleStatusInfo struct {
|
||||
VehicleStatus StatusData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type StatusData struct {
|
||||
Time string
|
||||
EvStatus struct {
|
||||
BatteryStatus float64
|
||||
RemainTime2 struct {
|
||||
Atc struct {
|
||||
Value, Unit int
|
||||
}
|
||||
}
|
||||
DrvDistance []DrivingDistance
|
||||
}
|
||||
Vehicles []Vehicle
|
||||
}
|
||||
|
||||
const (
|
||||
timeFormat = "20060102150405 -0700" // Note: must add timeOffset
|
||||
timeOffset = " +0100"
|
||||
)
|
||||
|
||||
func (d *StatusData) Updated() (time.Time, error) {
|
||||
return time.Parse(timeFormat, d.Time+timeOffset)
|
||||
}
|
||||
|
||||
type DrivingDistance struct {
|
||||
RangeByFuel struct {
|
||||
EvModeRange struct {
|
||||
Value int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
api := bluelink.NewAPI(log, identity, cc.Cache)
|
||||
api := bluelink.NewAPI(log, settings.URI, identity, cc.Cache)
|
||||
|
||||
vehicles, err := api.Vehicles()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
api := bluelink.NewAPI(log, identity, cc.Cache)
|
||||
api := bluelink.NewAPI(log, settings.URI, identity, cc.Cache)
|
||||
|
||||
vehicles, err := api.Vehicles()
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue