Loadpoint: add welcomecharge feature (#13534)

This commit is contained in:
andig 2024-04-25 13:36:35 +02:00 • committed by GitHub
parent 846ef7b6dd
commit 1d4e69591f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 48 additions and 13 deletions

View file

@ -10,4 +10,5 @@ const (
IntegratedDevice
Heating
Retryable
WelcomeCharge
)

View file

@ -7,11 +7,11 @@ import (
"strings"
)
const _FeatureName = "OfflineCoarseCurrentIntegratedDeviceHeatingRetryable"
const _FeatureName = "OfflineCoarseCurrentIntegratedDeviceHeatingRetryableWelcomeCharge"
var _FeatureIndex = [...]uint8{0, 7, 20, 36, 43, 52}
var _FeatureIndex = [...]uint8{0, 7, 20, 36, 43, 52, 65}
const _FeatureLowerName = "offlinecoarsecurrentintegrateddeviceheatingretryable"
const _FeatureLowerName = "offlinecoarsecurrentintegrateddeviceheatingretryablewelcomecharge"
func (i Feature) String() string {
i -= 1
@ -30,9 +30,10 @@ func _FeatureNoOp() {
_ = x[IntegratedDevice-(3)]
_ = x[Heating-(4)]
_ = x[Retryable-(5)]
_ = x[WelcomeCharge-(6)]
}
var _FeatureValues = []Feature{Offline, CoarseCurrent, IntegratedDevice, Heating, Retryable}
var _FeatureValues = []Feature{Offline, CoarseCurrent, IntegratedDevice, Heating, Retryable, WelcomeCharge}
var _FeatureNameToValueMap = map[string]Feature{
_FeatureName[0:7]: Offline,
@ -45,6 +46,8 @@ var _FeatureNameToValueMap = map[string]Feature{
_FeatureLowerName[36:43]: Heating,
_FeatureName[43:52]: Retryable,
_FeatureLowerName[43:52]: Retryable,
_FeatureName[52:65]: WelcomeCharge,
_FeatureLowerName[52:65]: WelcomeCharge,
}
var _FeatureNames = []string{
@ -53,6 +56,7 @@ var _FeatureNames = []string{
_FeatureName[20:36],
_FeatureName[36:43],
_FeatureName[43:52],
_FeatureName[52:65],
}
// FeatureString retrieves an enum value from the enum constants string name.

View file

@ -19,8 +19,8 @@ func NewAdapter(lp loadpoint.API, c *Coordinator) API {
}
}
func (a *adapter) GetVehicles() []api.Vehicle {
return a.c.GetVehicles()
func (a *adapter) GetVehicles(availableOnly bool) []api.Vehicle {
return a.c.GetVehicles(availableOnly)
}
func (a *adapter) Owner(v api.Vehicle) loadpoint.API {

View file

@ -7,8 +7,8 @@ import (
// API is the coordinator API
type API interface {
// GetVehicles returns the list of all vehicles
GetVehicles() []api.Vehicle
// GetVehicles returns the list of all vehicles, filtered by availability
GetVehicles(availableOnly bool) []api.Vehicle
// Owner returns the loadpoint that currently owns the vehicle
Owner(api.Vehicle) loadpoint.API

View file

@ -1,7 +1,6 @@
package coordinator
import (
"slices"
"sync"
"github.com/evcc-io/evcc/api"
@ -27,11 +26,18 @@ func New(log *util.Logger, vehicles []api.Vehicle) *Coordinator {
}
// GetVehicles returns the list of all vehicles
func (c *Coordinator) GetVehicles() []api.Vehicle {
func (c *Coordinator) GetVehicles(availableOnly bool) []api.Vehicle {
c.mu.RLock()
defer c.mu.RUnlock()
return slices.Clone(c.vehicles)
res := make([]api.Vehicle, 0, len(c.vehicles))
for _, v := range c.vehicles {
if _, tracked := c.tracked[v]; !availableOnly || availableOnly && !tracked {
res = append(res, v)
}
}
return res
}
// Owner returns the loadpoint that currently owns the vehicle

View file

@ -12,7 +12,7 @@ func NewDummy() API {
return new(dummy)
}
func (a *dummy) GetVehicles() []api.Vehicle {
func (a *dummy) GetVehicles(_ bool) []api.Vehicle {
return nil
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"math"
"reflect"
"slices"
"strings"
"sync"
"testing"
@ -470,6 +471,9 @@ func (lp *Loadpoint) evVehicleConnectHandler() {
lp.socEstimator.Reset()
}
// get pv mode before vehicle defaults are applied
pvMode := lp.GetMode() == api.ModePV || lp.GetMode() == api.ModeMinPV
// set default or start detection
if !lp.chargerHasFeature(api.IntegratedDevice) {
lp.vehicleDefaultOrDetect()
@ -478,6 +482,18 @@ func (lp *Loadpoint) evVehicleConnectHandler() {
// immediately allow pv mode activity
lp.elapsePVTimer()
// Enable charging on connect if any available vehicle requires it. We're using the PV timer
// to disable after the welcome, hence this must be placed after elapsePVTimer.
// TODO check is this doesn't conflict with vehicle defaults like mode: off
if pvMode {
for _, v := range lp.availableVehicles() {
if slices.Contains(v.Features(), api.WelcomeCharge) {
lp.setLimit(lp.effectiveMinCurrent())
break
}
}
}
// create charging session
lp.createSession()
}

View file

@ -20,12 +20,20 @@ const (
vehicleDetectDuration = 10 * time.Minute
)
// availableVehicles is the slice of vehicles from the coordinator that are available
func (lp *Loadpoint) availableVehicles() []api.Vehicle {
if lp.coordinator == nil {
return nil
}
return lp.coordinator.GetVehicles(true)
}
// coordinatedVehicles is the slice of vehicles from the coordinator
func (lp *Loadpoint) coordinatedVehicles() []api.Vehicle {
if lp.coordinator == nil {
return nil
}
return lp.coordinator.GetVehicles()
return lp.coordinator.GetVehicles(false)
}
// setVehicleIdentifier updated the vehicle id as read from the charger