chore: refactor vehicle functions into separate file
This commit is contained in:
parent
1b71c4ff82
commit
51db45e6c1
4 changed files with 328 additions and 309 deletions
|
|
@ -1,6 +1,8 @@
|
|||
package core
|
||||
|
||||
const (
|
||||
title = "title" // loadpoint title
|
||||
|
||||
phasesConfigured = "phasesConfigured" // configured phases (1/3, 0 for auto on 1p3p chargers, nil for plain chargers)
|
||||
phasesEnabled = "phasesEnabled" // enabled phases (1/3)
|
||||
phasesActive = "phasesActive" // active phases as used by vehicle (1/2/3)
|
||||
|
|
@ -16,9 +18,13 @@ const (
|
|||
vehiclePresent = "vehiclePresent" // vehicle detected
|
||||
vehicleTitle = "vehicleTitle" // vehicle title
|
||||
|
||||
minSoc = "minSoc" // min soc goal
|
||||
targetSoc = "targetSoc" // target charging soc goal
|
||||
targetTime = "targetTime" // target charging finish time goal
|
||||
planActive = "planActive" // target charging plan has determined current slot to be an active slot
|
||||
planProjectedStart = "planProjectedStart" // target charging plan start time (earliest slot)
|
||||
minCurrent = "minCurrent" // charger min current
|
||||
maxCurrent = "maxCurrent" // charger max current
|
||||
chargeRemainingDuration = "chargeRemainingDuration" // charge remaining duration
|
||||
minSoc = "minSoc" // min soc goal
|
||||
targetEnergy = "targetEnergy" // target charging energy goal
|
||||
targetSoc = "targetSoc" // target charging soc goal
|
||||
targetTime = "targetTime" // target charging finish time goal
|
||||
planActive = "planActive" // target charging plan has determined current slot to be an active slot
|
||||
planProjectedStart = "planProjectedStart" // target charging plan start time (earliest slot)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -17,7 +16,6 @@ import (
|
|||
"github.com/evcc-io/evcc/core/planner"
|
||||
"github.com/evcc-io/evcc/core/soc"
|
||||
"github.com/evcc-io/evcc/core/wrapper"
|
||||
"github.com/evcc-io/evcc/provider"
|
||||
"github.com/evcc-io/evcc/push"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
|
||||
|
|
@ -47,10 +45,8 @@ const (
|
|||
|
||||
timerInactive = "inactive"
|
||||
|
||||
minActiveCurrent = 1.0 // minimum current at which a phase is treated as active
|
||||
minActiveVoltage = 208 // minimum voltage at which a phase is treated as active
|
||||
vehicleDetectInterval = 1 * time.Minute
|
||||
vehicleDetectDuration = 10 * time.Minute
|
||||
minActiveCurrent = 1.0 // minimum current at which a phase is treated as active
|
||||
minActiveVoltage = 208 // minimum voltage at which a phase is treated as active
|
||||
|
||||
guardGracePeriod = 10 * time.Second // allow out of sync during this timespan
|
||||
)
|
||||
|
|
@ -553,9 +549,9 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
|
|||
_ = lp.bus.Subscribe(evVehicleSoc, lp.evVehicleSocProgressHandler)
|
||||
|
||||
// publish initial values
|
||||
lp.publish("title", lp.Title())
|
||||
lp.publish("minCurrent", lp.MinCurrent)
|
||||
lp.publish("maxCurrent", lp.MaxCurrent)
|
||||
lp.publish(title, lp.Title())
|
||||
lp.publish(minCurrent, lp.MinCurrent)
|
||||
lp.publish(maxCurrent, lp.MaxCurrent)
|
||||
|
||||
lp.setConfiguredPhases(lp.ConfiguredPhases)
|
||||
lp.publish(phasesEnabled, lp.phases)
|
||||
|
|
@ -791,294 +787,6 @@ func (lp *Loadpoint) remoteControlled(demand loadpoint.RemoteDemand) bool {
|
|||
return lp.remoteDemand == demand
|
||||
}
|
||||
|
||||
// setVehicleIdentifier updated the vehicle id as read from the charger
|
||||
func (lp *Loadpoint) setVehicleIdentifier(id string) {
|
||||
if lp.vehicleIdentifier != id {
|
||||
lp.vehicleIdentifier = id
|
||||
lp.publish("vehicleIdentity", id)
|
||||
}
|
||||
}
|
||||
|
||||
// identifyVehicle reads vehicle identification from charger
|
||||
func (lp *Loadpoint) identifyVehicle() {
|
||||
identifier, ok := lp.charger.(api.Identifier)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id, err := identifier.Identify()
|
||||
if err != nil {
|
||||
lp.log.ERROR.Println("charger vehicle id:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if lp.vehicleIdentifier == id {
|
||||
return
|
||||
}
|
||||
|
||||
// vehicle found or removed
|
||||
lp.setVehicleIdentifier(id)
|
||||
|
||||
if id != "" {
|
||||
lp.log.DEBUG.Println("charger vehicle id:", id)
|
||||
|
||||
if vehicle := lp.selectVehicleByID(id); vehicle != nil {
|
||||
lp.stopVehicleDetection()
|
||||
lp.setActiveVehicle(vehicle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selectVehicleByID selects the vehicle with the given ID
|
||||
func (lp *Loadpoint) selectVehicleByID(id string) api.Vehicle {
|
||||
vehicles := lp.coordinatedVehicles()
|
||||
|
||||
// find exact match
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
if strings.EqualFold(id, vid) {
|
||||
return vehicle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find placeholder match
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
// case insensitive match
|
||||
re, err := regexp.Compile("(?i)" + strings.ReplaceAll(vid, "*", ".*?"))
|
||||
if err != nil {
|
||||
lp.log.ERROR.Printf("vehicle id: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if re.MatchString(id) {
|
||||
return vehicle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setActiveVehicle assigns currently active vehicle, configures soc estimator
|
||||
// and adds an odometer task
|
||||
func (lp *Loadpoint) setActiveVehicle(vehicle api.Vehicle) {
|
||||
lp.Lock()
|
||||
|
||||
if lp.vehicle == vehicle {
|
||||
lp.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
from := "unknown"
|
||||
if lp.vehicle != nil {
|
||||
lp.coordinator.Release(lp.vehicle)
|
||||
from = lp.vehicle.Title()
|
||||
}
|
||||
to := "unknown"
|
||||
if vehicle != nil {
|
||||
lp.coordinator.Acquire(vehicle)
|
||||
to = vehicle.Title()
|
||||
}
|
||||
lp.log.INFO.Printf("vehicle updated: %s -> %s", from, to)
|
||||
|
||||
lp.vehicle = vehicle
|
||||
|
||||
// reset minSoc and targetSoc before change
|
||||
lp.setMinSoc(0)
|
||||
lp.setTargetSoc(100)
|
||||
|
||||
// reset target energy
|
||||
lp.setTargetEnergy(0)
|
||||
|
||||
// unblock api
|
||||
lp.Unlock()
|
||||
|
||||
if vehicle != nil {
|
||||
lp.socUpdated = time.Time{}
|
||||
|
||||
// resolve optional config
|
||||
var estimate bool
|
||||
if lp.Soc.Estimate == nil || *lp.Soc.Estimate {
|
||||
estimate = true
|
||||
}
|
||||
lp.socEstimator = soc.NewEstimator(lp.log, lp.charger, vehicle, estimate)
|
||||
|
||||
lp.publish(vehiclePresent, true)
|
||||
lp.publish(vehicleTitle, lp.vehicle.Title())
|
||||
lp.publish(vehicleIcon, lp.vehicle.Icon())
|
||||
lp.publish(vehicleCapacity, lp.vehicle.Capacity())
|
||||
|
||||
lp.applyAction(vehicle.OnIdentified())
|
||||
lp.addTask(lp.vehicleOdometer)
|
||||
|
||||
lp.progress.Reset()
|
||||
} else {
|
||||
lp.socEstimator = nil
|
||||
|
||||
lp.publish(vehiclePresent, false)
|
||||
lp.publish(vehicleTitle, "")
|
||||
lp.publish(vehicleIcon, "")
|
||||
lp.publish(vehicleCapacity, int64(0))
|
||||
lp.publish(vehicleOdometer, 0.0)
|
||||
}
|
||||
|
||||
// re-publish vehicle settings
|
||||
lp.publish(phasesActive, lp.activePhases())
|
||||
lp.unpublishVehicle()
|
||||
|
||||
lp.updateSession(func(session *db.Session) {
|
||||
var title string
|
||||
if lp.vehicle != nil {
|
||||
title = lp.vehicle.Title()
|
||||
}
|
||||
|
||||
lp.session.Vehicle = title
|
||||
})
|
||||
}
|
||||
|
||||
func (lp *Loadpoint) wakeUpVehicle() {
|
||||
// charger
|
||||
if c, ok := lp.charger.(api.Resurrector); ok {
|
||||
if err := c.WakeUp(); err != nil {
|
||||
lp.log.ERROR.Printf("wake-up charger: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// vehicle
|
||||
if vs, ok := lp.vehicle.(api.Resurrector); ok {
|
||||
if err := vs.WakeUp(); err != nil {
|
||||
lp.log.ERROR.Printf("wake-up vehicle: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unpublishVehicle resets published vehicle data
|
||||
func (lp *Loadpoint) unpublishVehicle() {
|
||||
lp.vehicleSoc = 0
|
||||
|
||||
lp.publish(vehicleSoc, 0.0)
|
||||
lp.publish(vehicleRange, int64(0))
|
||||
lp.publish(vehicleTargetSoc, 0.0)
|
||||
|
||||
lp.setRemainingEnergy(0)
|
||||
lp.setRemainingDuration(0)
|
||||
|
||||
lp.vehiclePublishFeature(api.Offline)
|
||||
}
|
||||
|
||||
// vehicleHasFeature checks availability of vehicle feature
|
||||
func (lp *Loadpoint) vehicleHasFeature(f api.Feature) bool {
|
||||
v, ok := lp.vehicle.(api.FeatureDescriber)
|
||||
if ok {
|
||||
ok = v.Has(f)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// vehiclePublishFeature availability of vehicle features
|
||||
func (lp *Loadpoint) vehiclePublishFeature(f api.Feature) {
|
||||
lp.publish("vehicleFeature"+f.String(), lp.vehicleHasFeature(f))
|
||||
}
|
||||
|
||||
// vehicleUnidentified returns true if there are associated vehicles and detection is running.
|
||||
// It will also reset the api cache at regular intervals.
|
||||
// Detection is stopped after maximum duration and the "guest vehicle" message dispatched.
|
||||
func (lp *Loadpoint) vehicleUnidentified() bool {
|
||||
if lp.vehicle != nil || lp.vehicleDetect.IsZero() || len(lp.coordinatedVehicles()) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// stop detection
|
||||
if lp.clock.Since(lp.vehicleDetect) > vehicleDetectDuration {
|
||||
lp.stopVehicleDetection()
|
||||
lp.pushEvent(evVehicleUnidentified)
|
||||
return false
|
||||
}
|
||||
|
||||
// request vehicle api refresh while waiting to identify
|
||||
select {
|
||||
case <-lp.vehicleDetectTicker.C:
|
||||
lp.log.DEBUG.Println("vehicle api refresh")
|
||||
provider.ResetCached()
|
||||
default:
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// vehicleDefaultOrDetect will assign and update default vehicle or start detection
|
||||
func (lp *Loadpoint) vehicleDefaultOrDetect() {
|
||||
if lp.defaultVehicle != nil {
|
||||
if lp.vehicle != lp.defaultVehicle {
|
||||
lp.setActiveVehicle(lp.defaultVehicle)
|
||||
} else {
|
||||
// default vehicle is already active, update odometer anyway
|
||||
// need to do this here since setActiveVehicle would short-circuit
|
||||
lp.addTask(lp.vehicleOdometer)
|
||||
}
|
||||
} else if len(lp.coordinatedVehicles()) > 0 && lp.connected() {
|
||||
lp.startVehicleDetection()
|
||||
}
|
||||
}
|
||||
|
||||
// startVehicleDetection reset connection timer and starts api refresh timer
|
||||
func (lp *Loadpoint) startVehicleDetection() {
|
||||
// flush all vehicles before detection starts
|
||||
lp.log.DEBUG.Println("vehicle api refresh")
|
||||
provider.ResetCached()
|
||||
|
||||
lp.vehicleDetect = lp.clock.Now()
|
||||
lp.vehicleDetectTicker = lp.clock.Ticker(vehicleDetectInterval)
|
||||
lp.publish(vehicleDetectionActive, true)
|
||||
}
|
||||
|
||||
// stopVehicleDetection expires the connection timer and ticker
|
||||
func (lp *Loadpoint) stopVehicleDetection() {
|
||||
lp.vehicleDetect = time.Time{}
|
||||
if lp.vehicleDetectTicker != nil {
|
||||
lp.vehicleDetectTicker.Stop()
|
||||
}
|
||||
lp.publish(vehicleDetectionActive, false)
|
||||
}
|
||||
|
||||
// identifyVehicleByStatus validates if the active vehicle is still connected to the loadpoint
|
||||
func (lp *Loadpoint) identifyVehicleByStatus() {
|
||||
if len(lp.coordinatedVehicles()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if vehicle := lp.coordinator.IdentifyVehicleByStatus(); vehicle != nil {
|
||||
lp.stopVehicleDetection()
|
||||
lp.setActiveVehicle(vehicle)
|
||||
return
|
||||
}
|
||||
|
||||
// remove previous vehicle if status was not confirmed
|
||||
if _, ok := lp.vehicle.(api.ChargeState); ok {
|
||||
lp.setActiveVehicle(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// vehicleOdometer updates odometer
|
||||
func (lp *Loadpoint) vehicleOdometer() {
|
||||
if vs, ok := lp.vehicle.(api.VehicleOdometer); ok {
|
||||
if odo, err := vs.Odometer(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle odometer: %.0fkm", odo)
|
||||
lp.publish(vehicleOdometer, odo)
|
||||
|
||||
// update session once odometer is read
|
||||
lp.updateSession(func(session *db.Session) {
|
||||
session.Odometer = odo
|
||||
})
|
||||
} else {
|
||||
lp.log.ERROR.Printf("vehicle odometer: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// statusEvents converts the observed charger status change into a logical sequence of events
|
||||
func statusEvents(prevStatus, status api.ChargeStatus) []string {
|
||||
res := make([]string, 0, 2)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (lp *Loadpoint) GetTargetEnergy() float64 {
|
|||
// setTargetEnergy sets loadpoint charge target energy (no mutex)
|
||||
func (lp *Loadpoint) setTargetEnergy(energy float64) {
|
||||
lp.targetEnergy = energy
|
||||
lp.publish("targetEnergy", energy)
|
||||
lp.publish(targetEnergy, energy)
|
||||
}
|
||||
|
||||
// SetTargetEnergy sets loadpoint charge target energy
|
||||
|
|
@ -301,7 +301,7 @@ func (lp *Loadpoint) SetMinCurrent(current float64) {
|
|||
|
||||
if current != lp.MinCurrent {
|
||||
lp.MinCurrent = current
|
||||
lp.publish("minCurrent", lp.MinCurrent)
|
||||
lp.publish(minCurrent, lp.MinCurrent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ func (lp *Loadpoint) SetMaxCurrent(current float64) {
|
|||
|
||||
if current != lp.MaxCurrent {
|
||||
lp.MaxCurrent = current
|
||||
lp.publish("maxCurrent", lp.MaxCurrent)
|
||||
lp.publish(maxCurrent, lp.MaxCurrent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,10 +343,10 @@ func (lp *Loadpoint) SetRemainingDuration(chargeRemainingDuration time.Duration)
|
|||
}
|
||||
|
||||
// setRemainingDuration sets the estimated remaining charging duration (no mutex)
|
||||
func (lp *Loadpoint) setRemainingDuration(chargeRemainingDuration time.Duration) {
|
||||
if lp.chargeRemainingDuration != chargeRemainingDuration {
|
||||
lp.chargeRemainingDuration = chargeRemainingDuration
|
||||
lp.publish("chargeRemainingDuration", chargeRemainingDuration)
|
||||
func (lp *Loadpoint) setRemainingDuration(remainingDuration time.Duration) {
|
||||
if lp.chargeRemainingDuration != remainingDuration {
|
||||
lp.chargeRemainingDuration = remainingDuration
|
||||
lp.publish(chargeRemainingDuration, remainingDuration)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
305
core/loadpoint_vehicle.go
Normal file
305
core/loadpoint_vehicle.go
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/core/db"
|
||||
"github.com/evcc-io/evcc/core/soc"
|
||||
"github.com/evcc-io/evcc/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
vehicleDetectInterval = 1 * time.Minute
|
||||
vehicleDetectDuration = 10 * time.Minute
|
||||
)
|
||||
|
||||
// setVehicleIdentifier updated the vehicle id as read from the charger
|
||||
func (lp *Loadpoint) setVehicleIdentifier(id string) {
|
||||
if lp.vehicleIdentifier != id {
|
||||
lp.vehicleIdentifier = id
|
||||
lp.publish("vehicleIdentity", id)
|
||||
}
|
||||
}
|
||||
|
||||
// identifyVehicle reads vehicle identification from charger
|
||||
func (lp *Loadpoint) identifyVehicle() {
|
||||
identifier, ok := lp.charger.(api.Identifier)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id, err := identifier.Identify()
|
||||
if err != nil {
|
||||
lp.log.ERROR.Println("charger vehicle id:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if lp.vehicleIdentifier == id {
|
||||
return
|
||||
}
|
||||
|
||||
// vehicle found or removed
|
||||
lp.setVehicleIdentifier(id)
|
||||
|
||||
if id != "" {
|
||||
lp.log.DEBUG.Println("charger vehicle id:", id)
|
||||
|
||||
if vehicle := lp.selectVehicleByID(id); vehicle != nil {
|
||||
lp.stopVehicleDetection()
|
||||
lp.setActiveVehicle(vehicle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selectVehicleByID selects the vehicle with the given ID
|
||||
func (lp *Loadpoint) selectVehicleByID(id string) api.Vehicle {
|
||||
vehicles := lp.coordinatedVehicles()
|
||||
|
||||
// find exact match
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
if strings.EqualFold(id, vid) {
|
||||
return vehicle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find placeholder match
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
// case insensitive match
|
||||
re, err := regexp.Compile("(?i)" + strings.ReplaceAll(vid, "*", ".*?"))
|
||||
if err != nil {
|
||||
lp.log.ERROR.Printf("vehicle id: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if re.MatchString(id) {
|
||||
return vehicle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setActiveVehicle assigns currently active vehicle, configures soc estimator
|
||||
// and adds an odometer task
|
||||
func (lp *Loadpoint) setActiveVehicle(vehicle api.Vehicle) {
|
||||
lp.Lock()
|
||||
|
||||
if lp.vehicle == vehicle {
|
||||
lp.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
from := "unknown"
|
||||
if lp.vehicle != nil {
|
||||
lp.coordinator.Release(lp.vehicle)
|
||||
from = lp.vehicle.Title()
|
||||
}
|
||||
to := "unknown"
|
||||
if vehicle != nil {
|
||||
lp.coordinator.Acquire(vehicle)
|
||||
to = vehicle.Title()
|
||||
}
|
||||
lp.log.INFO.Printf("vehicle updated: %s -> %s", from, to)
|
||||
|
||||
lp.vehicle = vehicle
|
||||
|
||||
// reset minSoc and targetSoc before change
|
||||
lp.setMinSoc(0)
|
||||
lp.setTargetSoc(100)
|
||||
|
||||
// reset target energy
|
||||
lp.setTargetEnergy(0)
|
||||
|
||||
// unblock api
|
||||
lp.Unlock()
|
||||
|
||||
if vehicle != nil {
|
||||
lp.socUpdated = time.Time{}
|
||||
|
||||
// resolve optional config
|
||||
var estimate bool
|
||||
if lp.Soc.Estimate == nil || *lp.Soc.Estimate {
|
||||
estimate = true
|
||||
}
|
||||
lp.socEstimator = soc.NewEstimator(lp.log, lp.charger, vehicle, estimate)
|
||||
|
||||
lp.publish(vehiclePresent, true)
|
||||
lp.publish(vehicleTitle, lp.vehicle.Title())
|
||||
lp.publish(vehicleIcon, lp.vehicle.Icon())
|
||||
lp.publish(vehicleCapacity, lp.vehicle.Capacity())
|
||||
|
||||
lp.applyAction(vehicle.OnIdentified())
|
||||
lp.addTask(lp.vehicleOdometer)
|
||||
|
||||
lp.progress.Reset()
|
||||
} else {
|
||||
lp.socEstimator = nil
|
||||
|
||||
lp.publish(vehiclePresent, false)
|
||||
lp.publish(vehicleTitle, "")
|
||||
lp.publish(vehicleIcon, "")
|
||||
lp.publish(vehicleCapacity, int64(0))
|
||||
lp.publish(vehicleOdometer, 0.0)
|
||||
}
|
||||
|
||||
// re-publish vehicle settings
|
||||
lp.publish(phasesActive, lp.activePhases())
|
||||
lp.unpublishVehicle()
|
||||
|
||||
lp.updateSession(func(session *db.Session) {
|
||||
var title string
|
||||
if lp.vehicle != nil {
|
||||
title = lp.vehicle.Title()
|
||||
}
|
||||
|
||||
lp.session.Vehicle = title
|
||||
})
|
||||
}
|
||||
|
||||
func (lp *Loadpoint) wakeUpVehicle() {
|
||||
// charger
|
||||
if c, ok := lp.charger.(api.Resurrector); ok {
|
||||
if err := c.WakeUp(); err != nil {
|
||||
lp.log.ERROR.Printf("wake-up charger: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// vehicle
|
||||
if vs, ok := lp.vehicle.(api.Resurrector); ok {
|
||||
if err := vs.WakeUp(); err != nil {
|
||||
lp.log.ERROR.Printf("wake-up vehicle: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unpublishVehicle resets published vehicle data
|
||||
func (lp *Loadpoint) unpublishVehicle() {
|
||||
lp.vehicleSoc = 0
|
||||
|
||||
lp.publish(vehicleSoc, 0.0)
|
||||
lp.publish(vehicleRange, int64(0))
|
||||
lp.publish(vehicleTargetSoc, 0.0)
|
||||
|
||||
lp.setRemainingEnergy(0)
|
||||
lp.setRemainingDuration(0)
|
||||
|
||||
lp.vehiclePublishFeature(api.Offline)
|
||||
}
|
||||
|
||||
// vehicleHasFeature checks availability of vehicle feature
|
||||
func (lp *Loadpoint) vehicleHasFeature(f api.Feature) bool {
|
||||
v, ok := lp.vehicle.(api.FeatureDescriber)
|
||||
if ok {
|
||||
ok = v.Has(f)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// vehiclePublishFeature availability of vehicle features
|
||||
func (lp *Loadpoint) vehiclePublishFeature(f api.Feature) {
|
||||
lp.publish("vehicleFeature"+f.String(), lp.vehicleHasFeature(f))
|
||||
}
|
||||
|
||||
// vehicleUnidentified returns true if there are associated vehicles and detection is running.
|
||||
// It will also reset the api cache at regular intervals.
|
||||
// Detection is stopped after maximum duration and the "guest vehicle" message dispatched.
|
||||
func (lp *Loadpoint) vehicleUnidentified() bool {
|
||||
if lp.vehicle != nil || lp.vehicleDetect.IsZero() || len(lp.coordinatedVehicles()) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// stop detection
|
||||
if lp.clock.Since(lp.vehicleDetect) > vehicleDetectDuration {
|
||||
lp.stopVehicleDetection()
|
||||
lp.pushEvent(evVehicleUnidentified)
|
||||
return false
|
||||
}
|
||||
|
||||
// request vehicle api refresh while waiting to identify
|
||||
select {
|
||||
case <-lp.vehicleDetectTicker.C:
|
||||
lp.log.DEBUG.Println("vehicle api refresh")
|
||||
provider.ResetCached()
|
||||
default:
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// vehicleDefaultOrDetect will assign and update default vehicle or start detection
|
||||
func (lp *Loadpoint) vehicleDefaultOrDetect() {
|
||||
if lp.defaultVehicle != nil {
|
||||
if lp.vehicle != lp.defaultVehicle {
|
||||
lp.setActiveVehicle(lp.defaultVehicle)
|
||||
} else {
|
||||
// default vehicle is already active, update odometer anyway
|
||||
// need to do this here since setActiveVehicle would short-circuit
|
||||
lp.addTask(lp.vehicleOdometer)
|
||||
}
|
||||
} else if len(lp.coordinatedVehicles()) > 0 && lp.connected() {
|
||||
lp.startVehicleDetection()
|
||||
}
|
||||
}
|
||||
|
||||
// startVehicleDetection reset connection timer and starts api refresh timer
|
||||
func (lp *Loadpoint) startVehicleDetection() {
|
||||
// flush all vehicles before detection starts
|
||||
lp.log.DEBUG.Println("vehicle api refresh")
|
||||
provider.ResetCached()
|
||||
|
||||
lp.vehicleDetect = lp.clock.Now()
|
||||
lp.vehicleDetectTicker = lp.clock.Ticker(vehicleDetectInterval)
|
||||
lp.publish(vehicleDetectionActive, true)
|
||||
}
|
||||
|
||||
// stopVehicleDetection expires the connection timer and ticker
|
||||
func (lp *Loadpoint) stopVehicleDetection() {
|
||||
lp.vehicleDetect = time.Time{}
|
||||
if lp.vehicleDetectTicker != nil {
|
||||
lp.vehicleDetectTicker.Stop()
|
||||
}
|
||||
lp.publish(vehicleDetectionActive, false)
|
||||
}
|
||||
|
||||
// identifyVehicleByStatus validates if the active vehicle is still connected to the loadpoint
|
||||
func (lp *Loadpoint) identifyVehicleByStatus() {
|
||||
if len(lp.coordinatedVehicles()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if vehicle := lp.coordinator.IdentifyVehicleByStatus(); vehicle != nil {
|
||||
lp.stopVehicleDetection()
|
||||
lp.setActiveVehicle(vehicle)
|
||||
return
|
||||
}
|
||||
|
||||
// remove previous vehicle if status was not confirmed
|
||||
if _, ok := lp.vehicle.(api.ChargeState); ok {
|
||||
lp.setActiveVehicle(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// vehicleOdometer updates odometer
|
||||
func (lp *Loadpoint) vehicleOdometer() {
|
||||
if vs, ok := lp.vehicle.(api.VehicleOdometer); ok {
|
||||
if odo, err := vs.Odometer(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle odometer: %.0fkm", odo)
|
||||
lp.publish(vehicleOdometer, odo)
|
||||
|
||||
// update session once odometer is read
|
||||
lp.updateSession(func(session *db.Session) {
|
||||
session.Odometer = odo
|
||||
})
|
||||
} else {
|
||||
lp.log.ERROR.Printf("vehicle odometer: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue