195 lines
3.9 KiB
Go
195 lines
3.9 KiB
Go
package hello
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/evcc-io/evcc/util"
|
|
"github.com/evcc-io/evcc/util/request"
|
|
"github.com/evcc-io/evcc/util/transport"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// https://github.com/TA2k/ioBroker.smart-eq
|
|
|
|
type API struct {
|
|
*request.Helper
|
|
identity *Identity
|
|
}
|
|
|
|
func NewAPI(log *util.Logger, identity *Identity) *API {
|
|
v := &API{
|
|
Helper: request.NewHelper(log),
|
|
identity: identity,
|
|
}
|
|
|
|
v.Client.Transport = &transport.Decorator{
|
|
Base: &transport.Decorator{
|
|
Base: v.Client.Transport,
|
|
|
|
// decorate token
|
|
Decorator: func(req *http.Request) error {
|
|
token, err := identity.Token()
|
|
if err == nil {
|
|
req.Header.Set("authorization", token.AccessToken)
|
|
}
|
|
return err
|
|
},
|
|
},
|
|
|
|
// decorate headers
|
|
Decorator: transport.DecorateHeaders(map[string]string{
|
|
"accept": "application/json;responseformat=3",
|
|
"content-type": "application/json; charset=utf-8",
|
|
"x-operator-code": operatorCode,
|
|
"x-api-signature-version": "1.0",
|
|
"x-app-id": appID,
|
|
"x-device-identifier": v.identity.DeviceID(),
|
|
}),
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
func (v *API) request(method, path string, params url.Values, body io.Reader) (*http.Request, error) {
|
|
if body != nil {
|
|
b, err := io.ReadAll(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// read from buffer
|
|
body = bytes.NewReader(b)
|
|
}
|
|
|
|
nonce, ts, sign, err := createSignature(method, path, params, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if body != nil {
|
|
// rewind body
|
|
body.(*bytes.Reader).Seek(0, io.SeekStart)
|
|
}
|
|
|
|
uri := fmt.Sprintf("%s/%s?%s", ApiURI, strings.TrimPrefix(path, "/"), params.Encode())
|
|
req, err := request.New(method, uri, body, map[string]string{
|
|
"x-api-signature-nonce": nonce,
|
|
"x-signature": sign,
|
|
"x-timestamp": ts,
|
|
})
|
|
|
|
return req, err
|
|
}
|
|
|
|
func (v *API) Vehicles() ([]string, error) {
|
|
var res struct {
|
|
Code Int
|
|
Message string
|
|
Error Error
|
|
Data struct {
|
|
List []Vehicle
|
|
}
|
|
}
|
|
|
|
userID, err := v.identity.UserID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
params := url.Values{
|
|
"needSharedCar": {"1"},
|
|
"userId": {userID},
|
|
}
|
|
|
|
path := "/device-platform/user/vehicle/secure"
|
|
req, err := v.request(http.MethodGet, path, params, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = v.DoJSON(req, &res)
|
|
if err := responseError(err, res.Code, res.Message, res.Error); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
vehicles := lo.Map(res.Data.List, func(v Vehicle, _ int) string {
|
|
return v.VIN
|
|
})
|
|
|
|
return vehicles, err
|
|
}
|
|
|
|
func (v *API) UpdateSession(vin string) error {
|
|
token, err := v.identity.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
params := url.Values{
|
|
"identity_type": {"smart"},
|
|
}
|
|
|
|
data := map[string]string{
|
|
"vin": vin,
|
|
"sessionToken": token.AccessToken,
|
|
"language": "",
|
|
}
|
|
|
|
path := "/device-platform/user/session/update"
|
|
req, err := v.request(http.MethodPost, path, params, request.MarshalJSON(data))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var res struct {
|
|
Code Int
|
|
Message string
|
|
Error Error
|
|
}
|
|
|
|
err = v.DoJSON(req, &res)
|
|
return responseError(err, res.Code, res.Message, res.Error)
|
|
}
|
|
|
|
func (v *API) Status(vin string) (VehicleStatus, error) {
|
|
if err := v.UpdateSession(vin); err != nil {
|
|
return VehicleStatus{}, fmt.Errorf("update session failed: %w", err)
|
|
}
|
|
|
|
var res struct {
|
|
Code Int
|
|
Message string
|
|
Error Error
|
|
Data struct {
|
|
VehicleStatus VehicleStatus
|
|
}
|
|
}
|
|
|
|
userID, err := v.identity.UserID()
|
|
if err != nil {
|
|
return VehicleStatus{}, err
|
|
}
|
|
|
|
params := url.Values{
|
|
"latest": {"true"},
|
|
"target": {""},
|
|
"userId": {userID},
|
|
}
|
|
|
|
path := "/remote-control/vehicle/status/" + vin
|
|
req, err := v.request(http.MethodGet, path, params, nil)
|
|
if err != nil {
|
|
return VehicleStatus{}, err
|
|
}
|
|
|
|
err = v.DoJSON(req, &res)
|
|
if err := responseError(err, res.Code, res.Message, res.Error); err != nil {
|
|
return VehicleStatus{}, err
|
|
}
|
|
|
|
return res.Data.VehicleStatus, err
|
|
}
|