Skip to content

Loadpoint: keep vehicle SoC when re-assigning the same default vehicle#31063

Draft
Alexxtheonly wants to merge 1 commit into
evcc-io:masterfrom
Alexxtheonly:fix/keep-vehiclesoc-on-reassign
Draft

Loadpoint: keep vehicle SoC when re-assigning the same default vehicle#31063
Alexxtheonly wants to merge 1 commit into
evcc-io:masterfrom
Alexxtheonly:fix/keep-vehiclesoc-on-reassign

Conversation

@Alexxtheonly

Copy link
Copy Markdown
Contributor

Problem

A loadpoint with a default vehicle: whose SoC is known (e.g. a teslamate vehicle reading SoC over MQTT) publishes vehicleSoc = 0 to /api/state (and messaging) while charging, even though the per-cycle vehicle soc: NN% debug log shows the correct value every cycle. It recovers after a clean physical connect (charger status A→B→C) but gets stuck at 0 after an evcc restart that happens mid-charge. vehicleRange/vehicleOdometer are zeroed the same way.

Root cause

setActiveVehicle() calls unpublishVehicle() unconditionally at the end of every invocation, which sets lp.vehicleSoc = 0 and publishes 0 for soc/range/limit/odometer.

vehicleDefaultOrDetect() re-assigns the already-active default vehicle on every reconnect (introduced in #27364 to fix the estimator gradient on reconnect, #27359):

// Always call setActiveVehicle even if defaultVehicle is already active.
// ... resets the estimator's initial values for the new charging session.
lp.setActiveVehicle(lp.defaultVehicle)

So when the active vehicle does not change, unpublishVehicle() still wipes a perfectly valid SoC. publishSocAndRange() only repopulates vehicleSoc on a cycle where a SoC is actually read, so whenever a same-vehicle re-assign lands without an immediately-following successful read (e.g. the repeated reconnect/charger out of sync churn after a mid-charge restart), the published value stays 0.

Fix

Only reset the published vehicle data when the active vehicle actually changes (from != to). The soc estimator is still rebuilt unconditionally for v != nil (lines just above), so the #27359 fresh-estimator-on-reconnect behaviour is unchanged. Switching to a different vehicle — or to none on disconnect — still clears the stale data as before.

-	lp.unpublishVehicle()
+	if from != to {
+		lp.unpublishVehicle()
+	}

This does not touch the vehicle-detection path: with a pinned default vehicle, vehicleDefaultOrDetect takes the defaultVehicle != nil branch and never runs detection.

Test

Added TestReassignActiveVehicleKeepsSoc, which sets a known vehicleSoc, re-assigns the same active vehicle, and asserts the SoC is preserved — then asserts it is still cleared when the vehicle changes. It fails on master (expected 71, actual 0) and passes with the fix. Full ./core/... suite passes; gofmt/go vet/modernize clean.

Reproduction

  • Loadpoint with a teslamate default vehicle (SoC via MQTT), actively charging.
  • Restart evcc while the car is mid-charge.
  • /api/state loadpoints[].vehicleSoc stays 0 while the logs show vehicle soc: NN% every cycle.

🤖 Generated with Claude Code

setActiveVehicle always called unpublishVehicle, which zeroes vehicleSoc
(and range/odometer) and publishes 0. vehicleDefaultOrDetect re-assigns
the already-active default vehicle on every (re)connect to refresh the
soc estimator for the new session (evcc-io#27359, evcc-io#27364), so unpublishVehicle
ran even when the active vehicle did not change.

For a vehicle whose SoC is known (e.g. teslamate via MQTT), this makes
vehicleSoc/vehicleRange flap to 0 until the next successful soc read. It
is most visible after an evcc restart mid-charge, where the synthesized
reconnect path keeps re-assigning the default vehicle and the published
SoC stays 0 while charging.

Only reset the published vehicle data when the active vehicle actually
changes (from != to). The soc estimator is still rebuilt unconditionally
for v != nil, so the evcc-io#27359 fresh-estimator behaviour is preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@andig

andig commented Jun 21, 2026

Copy link
Copy Markdown
Member

So when the active vehicle does not change, unpublishVehicle() still wipes a perfectly valid SoC.

Does this refer to the restart? At restart, evcc simply does not know what was assigned before and doing so is hence correct. Seems I'm missing the actual root cause here.

Comment thread core/loadpoint_vehicle.go
lp.publish(keys.PhasesActive, lp.ActivePhases())
lp.unpublishVehicle()

// only reset published vehicle data when the active vehicle actually changed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep comments to 2 lines please? Your AI ignores the agents.md?

@andig andig marked this pull request as draft June 21, 2026 09:36
@github-actions github-actions Bot added the stale Outdated and ready to close label Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale Outdated and ready to close

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants