Vehicle: add wakeupdisabled feature to suppress wake-up calls (#31461)

This commit is contained in:
andig 2026-07-04 14:04:38 +02:00 • committed by GitHub
parent 3a560d1254
commit 2bd319a361
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 67 additions and 10 deletions

View file

@ -18,4 +18,5 @@ const (
WelcomeCharge // vehicle
ClimaterDisabled // vehicle - ignore climater state for charge control
AutodetectDisabled // vehicle - do not try to identify vehicle by status
WakeUpDisabled // vehicle - do not send wake-up calls
)

View file

@ -7,11 +7,11 @@ import (
"strings"
)
const _FeatureName = "CoarseCurrentIntegratedDeviceSwitchDeviceHeatingContinuousAverageCacheableOfflineRetryableStreamingWelcomeChargeClimaterDisabledAutodetectDisabled"
const _FeatureName = "CoarseCurrentIntegratedDeviceSwitchDeviceHeatingContinuousAverageCacheableOfflineRetryableStreamingWelcomeChargeClimaterDisabledAutodetectDisabledWakeUpDisabled"
var _FeatureIndex = [...]uint8{0, 13, 29, 41, 48, 58, 65, 74, 81, 90, 99, 112, 128, 146}
var _FeatureIndex = [...]uint8{0, 13, 29, 41, 48, 58, 65, 74, 81, 90, 99, 112, 128, 146, 160}
const _FeatureLowerName = "coarsecurrentintegrateddeviceswitchdeviceheatingcontinuousaveragecacheableofflineretryablestreamingwelcomechargeclimaterdisabledautodetectdisabled"
const _FeatureLowerName = "coarsecurrentintegrateddeviceswitchdeviceheatingcontinuousaveragecacheableofflineretryablestreamingwelcomechargeclimaterdisabledautodetectdisabledwakeupdisabled"
func (i Feature) String() string {
i -= 1
@ -38,9 +38,10 @@ func _FeatureNoOp() {
_ = x[WelcomeCharge-(11)]
_ = x[ClimaterDisabled-(12)]
_ = x[AutodetectDisabled-(13)]
_ = x[WakeUpDisabled-(14)]
}
var _FeatureValues = []Feature{CoarseCurrent, IntegratedDevice, SwitchDevice, Heating, Continuous, Average, Cacheable, Offline, Retryable, Streaming, WelcomeCharge, ClimaterDisabled, AutodetectDisabled}
var _FeatureValues = []Feature{CoarseCurrent, IntegratedDevice, SwitchDevice, Heating, Continuous, Average, Cacheable, Offline, Retryable, Streaming, WelcomeCharge, ClimaterDisabled, AutodetectDisabled, WakeUpDisabled}
var _FeatureNameToValueMap = map[string]Feature{
_FeatureName[0:13]: CoarseCurrent,
@ -69,6 +70,8 @@ var _FeatureNameToValueMap = map[string]Feature{
_FeatureLowerName[112:128]: ClimaterDisabled,
_FeatureName[128:146]: AutodetectDisabled,
_FeatureLowerName[128:146]: AutodetectDisabled,
_FeatureName[146:160]: WakeUpDisabled,
_FeatureLowerName[146:160]: WakeUpDisabled,
}
var _FeatureNames = []string{
@ -85,6 +88,7 @@ var _FeatureNames = []string{
_FeatureName[99:112],
_FeatureName[112:128],
_FeatureName[128:146],
_FeatureName[146:160],
}
// FeatureString retrieves an enum value from the enum constants string name.

View file

@ -962,7 +962,7 @@ func (lp *Loadpoint) setLimit(current float64) error {
if err != nil {
v := lp.GetVehicle()
if vv, ok := api.Cap[api.Resurrector](v); ok && errors.Is(err, api.ErrAsleep) {
if vv, ok := api.Cap[api.Resurrector](v); ok && errors.Is(err, api.ErrAsleep) && !hasFeature(v, api.WakeUpDisabled) {
// https://github.com/evcc-io/evcc/issues/8254
// wakeup vehicle
lp.log.DEBUG.Printf("set charge current limit: waking up vehicle")
@ -983,7 +983,7 @@ func (lp *Loadpoint) setLimit(current float64) error {
if enabled := current >= effMinCurrent; enabled != lp.enabled {
if err := lp.charger.Enable(enabled); err != nil {
v := lp.GetVehicle()
if vv, ok := api.Cap[api.Resurrector](v); enabled && ok && errors.Is(err, api.ErrAsleep) {
if vv, ok := api.Cap[api.Resurrector](v); enabled && ok && errors.Is(err, api.ErrAsleep) && !hasFeature(v, api.WakeUpDisabled) {
// https://github.com/evcc-io/evcc/issues/8254
// wakeup vehicle
lp.log.DEBUG.Printf("charger %s: waking up vehicle", status[enabled])
@ -1959,6 +1959,10 @@ func (lp *Loadpoint) processTasks() {
// startWakeUpTimer starts wakeUpTimer
func (lp *Loadpoint) startWakeUpTimer() {
if lp.vehicleHasFeature(api.WakeUpDisabled) {
return
}
lp.log.DEBUG.Printf("wake-up timer: start")
lp.wakeUpTimer.Start()
}

View file

@ -218,6 +218,7 @@ func TestPvScalePhases(t *testing.T) {
vehicle := api.NewMockVehicle(ctrl)
vehicle.EXPECT().Phases().Return(tc.vehicle).MinTimes(1)
vehicle.EXPECT().OnIdentified().Return(api.ActionConfig{}).AnyTimes()
vehicle.EXPECT().Features().AnyTimes()
lp := &Loadpoint{
log: util.NewLogger("foo"),

View file

@ -353,6 +353,34 @@ func TestDisconnectIntegratedDeviceKeepsMode(t *testing.T) {
assert.Equal(t, api.ModeOff, lp.GetMode(), "integrated device disconnect must not reset mode")
}
func TestStartWakeUpTimerDisabled(t *testing.T) {
for _, tc := range []struct {
name string
features []api.Feature
wantRunning bool
}{
{"enabled", nil, true},
{"disabled", []api.Feature{api.WakeUpDisabled}, false},
} {
t.Run(tc.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
vehicle := api.NewMockVehicle(ctrl)
vehicle.EXPECT().Features().Return(tc.features).AnyTimes()
lp := &Loadpoint{
log: util.NewLogger("foo"),
vehicle: vehicle,
wakeUpTimer: NewTimer(),
}
lp.startWakeUpTimer()
assert.Equal(t, tc.wantRunning, lp.wakeUpTimer.Running())
})
}
}
func TestReconnectVehicle(t *testing.T) {
tc := []struct {
name string

View file

@ -22,6 +22,8 @@ params:
deprecated: true
- name: autodetectdisabled
deprecated: true
- name: wakeupdisabled
deprecated: true
render: |
type: custom
{{ include "vehicle-common" . }}

View file

@ -16,6 +16,8 @@ params:
deprecated: true
- name: autodetectdisabled
deprecated: true
- name: wakeupdisabled
deprecated: true
render: |
type: custom
{{- include "vehicle-common" . }}

View file

@ -342,6 +342,14 @@ params:
help:
en: Ignore the vehicle for auto detection of connected vehicles
de: Das Fahrzeug von der automatischen Erkennung beim Anschließen eines Fahrzeugs ausschließen
- name: wakeupdisabled
type: bool
description:
en: Disable wake-up calls
de: Aufweck-Aufrufe deaktivieren
help:
en: Do not send wake-up calls to the vehicle or charger, useful for vehicles that refuse to charge after repeated wake-ups.
de: Keine Aufweck-Aufrufe an Fahrzeug oder Ladegerät senden, hilfreich bei Fahrzeugen, die nach wiederholtem Aufwecken das Laden verweigern.
- name: heating
type: bool
description:
@ -611,11 +619,15 @@ presets:
advanced: true
- name: autodetectdisabled
advanced: true
- name: wakeupdisabled
advanced: true
vehicle-online:
- name: climaterdisabled
advanced: true
- name: autodetectdisabled
advanced: true
- name: wakeupdisabled
advanced: true
charger-features:
- name: heating
advanced: true

View file

@ -1,5 +1,5 @@
{{ define "vehicle-features" }}
{{- if or .basefeatures (eq .coarsecurrent "true") (eq .welcomecharge "true") (eq .streaming "true") (eq .climaterdisabled "true") (eq .autodetectdisabled "true") }}
{{- if or .basefeatures (eq .coarsecurrent "true") (eq .welcomecharge "true") (eq .streaming "true") (eq .climaterdisabled "true") (eq .autodetectdisabled "true") (eq .wakeupdisabled "true") }}
features:
{{- range .basefeatures }}
- {{ . }}
@ -19,5 +19,8 @@ features:
{{- if eq .autodetectdisabled "true" }}
- autodetectdisabled
{{- end }}
{{- if eq .wakeupdisabled "true" }}
- wakeupdisabled
{{- end }}
{{- end }}
{{- end }}

View file

@ -42,9 +42,9 @@ func TestTemplates(t *testing.T) {
})
}
// universalVehicleFeatures render via the shared vehicle-features include, so a
// onlineVehicleFeatures render via the shared vehicle-features include, so a
// stored config may carry them; dropping the param breaks reload (discussion #31291).
var universalVehicleFeatures = []string{"climaterdisabled", "autodetectdisabled"}
var onlineVehicleFeatures = []string{"climaterdisabled", "autodetectdisabled", "wakeupdisabled"}
func TestVehicleFeatureParamsConsistent(t *testing.T) {
for _, tmpl := range templates.ByClass(templates.Vehicle, templates.WithDeprecated()) {
@ -52,7 +52,7 @@ func TestVehicleFeatureParamsConsistent(t *testing.T) {
continue
}
for _, feat := range universalVehicleFeatures {
for _, feat := range onlineVehicleFeatures {
values := tmpl.Defaults(templates.RenderModeUnitTest)
values["template"] = tmpl.Template
values[feat] = true