Audi: add odometer api (#1481)
This commit is contained in:
parent
611b98884f
commit
b54382eb6c
4 changed files with 165 additions and 0 deletions
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/evcc-io/evcc/vehicle/audi"
|
||||
"github.com/evcc-io/evcc/vehicle/vw"
|
||||
)
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ import (
|
|||
type Audi struct {
|
||||
*embed
|
||||
*vw.Provider // provides the api implementations
|
||||
audiProvider *audi.Provider
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -78,5 +80,14 @@ func NewAudiFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
}
|
||||
}
|
||||
|
||||
audiApi := audi.NewAPI(log, identity, "Audi", "DE")
|
||||
v.audiProvider = audi.NewProvider(audiApi, cc.VIN, cc.Cache)
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
var _ api.VehicleOdometer = (*Audi)(nil)
|
||||
|
||||
func (v *Audi) Odometer() (float64, error) {
|
||||
return v.audiProvider.Odometer()
|
||||
}
|
||||
|
|
|
|||
65
vehicle/audi/api.go
Normal file
65
vehicle/audi/api.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package audi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// DefaultBaseURI is the Audi api base URI
|
||||
const DefaultBaseURI = "https://msg.audi.de/fs-car"
|
||||
|
||||
const StatusOdometer = "0x0101010002"
|
||||
|
||||
// API is the VW api client
|
||||
type API struct {
|
||||
*request.Helper
|
||||
brand, country string
|
||||
baseURI string
|
||||
}
|
||||
|
||||
// NewAPI creates a new api client
|
||||
func NewAPI(log *util.Logger, identity oauth2.TokenSource, brand, country string) *API {
|
||||
v := &API{
|
||||
Helper: request.NewHelper(log),
|
||||
brand: brand,
|
||||
country: country,
|
||||
baseURI: DefaultBaseURI,
|
||||
}
|
||||
|
||||
v.Client.Transport = &oauth2.Transport{
|
||||
Source: identity,
|
||||
Base: v.Client.Transport,
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *API) getJSON(uri string, res interface{}) error {
|
||||
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
|
||||
"Accept": request.JSONContent,
|
||||
"X-App-Name": "myAudi",
|
||||
"X-Country-Id": "DE",
|
||||
"X-Language-Id": "de",
|
||||
"X-App-Version": "3.22.0",
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
err = v.DoJSON(req, &res)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Status returns the /vehicles/<vin>/status response
|
||||
func (v *API) Status(vin string) (StatusResponse, error) {
|
||||
var res StatusResponse
|
||||
|
||||
uri := fmt.Sprintf("%s/bs/vsr/v1/Audi/DE/vehicles/%s/status", DefaultBaseURI, vin)
|
||||
err := v.getJSON(uri, &res)
|
||||
|
||||
return res, err
|
||||
}
|
||||
39
vehicle/audi/provider.go
Normal file
39
vehicle/audi/provider.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package audi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/provider"
|
||||
)
|
||||
|
||||
// Provider implements the evcc vehicle api
|
||||
type Provider struct {
|
||||
statusG func() (interface{}, error)
|
||||
}
|
||||
|
||||
// NewProvider provides the evcc vehicle api provider
|
||||
func NewProvider(api *API, vin string, cache time.Duration) *Provider {
|
||||
impl := &Provider{
|
||||
statusG: provider.NewCached(func() (interface{}, error) {
|
||||
return api.Status(vin)
|
||||
}, cache).InterfaceGetter(),
|
||||
}
|
||||
return impl
|
||||
}
|
||||
|
||||
var _ api.VehicleOdometer = (*Provider)(nil)
|
||||
|
||||
// Odometer implements the api.VehicleOdometer interface
|
||||
func (v *Provider) Odometer() (float64, error) {
|
||||
res, err := v.statusG()
|
||||
if res, ok := res.(StatusResponse); err == nil && ok {
|
||||
fd := res.ServiceByID(StatusOdometer).FieldByID(StatusOdometer)
|
||||
if fd != nil {
|
||||
return strconv.ParseFloat(fd.Value, 64)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
50
vehicle/audi/types.go
Normal file
50
vehicle/audi/types.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package audi
|
||||
|
||||
type StatusResponse struct {
|
||||
StoredVehicleDataResponse struct {
|
||||
VIN string
|
||||
VehicleData struct {
|
||||
Data []ServiceDefinition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StatusResponse) ServiceByID(id string) *ServiceDefinition {
|
||||
for _, d := range s.StoredVehicleDataResponse.VehicleData.Data {
|
||||
if d.ID == id {
|
||||
return &d
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServiceDefinition struct {
|
||||
ID string
|
||||
Field []FieldDefinition
|
||||
}
|
||||
|
||||
func (s *ServiceDefinition) FieldByID(id string) *FieldDefinition {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, f := range s.Field {
|
||||
if f.ID == id {
|
||||
return &f
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type FieldDefinition struct {
|
||||
ID string // "0x0101010001",
|
||||
TsCarSentUtc string // "2021-09-05T07:54:20Z",
|
||||
TsCarSent string // "2021-09-05T07:54:19",
|
||||
TsCarCaptured string // "2021-09-05T07:54:19",
|
||||
TsTssReceivedUtc string // "2021-09-05T07:54:23Z",
|
||||
MilCarCaptured int // 25009,
|
||||
MilCarSent int // 25009,
|
||||
Value string // "echo"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue