Add vehicle range api (#486)
Co-authored-by: mark-sch <mark@schmalohr.de>
This commit is contained in:
parent
530ecb3b4c
commit
fcfc22fa72
9 changed files with 143 additions and 23 deletions
|
|
@ -97,6 +97,11 @@ type VehicleStatus interface {
|
|||
Status() (ChargeStatus, error)
|
||||
}
|
||||
|
||||
// VehicleRange provides the vehicles remaining km range
|
||||
type VehicleRange interface {
|
||||
Range() (int64, error)
|
||||
}
|
||||
|
||||
// Climater provides climatisation data
|
||||
type Climater interface {
|
||||
Climater() (active bool, outsideTemp float64, targetTemp float64, err error)
|
||||
|
|
|
|||
|
|
@ -74,12 +74,6 @@ func (d *dumper) Dump(name string, v interface{}) {
|
|||
// charger
|
||||
|
||||
if v, ok := v.(api.Charger); ok {
|
||||
// if status, err := v.Status(); err != nil {
|
||||
// fmt.Fprintf(w, "Status:\t%v\n", err)
|
||||
// } else {
|
||||
// fmt.Fprintf(w, "Status:\t%s\n", status)
|
||||
// }
|
||||
|
||||
if enabled, err := v.Enabled(); err != nil {
|
||||
fmt.Fprintf(w, "Enabled:\t%v\n", err)
|
||||
} else {
|
||||
|
|
@ -103,23 +97,23 @@ func (d *dumper) Dump(name string, v interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, ok := v.(api.ChargeFinishTimer); ok {
|
||||
if ft, err := v.FinishTime(); err != nil {
|
||||
fmt.Fprintf(w, "Finish time:\t%v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(w, "Finish time:\t%v\n", ft.Truncate(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
// vehicle
|
||||
|
||||
if v, ok := v.(api.Vehicle); ok {
|
||||
fmt.Printf("Capacity: %dkWh\n", v.Capacity())
|
||||
fmt.Fprintf(w, "Capacity:\t%dkWh\n", v.Capacity())
|
||||
|
||||
if soc, err := v.ChargeState(); err != nil {
|
||||
fmt.Printf("State: %v\n", err)
|
||||
fmt.Fprintf(w, "State:\t%v\n", err)
|
||||
} else {
|
||||
fmt.Printf("State: %.0f%%\n", soc)
|
||||
fmt.Fprintf(w, "State:\t%.0f%%\n", soc)
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := v.(api.VehicleRange); ok {
|
||||
if rng, err := v.Range(); err != nil {
|
||||
fmt.Fprintf(w, "Vehicle range:\t%v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(w, "Vehicle range:\t%vkm\n", rng)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +125,14 @@ func (d *dumper) Dump(name string, v interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, ok := v.(api.ChargeFinishTimer); ok {
|
||||
if ft, err := v.FinishTime(); err != nil {
|
||||
fmt.Fprintf(w, "Finish time:\t%v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(w, "Finish time:\t%v\n", ft.Truncate(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := v.(api.Climater); ok {
|
||||
if active, ot, tt, err := v.Climater(); err != nil {
|
||||
fmt.Fprintf(w, "Climater:\t%v\n", err)
|
||||
|
|
|
|||
|
|
@ -750,6 +750,20 @@ func (lp *LoadPoint) publishSoC() {
|
|||
lp.publish("chargeEstimate", time.Duration(-1))
|
||||
}
|
||||
|
||||
// publish remaining vehicle range
|
||||
func (lp *LoadPoint) publishRange() {
|
||||
if vs, ok := lp.vehicle.(api.VehicleRange); ok {
|
||||
if rng, err := vs.Range(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle range: %vkm", rng)
|
||||
lp.publish("range", rng)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
lp.publish("range", -1)
|
||||
}
|
||||
|
||||
// Update is the main control function. It reevaluates meters and charger state
|
||||
func (lp *LoadPoint) Update(sitePower float64) {
|
||||
mode := lp.GetMode()
|
||||
|
|
@ -779,6 +793,7 @@ func (lp *LoadPoint) Update(sitePower float64) {
|
|||
// initial update of connected state matches charger status
|
||||
lp.findActiveVehicle()
|
||||
lp.publishSoC()
|
||||
lp.publishRange()
|
||||
|
||||
// sync settings with charger
|
||||
lp.syncCharger()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/andig/evcc/api"
|
||||
)
|
||||
|
||||
// Response structure for kamereon api
|
||||
type Response struct {
|
||||
Data data `json:"data"`
|
||||
}
|
||||
|
|
@ -18,7 +19,8 @@ type attributes struct {
|
|||
Timestamp string `json:"timestamp"`
|
||||
ChargingStatus float32 `json:"chargingStatus"`
|
||||
InstantaneousPower int `json:"instantaneousPower"`
|
||||
RangeHvacOff int `json:"rangeHvacOff"`
|
||||
RangeHvacOff int `json:"rangeHvacOff"` // Nissan
|
||||
BatteryAutonomy int `json:"batteryAutonomy"` // Renault
|
||||
BatteryLevel int `json:"batteryLevel"`
|
||||
BatteryCapacity int `json:"batteryCapacity"` // Nissan
|
||||
BatteryTemperature int `json:"batteryTemperature"`
|
||||
|
|
@ -66,6 +68,17 @@ func (v *API) Status() (api.ChargeStatus, error) {
|
|||
return status, err
|
||||
}
|
||||
|
||||
// Range implements the Vehicle.Range interface
|
||||
func (v *API) Range() (int64, error) {
|
||||
res, err := v.apiG()
|
||||
|
||||
if res, ok := res.(Response); err == nil && ok {
|
||||
return int64(res.Data.Attributes.RangeHvacOff), nil
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// FinishTime implements the Vehicle.ChargeFinishTimer interface
|
||||
func (v *API) FinishTime() (time.Time, error) {
|
||||
res, err := v.apiG()
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ type attributes struct {
|
|||
ChargingStatus float32 `json:"chargingStatus"`
|
||||
InstantaneousPower int `json:"instantaneousPower"`
|
||||
RangeHvacOff int `json:"rangeHvacOff"`
|
||||
BatteryAutonomy int `json:"batteryAutonomy"`
|
||||
BatteryLevel int `json:"batteryLevel"`
|
||||
BatteryTemperature int `json:"batteryTemperature"`
|
||||
PlugStatus int `json:"plugStatus"`
|
||||
|
|
@ -341,6 +342,17 @@ func (v *Renault) Status() (api.ChargeStatus, error) {
|
|||
return status, err
|
||||
}
|
||||
|
||||
// Range implements the Vehicle.Range interface
|
||||
func (v *Renault) Range() (int64, error) {
|
||||
res, err := v.apiG()
|
||||
|
||||
if res, ok := res.(kamereonResponse); err == nil && ok {
|
||||
return int64(res.Data.Attributes.BatteryAutonomy), nil
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// FinishTime implements the Vehicle.ChargeFinishTimer interface
|
||||
func (v *Renault) FinishTime() (time.Time, error) {
|
||||
res, err := v.apiG()
|
||||
|
|
|
|||
|
|
@ -24,13 +24,14 @@ func (m *embed) Capacity() int64 {
|
|||
return m.capacity
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -p vehicle -f decorateVehicle -b api.Vehicle -o vehicle_decorators -t "api.VehicleStatus,Status,func() (api.ChargeStatus, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -p vehicle -f decorateVehicle -b api.Vehicle -o vehicle_decorators -t "api.VehicleStatus,Status,func() (api.ChargeStatus, error)" -t "api.VehicleRange,Range,func() (int64, error)"
|
||||
|
||||
// Vehicle is an api.Vehicle implementation with configurable getters and setters.
|
||||
type Vehicle struct {
|
||||
*embed
|
||||
chargeG func() (float64, error)
|
||||
statusG func() (string, error)
|
||||
rangeG func() (int64, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -44,6 +45,7 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Vehicle, error
|
|||
Capacity int64
|
||||
Charge provider.Config
|
||||
Status *provider.Config
|
||||
Range *provider.Config
|
||||
Cache time.Duration
|
||||
}{}
|
||||
|
||||
|
|
@ -81,7 +83,17 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Vehicle, error
|
|||
status = v.status
|
||||
}
|
||||
|
||||
res := decorateVehicle(v, status)
|
||||
// decorate vehicle with Range
|
||||
var rng func() (int64, error)
|
||||
if cc.Range != nil {
|
||||
v.rangeG, err = provider.NewIntGetterFromConfig(*cc.Range)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rng = v.rng
|
||||
}
|
||||
|
||||
res := decorateVehicle(v, status, rng)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
@ -102,3 +114,8 @@ func (m *Vehicle) status() (api.ChargeStatus, error) {
|
|||
|
||||
return status, err
|
||||
}
|
||||
|
||||
// rng implements the Vehicle.Range interface
|
||||
func (m *Vehicle) rng() (int64, error) {
|
||||
return m.rangeG()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import (
|
|||
"github.com/andig/evcc/api"
|
||||
)
|
||||
|
||||
func decorateVehicle(base api.Vehicle, vehicleStatus func() (api.ChargeStatus, error)) api.Vehicle {
|
||||
func decorateVehicle(base api.Vehicle, vehicleStatus func() (api.ChargeStatus, error), vehicleRange func() (int64, error)) api.Vehicle {
|
||||
switch {
|
||||
case vehicleStatus == nil:
|
||||
case vehicleRange == nil && vehicleStatus == nil:
|
||||
return base
|
||||
|
||||
case vehicleStatus != nil:
|
||||
case vehicleRange == nil && vehicleStatus != nil:
|
||||
return &struct {
|
||||
api.Vehicle
|
||||
api.VehicleStatus
|
||||
|
|
@ -21,11 +21,45 @@ func decorateVehicle(base api.Vehicle, vehicleStatus func() (api.ChargeStatus, e
|
|||
vehicleStatus: vehicleStatus,
|
||||
},
|
||||
}
|
||||
|
||||
case vehicleRange != nil && vehicleStatus == nil:
|
||||
return &struct {
|
||||
api.Vehicle
|
||||
api.VehicleRange
|
||||
}{
|
||||
Vehicle: base,
|
||||
VehicleRange: &decorateVehicleVehicleRangeImpl{
|
||||
vehicleRange: vehicleRange,
|
||||
},
|
||||
}
|
||||
|
||||
case vehicleRange != nil && vehicleStatus != nil:
|
||||
return &struct {
|
||||
api.Vehicle
|
||||
api.VehicleRange
|
||||
api.VehicleStatus
|
||||
}{
|
||||
Vehicle: base,
|
||||
VehicleRange: &decorateVehicleVehicleRangeImpl{
|
||||
vehicleRange: vehicleRange,
|
||||
},
|
||||
VehicleStatus: &decorateVehicleVehicleStatusImpl{
|
||||
vehicleStatus: vehicleStatus,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decorateVehicleVehicleRangeImpl struct {
|
||||
vehicleRange func() (int64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateVehicleVehicleRangeImpl) Range() (int64, error) {
|
||||
return impl.vehicleRange()
|
||||
}
|
||||
|
||||
type decorateVehicleVehicleStatusImpl struct {
|
||||
vehicleStatus func() (api.ChargeStatus, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,13 @@ type ChargerResponse struct {
|
|||
PlugStatusData struct {
|
||||
PlugState TimedString // connected
|
||||
}
|
||||
CruisingRangeStatusData struct {
|
||||
EngineTypeFirstEngine TimedString // typeIsElectric, petrolGasoline
|
||||
EngineTypeSecondEngine TimedString // typeIsElectric, petrolGasoline
|
||||
PrimaryEngineRange TimedInt
|
||||
SecondaryEngineRange TimedInt
|
||||
HybridRange TimedInt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,21 @@ func (v *Implementation) Status() (api.ChargeStatus, error) {
|
|||
return status, err
|
||||
}
|
||||
|
||||
// Range implements the Vehicle.Range interface
|
||||
func (v *Implementation) Range() (rng int64, err error) {
|
||||
res, err := v.chargerG()
|
||||
if res, ok := res.(ChargerResponse); err == nil && ok {
|
||||
crsd := res.Charger.Status.CruisingRangeStatusData
|
||||
|
||||
rng = int64(crsd.PrimaryEngineRange.Content)
|
||||
if crsd.EngineTypeFirstEngine.Content != "typeIsElectric" {
|
||||
rng = int64(crsd.SecondaryEngineRange.Content)
|
||||
}
|
||||
}
|
||||
|
||||
return rng, err
|
||||
}
|
||||
|
||||
// Climater implements the Vehicle.Climater interface
|
||||
func (v *Implementation) Climater() (active bool, outsideTemp float64, targetTemp float64, err error) {
|
||||
res, err := v.climateG()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue