Repository navigation
fix(ev): plan from a newer car SoC than the manual level - #1550
Conversation
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
Signed-off-by: segran2 <268038031+segran2@users.noreply.github.com>
A cloud vehicle report older than five minutes was shown on the charger card and left out of the plan, so an earlier 27% slider or plug-in assumption kept scheduling a full charge. A reading inside the one-hour anchor window now starts the plan when it is newer than that confirmation, including a cached or driver-stale replay, and the charging view uses the same number. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0419ca0710
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // measured SoC — cloud drivers such as vag_vehicle mark a reading | ||
| // stale once it leaves their own fresh window, which is well | ||
| // inside VehicleAnchorMaxAge. Age, not the flag, bounds planning. | ||
| if meta.Stale && use == vehiclePickLive { |
There was a problem hiding this comment.
Keep stale anchors from confirming charge completion
When an idle loadpoint has no confirmation newer than a cached or driver-stale vehicle reading, and that reading is within the one-hour anchor window and at or above the target, this condition admits it through PickVehicleForAnchor; the control loop then passes it to AnchorVehicleSoCAt, which sets completionNotified and publishes ChargingSessionComplete. That can produce a false “Car charged” notification from telemetry explicitly deemed unsuitable for live control, while suppressing the later real completion notification. Stale readings may update the planning estimate, but they must not use the completion-emitting anchor path.
AGENTS.md reference: AGENTS.md:L22-L23
Useful? React with 👍 / 👎.
| if !lp.socConfirmed || lp.socConfirmedAt.IsZero() { | ||
| return true |
There was a problem hiding this comment.
Persist the manual confirmation timestamp
When a manually corrected session is restored after a process restart, savedSession restores socConfirmed but never socConfirmedAt; this branch therefore treats the restored confirmation as timestamp-less and allows any vehicle observation within the anchor window to replace it, even if that cloud observation predates the user's correction. A restart can thus discard the newer durable manual level and plan from an older cached value, so the confirmation time needs to be persisted/restored or restored confirmations need a precedence-safe fallback.
AGENTS.md reference: AGENTS.md:L20-L21
Useful? React with 👍 / 👎.
Problem and result
Göran Andersson's charger card showed the Audi at 78% from
vag_vehicle(34 minutes old) while the plan still started at 27%, the old slider or plug-in assumption, and scheduled about 50 kWh up to 80%. The car then delivered no power and the card said Not following.The plan now starts from that car reading. The charging view's "Planning from" line uses the same number.
Root cause
Two different freshness rules were in force.
PickBestVehicleForDisplay, from fix(ev): preserve vehicle SoC provenance and age #1470) keeps a cached or driver-stale vehicle SoC and shows its age. That is the 78% / "From Car · 34 min old" line.PickBestVehicleForLoadpointrequiresVehicleMaxAge(5 minutes) and rejectsstale=true.vag_vehiclemarks a portal report stale after 22.5 minutes and emits it withsoc_fresh=false. A 34-minute report therefore never became the plan's start SoC.soc_sourceassumed), which is why the card also said "confirm the current level". The slider was hidden whenever any car SoC was on screen, so the stale 27% could not be corrected and was never replaced.A second hole remained after the anchor-picker change in #1549: a report that had once been fresh, then was replayed with
stale=trueas it aged past the driver's own fresh window, was still dropped. That is the usual VW/Audi portal path (a file arrives within 20 minutes, then the same measurement is replayed until it is about 45 minutes old).Freshness policy
staleflag. A stale charge limit does not cap the plan. A cached report cannot mark the goal complete.VehicleAnchorMaxAge). Cloud sources such as the VW Group portal report about every 15 minutes; the driver itself stops emitting after 45 minutes. Inside that hour, a cached report (soc_fresh=false) and a driver-stale replay both count. The plan starts from the measurement plus energy the session has delivered since that time.planning_soc/planning_from_vehicleon the loadpoint are that same choice, and the charging view plans from them.Scope and safety
This branch includes the commits from #1549 (cached observations can anchor, slider stays available for a stale displayed report) and continues from there. Merging this covers that PR; the two should not both land.
Not in scope: when the car is already at its own limit and the charger reports no power ("limited by charger current"), the plan can still offer energy until the existing completion latch decides the car has stopped asking. A stale charge limit is intentionally not treated as that stop point. That is a separate refusal-to-charge behaviour, not the 27% vs 78% start.
Positive power remains into the site. The planner output is still validated by core before any command. No driver or optimizer change.
Verification
go test ./internal/telemetry/ ./internal/loadpoint/ ./internal/api/ -run 'Planning|Vehicle|Cached|Anchor|LoadpointPlanning|DecorateLoadpoints|PickBest|PickVehicle'go test ./internal/telemetry/ ./internal/loadpoint/node --test web/ev-plan.test.mjs web/ev-plan-status.test.mjsgo test -c ./cmd/ftw/(package builds)go veton telemetry, loadpoint, api, and cmd/ftwCovered cases: manual 27% set earlier and a 34-minute driver report of 78% later, the plan starts at 78% (and adds energy delivered after the measurement once); a driver report older than one hour stays on 27%; no driver stays on 27%; a manual level set after the measurement stays on 27%.
The charging-view text is asserted in the web test. I did not drive the full UI in a browser against a vehicle.
Checklist