evcc-io/vehicle/toyota/provider.go

85 lines
1.6 KiB
Go

package toyota
import (
"strings"
"sync"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
)
const refreshInterval = 15 * time.Minute
type Provider struct {
status func() (Status, error)
refresh func() error
}
func NewProvider(log *util.Logger, api *API, vin string, cache time.Duration) *Provider {
var (
mu sync.Mutex
lastRefresh time.Time
)
refresh := func() error {
err := api.RefreshStatus(vin)
if err == nil {
mu.Lock()
lastRefresh = time.Now()
mu.Unlock()
}
return err
}
impl := &Provider{
status: util.Cached(func() (Status, error) {
res, err := api.Status(vin)
if err != nil {
return res, err
}
// While charging, periodically ask the TCU to push fresh data
// to the cloud so subsequent polls return up-to-date SOC values.
mu.Lock()
needsRefresh := strings.EqualFold(res.Payload.ChargingStatus, "charging") && time.Since(lastRefresh) >= refreshInterval
mu.Unlock()
if needsRefresh {
if err := refresh(); err != nil {
log.ERROR.Printf("status refresh: %v", err)
}
}
return res, nil
}, cache),
refresh: refresh,
}
return impl
}
var _ api.Resurrector = (*Provider)(nil)
func (v *Provider) WakeUp() error {
return v.refresh()
}
func (v *Provider) Soc() (float64, error) {
res, err := v.status()
return float64(res.Payload.BatteryLevel), err
}
// Range implements the api.VehicleRange interface
func (v *Provider) Range() (int64, error) {
res, err := v.status()
if err != nil {
return 0, err
}
rng, err := res.Payload.EvRangeWithAc.ValueInKilometers()
if err == nil {
return rng, nil
}
return res.Payload.EvRange.ValueInKilometers()
}