Add status and climatisation apis for ID (#569)

This commit is contained in:
andig 2021-01-03 14:18:57 +01:00 • committed by GitHub
parent c6a64d1d75
commit 8db09e8160
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 26 deletions

View file

@ -3,12 +3,16 @@ package id
import (
"fmt"
"net/http"
"strings"
"github.com/andig/evcc/util"
"github.com/andig/evcc/util/request"
"github.com/andig/evcc/vehicle/vw"
)
// https://identity-userinfo.vwgroup.io/oidc/userinfo
// https://customer-profile.apps.emea.vwapps.io/v1/customers/<userId>/realCarData
// API is an api.Vehicle implementation for VW ID cars
type API struct {
*request.Helper
@ -35,7 +39,8 @@ func (v *API) Vehicles() (res []string, err error) {
var vehicles struct {
Data []struct {
VIN string
VIN string
Nickname string
}
}
@ -55,10 +60,11 @@ type Status struct {
Data struct {
BatteryStatus BatteryStatus
ChargingStatus ChargingStatus
ChargingSettings ChargingSettings
PlugStatus PlugStatus
RangeStatus RangeStatus
ClimatisationSettings ClimatisationSettings
// ClimatisationStatus ClimatisationStatus // currently not available
ClimatisationStatus ClimatisationStatus // may be currently not available
}
}
@ -72,16 +78,16 @@ type BatteryStatus struct {
// ChargingStatus is the /status.chargingStatus api
type ChargingStatus struct {
CarCapturedTimestamp string
ChargingState string
RemainingChargingTimeToCompleteMin int `json:"remainingChargingTimeToComplete_min"`
ChargePowerKW int `json:"chargePower_kW"`
ChargeRateKmph int `json:"chargeRate_kmph"`
ChargingState string // readyForCharging
RemainingChargingTimeToCompleteMin int `json:"remainingChargingTimeToComplete_min"`
ChargePowerKW int `json:"chargePower_kW"`
ChargeRateKmph int `json:"chargeRate_kmph"`
}
// ChargingSettings is the /status.chargingSettings api
type ChargingSettings struct {
CarCapturedTimestamp string
MaxChargeCurrentAC string
MaxChargeCurrentAC string // reduced, maximum
AutoUnlockPlugWhenCharged string
TargetSOCPercent int `json:"targetSOC_pct"`
}
@ -89,17 +95,24 @@ type ChargingSettings struct {
// PlugStatus is the /status.plugStatus api
type PlugStatus struct {
CarCapturedTimestamp string
PlugConnectionState string
PlugConnectionState string // connected, disconnected
PlugLockState string
}
// ClimatisationStatus is the /status.climatisationStatus api
type ClimatisationStatus struct {
CarCapturedTimestamp string
RemainingClimatisationTimeMin int `json:"remainingClimatisationTime_min"`
ClimatisationState string // off
}
// ClimatisationSettings is the /status.climatisationSettings api
type ClimatisationSettings struct {
CarCapturedTimestamp string
TargetTemperatureK float64 `json:"targetTemperature_K"`
TargetTemperatureC float64 `json:"targetTemperature_C"`
ClimatisationWithoutExternalPower bool
ClimatizationAtUnlock bool
ClimatisationAtUnlock bool // ClimatizationAtUnlock?
WindowHeatingEnabled bool
ZoneFrontLeftEnabled bool
ZoneFrontRightEnabled bool
@ -134,3 +147,22 @@ func (v *API) Status(vin string) (res Status, err error) {
return res, err
}
// Any implements any api response
func (v *API) Any(uri, vin string) (interface{}, error) {
if strings.Contains(uri, "%s") {
uri = fmt.Sprintf(uri, vin)
}
req, err := request.New(http.MethodGet, uri, nil, map[string]string{
"Accept": "application/json",
"Authorization": "Bearer " + v.identity.Token(),
})
var res interface{}
if err == nil {
err = v.DoJSON(req, &res)
}
return res, err
}

View file

@ -1,8 +1,10 @@
package id
import (
"strings"
"time"
"github.com/andig/evcc/api"
"github.com/andig/evcc/provider"
)
@ -31,6 +33,23 @@ func (v *Provider) ChargeState() (float64, error) {
return 0, err
}
// Status implements the Vehicle.Status interface
func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
if res.Data.PlugStatus.PlugConnectionState == "connected" {
status = api.StatusB
}
if res.Data.ChargingStatus.ChargingState == "charging" {
status = api.StatusC
}
}
return status, err
}
// FinishTime implements the Vehicle.ChargeFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.statusG()
@ -52,3 +71,26 @@ func (v *Provider) Range() (int64, error) {
return 0, err
}
// Climater implements the Vehicle.Climater interface
func (v *Provider) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
res, err := v.statusG()
if res, ok := res.(Status); err == nil && ok {
state := strings.ToLower(res.Data.ClimatisationStatus.ClimatisationState)
if state == "" {
return false, 0, 0, api.ErrNotAvailable
}
active := state != "off" && state != "invalid" && state != "error"
targetTemp = res.Data.ClimatisationSettings.TargetTemperatureC
// TODO: not available; use target temp to avoid wrong heating/cooling display
outsideTemp = targetTemp
return active, outsideTemp, targetTemp, nil
}
return active, outsideTemp, targetTemp, err
}

View file

@ -37,6 +37,23 @@ func (v *Provider) ChargeState() (float64, error) {
return 0, err
}
// Status implements the Vehicle.Status interface
func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if res.Charger.Status.PlugStatusData.PlugState.Content == "connected" {
status = api.StatusB
}
if res.Charger.Status.ChargingStatusData.ChargingState.Content == "charging" {
status = api.StatusC
}
}
return status, err
}
// FinishTime implements the Vehicle.ChargeFinishTimer interface
func (v *Provider) FinishTime() (time.Time, error) {
res, err := v.chargerG()
@ -57,23 +74,6 @@ func (v *Provider) FinishTime() (time.Time, error) {
return time.Time{}, err
}
// Status implements the Vehicle.Status interface
func (v *Provider) Status() (api.ChargeStatus, error) {
status := api.StatusA // disconnected
res, err := v.chargerG()
if res, ok := res.(ChargerResponse); err == nil && ok {
if res.Charger.Status.PlugStatusData.PlugState.Content == "connected" {
status = api.StatusB
}
if res.Charger.Status.ChargingStatusData.ChargingState.Content == "charging" {
status = api.StatusC
}
}
return status, err
}
// Range implements the Vehicle.Range interface
func (v *Provider) Range() (rng int64, err error) {
res, err := v.chargerG()