Loadpoint: add asleep messaging event if wakeup fails (#23116)

This commit is contained in:
andig 2025-08-24 16:50:41 +02:00 • committed by GitHub
parent 4b0524e392
commit 0f70bb3106
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 63 additions and 27 deletions

View file

@ -17,6 +17,9 @@
# guest:
# title: Unknown vehicle
# msg: Unknown vehicle, guest connected?
# asleep:
# title: Vehicle asleep
# msg: Charge release, vehicle {{ if .vehicleTitle }}{{ .vehicleTitle }} {{ end }}not charging.
#services:
#- type: pushover

View file

@ -40,6 +40,7 @@ const (
evVehicleDisconnect = "disconnect" // vehicle disconnected
evVehicleSoc = "soc" // vehicle soc progress
evVehicleUnidentified = "guest" // vehicle unidentified
evVehicleAsleep = "asleep" // vehicle doesn't charge
pvTimer = "pv"
pvEnable = "enable"
@ -1220,6 +1221,9 @@ func (lp *Loadpoint) scalePhases(phases int) error {
// update setting and reset timer
lp.SetPhases(phases)
// some vehicles may hang on phase switch
lp.startWakeUpTimer()
}
return nil
@ -1972,8 +1976,13 @@ func (lp *Loadpoint) Update(sitePower, batteryBoostPower float64, consumption, f
// Wake-up checks
if lp.enabled && lp.status == api.StatusB &&
// TODO take vehicle api limits into account
int(lp.vehicleSoc) < lp.EffectiveLimitSoc() && lp.wakeUpTimer.Expired() {
lp.wakeUpVehicle()
!lp.chargerHasFeature(api.IntegratedDevice) && int(lp.vehicleSoc) < lp.EffectiveLimitSoc() {
switch lp.wakeUpTimer.Elapsed() {
case WakeUpTimerElapsed:
lp.wakeUpVehicle()
case WakeUpTimerFinished:
lp.pushEvent(evVehicleAsleep)
}
}
// effective disabled status

View file

@ -399,6 +399,7 @@ func TestPvScalePhasesTimer(t *testing.T) {
lp := &Loadpoint{
log: util.NewLogger("foo"),
wakeUpTimer: NewTimer(),
clock: clock,
charger: charger,
minCurrent: minA,
@ -453,8 +454,9 @@ func TestScalePhasesIfAvailable(t *testing.T) {
phaseCharger := api.NewMockPhaseSwitcher(ctrl)
lp := &Loadpoint{
log: util.NewLogger("foo"),
clock: clock.NewMock(),
log: util.NewLogger("foo"),
clock: clock.NewMock(),
wakeUpTimer: NewTimer(),
charger: struct {
*api.MockCharger
*api.MockPhaseSwitcher

View file

@ -741,11 +741,12 @@ func TestPVHysteresisAfterPhaseSwitch(t *testing.T) {
Voltage = 100
lp := &Loadpoint{
log: util.NewLogger("foo"),
clock: clock,
charger: charger,
minCurrent: minA,
maxCurrent: maxA,
log: util.NewLogger("foo"),
wakeUpTimer: NewTimer(),
clock: clock,
charger: charger,
minCurrent: minA,
maxCurrent: maxA,
Disable: loadpoint.ThresholdConfig{
Delay: dt,
},

View file

@ -7,9 +7,18 @@ import (
"github.com/benbjohnson/clock"
)
const wakeupTimeout = 30 * time.Second
const (
wakeupTimeout = 30 * time.Second
wakeupAttempts = 6
)
const wakeupAttempts = 6 // wakeupAttempts is the count of wakeup attempts
type WakeUpEvent int
const (
WakeUpTimerInactive WakeUpEvent = iota
WakeUpTimerElapsed
WakeUpTimerFinished
)
// Timer measures active time between start and stop events
type Timer struct {
@ -48,20 +57,22 @@ func (m *Timer) Stop() {
m.started = time.Time{}
}
// Expired checks if the timer has elapsed and if resets its status
func (m *Timer) Expired() bool {
// Elapsed checks if the timer has elapsed and if resets its status
func (m *Timer) Elapsed() WakeUpEvent {
m.Lock()
defer m.Unlock()
res := !m.started.IsZero() && (m.clck.Since(m.started) >= wakeupTimeout)
if res {
m.wakeupAttemptsLeft--
if m.wakeupAttemptsLeft == 0 {
m.started = time.Time{}
} else {
m.started = m.clck.Now()
}
if m.started.IsZero() || m.clck.Since(m.started) < wakeupTimeout {
return WakeUpTimerInactive
}
return res
if m.wakeupAttemptsLeft == 0 {
m.started = time.Time{}
return WakeUpTimerFinished
}
m.wakeupAttemptsLeft--
m.started = m.clck.Now()
return WakeUpTimerElapsed
}

View file

@ -16,15 +16,22 @@ func TestTimer(t *testing.T) {
// start
at.Start()
// maximum 2 attempts
at.wakeupAttemptsLeft = 2
clck.Add(10 * time.Second)
require.False(t, at.Expired())
require.Equal(t, WakeUpTimerInactive, at.Elapsed())
// wait another 20 sec to expire the timer - this will reset the timer as well
clck.Add(wakeupTimeout + 10*time.Second)
require.True(t, at.Expired())
require.Equal(t, WakeUpTimerElapsed, at.Elapsed())
// start
at.Start()
// elapse
clck.Add(time.Minute)
require.True(t, at.Expired())
require.Equal(t, WakeUpTimerElapsed, at.Elapsed())
// elapse
clck.Add(time.Minute)
require.Equal(t, WakeUpTimerFinished, at.Elapsed())
}

View file

@ -214,6 +214,9 @@ messaging:
guest: # vehicle could not be identified
title: Unknown vehicle
msg: Unknown vehicle, guest connected?
asleep: # vehicle doesn't start charging
title: Vehicle asleep
msg: Charge release, vehicle {{ if .vehicleTitle }}{{ .vehicleTitle }} {{ end }}not charging.
services:
# - type: pushover
# app: # app id