parent
dcaadb783f
commit
f38353bf0d
8 changed files with 454 additions and 0 deletions
12
templates/definition/vehicle/aiways.yaml
Normal file
12
templates/definition/vehicle/aiways.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
template: aiways
|
||||
products:
|
||||
- brand: Aiways
|
||||
params:
|
||||
- preset: vehiclebase
|
||||
- preset: vehicleidentify
|
||||
- name: vin
|
||||
required: true
|
||||
render: |
|
||||
type: aiways
|
||||
{{ include "vehicle-base" . }}
|
||||
{{ include "vehicle-identify" . }}
|
||||
28
templates/docs/vehicle/aiways_0.yaml
Normal file
28
templates/docs/vehicle/aiways_0.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
product:
|
||||
brand: Aiways
|
||||
render:
|
||||
- default: |
|
||||
type: template
|
||||
template: aiways
|
||||
title: # Wird in der Benutzeroberfläche angezeigt # Optional
|
||||
user: # Benutzerkonto (bspw. E-Mail Adresse, User Id, etc.)
|
||||
password: # Passwort des Benutzerkontos (bei führenden Nullen bitte in einfache Hochkommata setzen)
|
||||
vin: W... # Erforderlich, wenn mehrere Fahrzeuge des Herstellers vorhanden sind
|
||||
capacity: 50 # Akkukapazität in kWh # Optional
|
||||
advanced: |
|
||||
type: template
|
||||
template: aiways
|
||||
title: # Wird in der Benutzeroberfläche angezeigt # Optional
|
||||
user: # Benutzerkonto (bspw. E-Mail Adresse, User Id, etc.)
|
||||
password: # Passwort des Benutzerkontos (bei führenden Nullen bitte in einfache Hochkommata setzen)
|
||||
vin: W... # Erforderlich, wenn mehrere Fahrzeuge des Herstellers vorhanden sind
|
||||
capacity: 50 # Akkukapazität in kWh # Optional
|
||||
phases: 3 # Die maximale Anzahl der Phasen welche genutzt werden können # Optional
|
||||
icon: car # Darstellung in der UI. Mögliche Werte: bike, bus, car, heater, heatercooler, heatpump, moped, motorcycle, plug, scooter, tractor, van, waterheater # Optional
|
||||
cache: 15m # Zeitintervall nach dem Daten erneut vom Fahrzeug abgefragt werden # Optional
|
||||
mode: # Möglich sind Off, Now, MinPV und PV, oder leer wenn keiner definiert werden soll # Optional
|
||||
minSoc: 25 # Lade sofort mit maximaler Geschwindigkeit bis zu dem angegeben Ladestand, wenn der Lademodus nicht auf 'Aus' steht # Optional
|
||||
targetSoc: 80 # Bis zu welchem Ladestand (Soc) soll das Fahrzeug geladen werden # Optional
|
||||
minCurrent: 6 # Definiert die minimale Stromstärke pro angeschlossener Phase mit welcher das Fahrzeug geladen werden soll # Optional
|
||||
maxCurrent: 16 # Definiert die maximale Stromstärke pro angeschlossener Phase mit welcher das Fahrzeug geladen werden soll # Optional
|
||||
identifiers: # Kann meist erst später eingetragen werden, siehe: https://docs.evcc.io/docs/guides/vehicles/#erkennung-des-fahrzeugs-an-der-wallbox # Optional
|
||||
|
|
@ -131,6 +131,7 @@
|
|||
"Victron"
|
||||
],
|
||||
"Vehicles": [
|
||||
"Aiways",
|
||||
"Audi",
|
||||
"BMW",
|
||||
"Citroën",
|
||||
|
|
|
|||
67
vehicle/aiways.go
Normal file
67
vehicle/aiways.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package vehicle
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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/aiways"
|
||||
)
|
||||
|
||||
// https://github.com/davidgiga1993/AiwaysAPI
|
||||
// https://github.com/TA2k/ioBroker.vw-connect
|
||||
|
||||
// Aiways is an api.Vehicle implementation for Aiways cars
|
||||
type Aiways struct {
|
||||
*embed
|
||||
*aiways.Provider // provides the api implementations
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Add("aiways", NewAiwaysFromConfig)
|
||||
}
|
||||
|
||||
// NewAiwaysFromConfig creates a new vehicle
|
||||
func NewAiwaysFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
||||
cc := struct {
|
||||
embed `mapstructure:",squash"`
|
||||
User, Password, VIN string
|
||||
Cache time.Duration
|
||||
Timeout time.Duration
|
||||
}{
|
||||
Cache: interval,
|
||||
Timeout: request.Timeout,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Aiways{
|
||||
embed: &cc.embed,
|
||||
}
|
||||
|
||||
log := util.NewLogger("aiways").Redact(cc.User, cc.Password, cc.VIN)
|
||||
|
||||
identity := aiways.NewIdentity(log)
|
||||
user, err := identity.Login(cc.User, cc.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Redact(strconv.FormatInt(user, 10))
|
||||
|
||||
api := aiways.NewAPI(log, identity)
|
||||
api.Client.Timeout = cc.Timeout
|
||||
|
||||
// _, err := api.Vehicles()
|
||||
// cc.VIN, err = ensureVehicle(cc.VIN, api.Vehicles)
|
||||
|
||||
if err == nil {
|
||||
v.Provider = aiways.NewProvider(api, user, cc.VIN, cc.Cache)
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
60
vehicle/aiways/api.go
Normal file
60
vehicle/aiways/api.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package aiways
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
)
|
||||
|
||||
// https://github.com/snaptec/openWB/blob/master/modules/soc_aiways/aiways_get_soc.py
|
||||
|
||||
const URI = "https://coiapp-api-eu.ai-ways.com:10443"
|
||||
|
||||
// API implements the Aiways api
|
||||
type API struct {
|
||||
*request.Helper
|
||||
identity TokenProvider
|
||||
}
|
||||
|
||||
// New creates a new Aiways API
|
||||
func NewAPI(log *util.Logger, identity TokenProvider) *API {
|
||||
v := &API{
|
||||
Helper: request.NewHelper(log),
|
||||
identity: identity,
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// func (v *API) Vehicles() ([]Vehicle, error) {
|
||||
// }
|
||||
|
||||
func (v *API) Status(user int64, vin string) (StatusResponse, error) {
|
||||
var res StatusResponse
|
||||
|
||||
data2 := struct {
|
||||
UserId int64 `json:"userId"`
|
||||
VIN string `json:"vin"`
|
||||
}{
|
||||
UserId: user,
|
||||
VIN: vin,
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/app/vc/getCondition", URI)
|
||||
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data2), map[string]string{
|
||||
"Content-Type": request.JSONContent,
|
||||
"Accept": request.JSONContent,
|
||||
"Token": v.identity.Token(),
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
if err = v.DoJSON(req, &res); err == nil && res.Data == nil {
|
||||
err = errors.New(res.Message)
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
68
vehicle/aiways/identity.go
Normal file
68
vehicle/aiways/identity.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package aiways
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
)
|
||||
|
||||
type Identity struct {
|
||||
*request.Helper
|
||||
user, hash, token string
|
||||
}
|
||||
|
||||
type TokenProvider interface {
|
||||
Token() string
|
||||
}
|
||||
|
||||
// NewIdentity creates BMW identity
|
||||
func NewIdentity(log *util.Logger) *Identity {
|
||||
v := &Identity{
|
||||
Helper: request.NewHelper(log),
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Identity) Login(user, password string) (int64, error) {
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(password))
|
||||
|
||||
v.user = user
|
||||
v.hash = hex.EncodeToString(hash.Sum(nil))
|
||||
|
||||
var res User
|
||||
|
||||
data := struct {
|
||||
Account string `json:"account"`
|
||||
Password string `json:"password"`
|
||||
}{
|
||||
Account: v.user,
|
||||
Password: v.hash,
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/aiways-passport-service/passport/login/password", URI)
|
||||
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
|
||||
|
||||
if err == nil {
|
||||
if err = v.DoJSON(req, &res); err == nil && res.Data == nil {
|
||||
err = errors.New(res.Message)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
v.token = res.Data.Token
|
||||
return res.Data.UserID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (v *Identity) Token() string {
|
||||
return v.token
|
||||
}
|
||||
89
vehicle/aiways/provider.go
Normal file
89
vehicle/aiways/provider.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package aiways
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/provider"
|
||||
)
|
||||
|
||||
// Provider implements the vehicle api
|
||||
type Provider struct {
|
||||
statusG func() (StatusResponse, error)
|
||||
}
|
||||
|
||||
// NewProvider creates a vehicle api provider
|
||||
func NewProvider(api *API, user int64, vin string, cache time.Duration) *Provider {
|
||||
impl := &Provider{
|
||||
statusG: provider.Cached(func() (StatusResponse, error) {
|
||||
return api.Status(user, vin)
|
||||
}, cache),
|
||||
}
|
||||
return impl
|
||||
}
|
||||
|
||||
var _ api.Battery = (*Provider)(nil)
|
||||
|
||||
// Soc implements the api.Vehicle interface
|
||||
func (v *Provider) Soc() (float64, error) {
|
||||
res, err := v.statusG()
|
||||
if err == nil {
|
||||
return float64(res.Data.Vc.Soc), nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// var _ api.ChargeState = (*Provider)(nil)
|
||||
|
||||
// // Status implements the api.ChargeState interface
|
||||
// func (v *Provider) Status() (api.ChargeStatus, error) {
|
||||
// status := api.StatusA // disconnected
|
||||
|
||||
// res, err := v.statusG()
|
||||
// if err == nil {
|
||||
// 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
|
||||
// }
|
||||
|
||||
var _ api.VehicleRange = (*Provider)(nil)
|
||||
|
||||
// Range implements the api.VehicleRange interface
|
||||
func (v *Provider) Range() (int64, error) {
|
||||
res, err := v.statusG()
|
||||
if err == nil {
|
||||
return int64(res.Data.Vc.DrivingRange), nil
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var _ api.VehicleOdometer = (*Provider)(nil)
|
||||
|
||||
// Odometer implements the api.VehicleOdometer interface
|
||||
func (v *Provider) Odometer() (float64, error) {
|
||||
res, err := v.statusG()
|
||||
if err == nil {
|
||||
return res.Data.Vc.VehicleMileage, nil
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// var _ api.VehiclePosition = (*Provider)(nil)
|
||||
|
||||
// // Position implements the api.VehiclePosition interface
|
||||
// func (v *Provider) Position() (float64, float64, error) {
|
||||
// res, err := v.statusG()
|
||||
// if err == nil {
|
||||
// return res.Data.Vc.Lat, res.Data.Vc.Lon, nil
|
||||
// }
|
||||
|
||||
// return 0, 0, err
|
||||
// }
|
||||
129
vehicle/aiways/types.go
Normal file
129
vehicle/aiways/types.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package aiways
|
||||
|
||||
type User struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data *struct {
|
||||
Email string `json:"email"`
|
||||
HeadURL string `json:"headUrl"`
|
||||
IsFirstLogin int64 `json:"isFirstLogin"`
|
||||
Mobile interface{} `json:"mobile"`
|
||||
Nickname string `json:"nickname"`
|
||||
Sex interface{} `json:"sex"`
|
||||
Token string `json:"token"`
|
||||
UserID int64 `json:"userId"`
|
||||
UserType int64 `json:"userType"`
|
||||
Username string `json:"username"`
|
||||
} `json:"data"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type StatusResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data *struct {
|
||||
Vc struct {
|
||||
DataTime string `json:"dataTime"`
|
||||
DataTimeTS int64 `json:"dataTimeTS"`
|
||||
// NorthLat string `json:"northLat"`
|
||||
// EastLon string `json:"eastLon"`
|
||||
Lat float64 `json:"lat,string"`
|
||||
Lon float64 `json:"lon,string"`
|
||||
Address string `json:"address"`
|
||||
ChargeSts int `json:"chargeSts,string"`
|
||||
// SocLow string `json:"socLow"`
|
||||
// LastFlameoutTime string `json:"lastFlameoutTime"`
|
||||
// VehicleSts string `json:"vehicleSts"`
|
||||
// LowSpdMuteSts string `json:"lowSpdMuteSts"`
|
||||
// DoorLockSts string `json:"doorLockSts"`
|
||||
// TrunkLockSts string `json:"trunkLockSts"`
|
||||
// DoorStsReserved string `json:"doorStsReserved"`
|
||||
// DoorOpenStsLf string `json:"doorOpenStsLf"`
|
||||
// DoorOpenStsRf string `json:"doorOpenStsRf"`
|
||||
// DoorOpenStsLb string `json:"doorOpenStsLb"`
|
||||
// DoorOpenStsRb string `json:"doorOpenStsRb"`
|
||||
// FrontHatchCoverOpenSts string `json:"frontHatchCoverOpenSts"`
|
||||
// TrunkDoorOpenSts string `json:"trunkDoorOpenSts"`
|
||||
// ChargingCoverOpenSts string `json:"chargingCoverOpenSts"`
|
||||
// WindowOpenStsLf string `json:"windowOpenStsLf"`
|
||||
// WindowOpenStsRf string `json:"windowOpenStsRf"`
|
||||
// WindowOpenStsLb string `json:"windowOpenStsLb"`
|
||||
// WindowOpenStsRb string `json:"windowOpenStsRb"`
|
||||
// LeftTurnLightOpenSts string `json:"leftTurnLightOpenSts"`
|
||||
// RightTurnLightOpenSts string `json:"rightTurnLightOpenSts"`
|
||||
// BackFrogLightOpenSts string `json:"backFrogLightOpenSts"`
|
||||
// HighLightOpenSts string `json:"highLightOpenSts"`
|
||||
// LowLightOpenSts string `json:"lowLightOpenSts"`
|
||||
// WidthLightOpenSts string `json:"widthLightOpenSts"`
|
||||
// BrakeLightOpenSts string `json:"brakeLightOpenSts"`
|
||||
// ReversingLightOpenSts string `json:"reversingLightOpenSts"`
|
||||
// DayRunningLightOpenSts string `json:"dayRunningLightOpenSts"`
|
||||
// AutoLightOpenSts string `json:"autoLightOpenSts"`
|
||||
// SunroofStsSts string `json:"sunroofStsSts"`
|
||||
// SunroofHorizontalOpen string `json:"sunroofHorizontalOpen"`
|
||||
// SunroofSunshadeOpen string `json:"sunroofSunshadeOpen"`
|
||||
// TirePressureStsLf string `json:"tirePressureStsLf"`
|
||||
// TirePressureStsRf string `json:"tirePressureStsRf"`
|
||||
// TirePressureStsLb string `json:"tirePressureStsLb"`
|
||||
// TirePressureStsRb string `json:"tirePressureStsRb"`
|
||||
// TirePressureLf string `json:"tirePressureLf"`
|
||||
// TirePressureRf string `json:"tirePressureRf"`
|
||||
// TirePressureLb string `json:"tirePressureLb"`
|
||||
// TirePressureRb string `json:"tirePressureRb"`
|
||||
// TireTempHighStsLf string `json:"tireTempHighStsLf"`
|
||||
// TireTempHighStsRf string `json:"tireTempHighStsRf"`
|
||||
// TireTempHighStsLb string `json:"tireTempHighStsLb"`
|
||||
// TireTempHighStsRb string `json:"tireTempHighStsRb"`
|
||||
// TireTempLf string `json:"tireTempLf"`
|
||||
// TireTempRf string `json:"tireTempRf"`
|
||||
// TireTempLb string `json:"tireTempLb"`
|
||||
// TireTempRb string `json:"tireTempRb"`
|
||||
// AirconAcSts string `json:"airconAcSts"`
|
||||
// AirconAutoSts string `json:"airconAutoSts"`
|
||||
// AirconWindMode string `json:"airconWindMode"`
|
||||
// AirconWindVolume string `json:"airconWindVolume"`
|
||||
// AirconCycleSts string `json:"airconCycleSts"`
|
||||
// AirconTempDisLf string `json:"airconTempDisLf"`
|
||||
// AirconTempDisRf string `json:"airconTempDisRf"`
|
||||
// AirconSyncSts string `json:"airconSyncSts"`
|
||||
// AirconRunSts string `json:"airconRunSts"`
|
||||
// AirconOutsideTemp string `json:"airconOutsideTemp"`
|
||||
// AirconInsideTemp string `json:"airconInsideTemp"`
|
||||
// SafetyBeltStsDrv string `json:"safetyBeltStsDrv"`
|
||||
// SafetyBeltStsPass string `json:"safetyBeltStsPass"`
|
||||
// SeatBeltStsBl string `json:"seatBeltStsBl"`
|
||||
// SeatBeltStsBm string `json:"seatBeltStsBm"`
|
||||
// SeatBeltStsBr string `json:"seatBeltStsBr"`
|
||||
// SeatHeatStsDrv string `json:"seatHeatStsDrv"`
|
||||
// SeatHeatStsPass string `json:"seatHeatStsPass"`
|
||||
// SeatHeatStsSys string `json:"seatHeatStsSys"`
|
||||
// ChgConnStsDc string `json:"chgConnStsDc"`
|
||||
// ChgConnStsAc string `json:"chgConnStsAc"`
|
||||
// KeyLowPowerWarn string `json:"keyLowPowerWarn"`
|
||||
DrivingRange float64 `json:"drivingRange,string"`
|
||||
VehicleMileage float64 `json:"vehicleMileage,string"`
|
||||
// Speed string `json:"speed"`
|
||||
// CrashSts string `json:"crashSts"`
|
||||
// ElecModeFlg string `json:"elecModeFlg"`
|
||||
// PowerMode string `json:"powerMode"`
|
||||
// SteeringAngle string `json:"steeringAngle"`
|
||||
// EpbSts string `json:"epbSts"`
|
||||
Soc int `json:"soc,string"`
|
||||
// AgpsSts string `json:"agpsSts"`
|
||||
// BtConnMac string `json:"btConnMac"`
|
||||
// BtConnUserID string `json:"btConnUserId"`
|
||||
// WifiConnCount string `json:"wifiConnCount"`
|
||||
// HasSkylight string `json:"hasSkylight"`
|
||||
// Iccid string `json:"iccid"`
|
||||
// DataFlow string `json:"dataFlow"`
|
||||
// AuthStatus string `json:"authStatus"`
|
||||
// SenceSeason int `json:"senceSeason"`
|
||||
// SenceSeasonText string `json:"senceSeasonText"`
|
||||
// CarSenceSeasonText string `json:"carSenceSeasonText"`
|
||||
// BmsChgRemTime string `json:"bmsChgRemTime"`
|
||||
// BmsPreThemalMode string `json:"bmsPreThemalMode"`
|
||||
// BcmRemoteControlSt string `json:"bcmRemoteControlSt"`
|
||||
Active bool `json:"active"`
|
||||
} `json:"vc"`
|
||||
} `json:"data"`
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue