From b636d37f6c3fe76d6f1044f456ce3a1cfab3bd45 Mon Sep 17 00:00:00 2001 From: Ingo <71161062+iseeberg79@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:25:29 +0100 Subject: [PATCH] Loadpoint: show remaining duration when soc estimator is disabled (#27772) --- core/loadpoint.go | 39 +++++++++++++++++++++++---------------- core/soc/estimator.go | 17 +++++++++++------ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/core/loadpoint.go b/core/loadpoint.go index 45401bb02..fe7a271af 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -1765,10 +1765,10 @@ func (lp *Loadpoint) publishSocAndRange() { return socR, limitR } - soc, limit := socAndLimit("charger", lp.charger) - if soc == nil && (lp.vehicleSocPollAllowed() || lp.chargerHasFeature(api.IntegratedDevice)) { + socR, limitR := socAndLimit("charger", lp.charger) + if socR == nil && (lp.vehicleSocPollAllowed() || lp.chargerHasFeature(api.IntegratedDevice)) { lp.socUpdated = lp.clock.Now() - soc, limit = socAndLimit("vehicle", lp.GetVehicle()) + socR, limitR = socAndLimit("vehicle", lp.GetVehicle()) // range if vs, ok := lp.GetVehicle().(api.VehicleRange); ok { @@ -1781,35 +1781,42 @@ func (lp *Loadpoint) publishSocAndRange() { } } - if soc != nil { + if socR != nil { if socEstimator == nil { - lp.vehicleSoc = *soc + lp.vehicleSoc = *socR } else { - lp.vehicleSoc = socEstimator.Soc(soc, lp.GetChargedEnergy()) + lp.vehicleSoc = socEstimator.Soc(socR, lp.GetChargedEnergy()) lp.log.DEBUG.Printf("vehicle soc (estimator): %.0f%%", lp.vehicleSoc) } } lp.publish(keys.VehicleSoc, lp.vehicleSoc) apiLimitSoc := 100 - if limit != nil { - apiLimitSoc = int(*limit) + if limitR != nil { + apiLimitSoc = int(*limitR) // https://github.com/evcc-io/evcc/issues/13349 - lp.publish(keys.VehicleLimitSoc, float64(*limit)) + lp.publish(keys.VehicleLimitSoc, float64(*limitR)) } - if socEstimator != nil { - // use minimum of vehicle and loadpoint - limitSoc := min(apiLimitSoc, lp.EffectiveLimitSoc()) + limitSoc := min(apiLimitSoc, lp.EffectiveLimitSoc()) + v := lp.GetVehicle() - var d time.Duration + var d time.Duration + var e float64 + switch { + case socEstimator != nil: if lp.charging() { d = socEstimator.RemainingChargeDuration(float64(limitSoc), lp.chargePower) } - lp.SetRemainingDuration(d) - - lp.SetRemainingEnergy(socEstimator.RemainingChargeEnergy(limitSoc)) + e = socEstimator.RemainingChargeEnergy(limitSoc) + case v != nil && v.Capacity() > 0 && lp.vehicleSoc > 0: + if lp.charging() { + d = soc.RemainingChargeDuration(float64(limitSoc), lp.chargePower, lp.vehicleSoc, v.Capacity()) + } + e = soc.RemainingChargeEnergy(limitSoc, lp.vehicleSoc, v.Capacity()) } + lp.SetRemainingDuration(d) + lp.SetRemainingEnergy(e) // trigger message after variables are updated lp.bus.Publish(evVehicleSoc, lp.vehicleSoc) diff --git a/core/soc/estimator.go b/core/soc/estimator.go index fa5d0591e..f1d241877 100644 --- a/core/soc/estimator.go +++ b/core/soc/estimator.go @@ -78,14 +78,19 @@ func remainingChargeDuration(targetSoc, chargePower, vehicleSoc, virtualCapacity // RemainingChargeEnergy returns the remaining charge energy in kWh func (s *Estimator) RemainingChargeEnergy(targetSoc int) float64 { - percentRemaining := float64(targetSoc) - s.vehicleSoc - if percentRemaining <= 0 || s.virtualCapacity <= 0 { + return remainingChargeEnergy(targetSoc, s.vehicleSoc, s.virtualCapacity) +} + +func RemainingChargeEnergy(targetSoc int, vehicleSoc, capacity float64) float64 { + return remainingChargeEnergy(targetSoc, vehicleSoc, capacity*1e3/ChargeEfficiency) +} + +func remainingChargeEnergy(targetSoc int, vehicleSoc, virtualCapacity float64) float64 { + percentRemaining := float64(targetSoc) - vehicleSoc + if percentRemaining <= 0 || virtualCapacity <= 0 { return 0 } - - // estimate remaining energy - whRemaining := percentRemaining / 100 * s.virtualCapacity - return whRemaining / 1e3 + return percentRemaining / 100 * virtualCapacity / 1e3 } // Soc replaces the api.Vehicle.Soc interface to take charged energy into account