Add ChargeRemainder api (#325)
This commit is contained in:
parent
a24caac73d
commit
90e23dadcc
5 changed files with 75 additions and 1 deletions
|
|
@ -81,3 +81,8 @@ type Vehicle interface {
|
|||
Capacity() int64
|
||||
ChargeState() (float64, error)
|
||||
}
|
||||
|
||||
// ChargeRemainder provides current charge cycle duration
|
||||
type ChargeRemainder interface {
|
||||
RemainingTime() (time.Duration, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ func dumpAPIs(v interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, ok := v.(api.ChargeRemainder); ok {
|
||||
if duration, err := v.RemainingTime(); err != nil {
|
||||
fmt.Printf("Remaining: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Remaining: %v\n", duration)
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := v.(api.Diagnosis); ok {
|
||||
fmt.Println("Diagnostic dump:")
|
||||
v.Diagnosis()
|
||||
|
|
|
|||
|
|
@ -51,6 +51,17 @@ func (s *SocEstimator) RemainingChargeDuration(chargePower float64, targetSoC in
|
|||
return 0
|
||||
}
|
||||
|
||||
// use vehicle api if available
|
||||
if vr, ok := s.vehicle.(api.ChargeRemainder); ok {
|
||||
timeRemaining, err := vr.RemainingTime()
|
||||
if err == nil {
|
||||
return timeRemaining * time.Duration(percentRemaining/(100-s.socCharge))
|
||||
}
|
||||
|
||||
s.log.WARN.Printf("updating remaining time failed: %v", err)
|
||||
}
|
||||
|
||||
// estimate remaining time
|
||||
whRemaining := percentRemaining / 100 * s.capacity
|
||||
return time.Duration(float64(time.Hour) * whRemaining / chargePower).Round(time.Second)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,3 +95,20 @@ func (c *Cached) BoolGetter() func() (bool, error) {
|
|||
return c.val.(bool), c.err
|
||||
}
|
||||
}
|
||||
|
||||
// DurationGetter gets time.Duration value
|
||||
func (c *Cached) DurationGetter() func() (time.Duration, error) {
|
||||
g, ok := c.getter.(func() (time.Duration, error))
|
||||
if !ok {
|
||||
c.log.FATAL.Fatalf("invalid type: %T", c.getter)
|
||||
}
|
||||
|
||||
return func() (time.Duration, error) {
|
||||
if c.clock.Since(c.updated) > c.cache {
|
||||
c.val, c.err = g()
|
||||
c.updated = c.clock.Now()
|
||||
}
|
||||
|
||||
return c.val.(time.Duration), c.err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ type kamereonData struct {
|
|||
}
|
||||
|
||||
type batteryAttributes struct {
|
||||
Timestamp string `json:"timestamp"`
|
||||
ChargeStatus int `json:"chargeStatus"`
|
||||
InstantaneousPower int `json:"instantaneousPower"`
|
||||
RangeHvacOff int `json:"rangeHvacOff"`
|
||||
|
|
@ -80,6 +81,7 @@ type batteryAttributes struct {
|
|||
PlugStatus int `json:"plugStatus"`
|
||||
LastUpdateTime string `json:"lastUpdateTime"`
|
||||
ChargePower int `json:"chargePower"`
|
||||
RemainingTime *int `json:"chargingRemainingTime"`
|
||||
}
|
||||
|
||||
// Renault is an api.Vehicle implementation for Renault cars
|
||||
|
|
@ -91,6 +93,7 @@ type Renault struct {
|
|||
gigyaJwtToken string
|
||||
accountID string
|
||||
chargeStateG func() (float64, error)
|
||||
remainingTimeG func() (time.Duration, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -131,6 +134,7 @@ func NewRenaultFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|||
}
|
||||
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
v.remainingTimeG = provider.NewCached(v.remainingTime, cc.Cache).DurationGetter()
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
|
@ -302,7 +306,7 @@ func (v *Renault) kamereonVehicles(accountID string) (string, error) {
|
|||
|
||||
// chargeState implements the Vehicle.ChargeState interface
|
||||
func (v *Renault) chargeState() (float64, error) {
|
||||
uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v1/cars/%s/battery-status", v.kamereon.Target, v.accountID, v.vin)
|
||||
uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v2/cars/%s/battery-status", v.kamereon.Target, v.accountID, v.vin)
|
||||
kr, err := v.kamereonRequest(uri)
|
||||
|
||||
// repeat auth if error
|
||||
|
|
@ -319,3 +323,32 @@ func (v *Renault) chargeState() (float64, error) {
|
|||
func (v *Renault) ChargeState() (float64, error) {
|
||||
return v.chargeStateG()
|
||||
}
|
||||
|
||||
// remainingTime implements the Vehicle.ChargeRemainder interface
|
||||
func (v *Renault) remainingTime() (time.Duration, error) {
|
||||
uri := fmt.Sprintf("%s/commerce/v1/accounts/%s/kamereon/kca/car-adapter/v2/cars/%s/battery-status", v.kamereon.Target, v.accountID, v.vin)
|
||||
kr, err := v.kamereonRequest(uri)
|
||||
|
||||
// repeat auth if error
|
||||
if err != nil {
|
||||
if err = v.authFlow(); err == nil {
|
||||
kr, err = v.kamereonRequest(uri)
|
||||
}
|
||||
}
|
||||
|
||||
var timestamp time.Time
|
||||
if err == nil {
|
||||
timestamp, err = time.Parse(time.RFC3339, kr.Data.Attributes.Timestamp)
|
||||
}
|
||||
|
||||
if kr.Data.Attributes.RemainingTime == nil {
|
||||
return 0, errors.New("estimate not available")
|
||||
}
|
||||
|
||||
return time.Duration(*kr.Data.Attributes.RemainingTime)*time.Minute - time.Since(timestamp), err
|
||||
}
|
||||
|
||||
// RemainingTime implements the Vehicle.ChargeRemainder interface
|
||||
func (v *Renault) RemainingTime() (time.Duration, error) {
|
||||
return v.remainingTimeG()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue