Skip to content

feat(loadpoint): finish charging at the vehicle limit - #1297

Merged
frahlg merged 4 commits into
masterfrom
fix/ev-vehicle-limit-20260917
Sep 17, 2026
Merged

frahlg merged 4 commits into
masterfrom
fix/ev-vehicle-limit-20260917

Conversation

@frahlg

@frahlg frahlg commented Sep 17, 2026

Copy link
Copy Markdown
Member

Problem and change

FTW stopped an EV session when its inferred SoC reached 80.01%, although the driver reported that the car showed 78%. A model estimate must not prove that the car has reached its own charge limit.

Add an explicit finish_at_vehicle_limit schedule goal. Keep price planning while energy remains, then continue offering charge when the estimate runs out or the deadline passes. Use fresh vehicle SoC and charge limit when available. A fresh vehicle Complete report ends a one-shot goal; an inferred percentage cannot. Charger refusal remains distinct from confirmed completion. Core still applies manual Stop, meter freshness, fuse and installation limits.

Keep existing percentage goals unchanged. Persist the goal identity, original one-shot deadline and confirmed completion across restart. Match active sessions by stable hardware identity. Advertise vehicle_limit_goal_supported for clients. State schema 7 prevents rollback to Core versions that ignore the new stored goal mode.

Paired UI: #1295. It must require the capability flag before saving the new goal. No UI changes in this PR; API and architecture edits use sections separate from the existing schedule-auth and Ask why PRs.

Refs #1293. The issue remains open until the paired UI and a connected-car validation have completed. This PR does not claim to fix the separate storage latency and control I/O issues #1246 and #1247.

Validation

  • PATH=/opt/homebrew/bin:$PATH make verify: passed.
  • Race tests for vehicle-limit goals: passed.
  • Linux arm64 regression binary on the user's Raspberry Pi, TestVehicleLimit, two runs: passed. These tests use a simulated charger; they do not prove a physical charge cycle.
  • Tests cover the reported 78%/inferred-80% case, estimated target exhaustion, price pauses, past deadlines, fresh and stale completion reports, charger refusal, manual Stop, fuse and freshness stops, stable identity, restart and one-shot completion.

The running box has not been updated. A charger-only setup cannot confirm the car's exact SoC or limit; it can continue offering charge until the car declines it without claiming that this proves completion.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-17T13:45:38.483712Z bc797db PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc797db740

ℹ️ 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".

Comment thread go/cmd/ftw/main.go Outdated
if !ok || !st.PluggedIn {
return loadpoint.VehicleChargeState{}, false
}
pick := telemetry.PickBestVehicleForLoadpoint(tel, st.CurrentPowerW > loadpoint.DeliveringW, time.Now())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bind completion telemetry to the requested loadpoint

When a loadpoint is idle and multiple vehicle drivers publish telemetry, this callback ignores lpID when choosing a vehicle: PickBestVehicleForLoadpoint scans all vehicle readings and returns the globally highest-ranked/freshest one. Consequently, a fresh Complete state from a different parked car can be passed to vehicleCompletionOffer, which permanently marks this loadpoint's one-shot goal complete and stops charging the actual connected car. Completion must require a vehicle-to-loadpoint association rather than the idle-state heuristic.

Useful? React with 👍 / 👎.

defer m.sessionMu.Unlock()
m.mu.Lock()
lp := m.byID[id]
if lp == nil || !lp.finishAtVehicleLimit || lp.schedule.Recurring || !lp.pluggedIn {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Roll recurring goals after the vehicle reports completion

For a recurring vehicle-limit goal, a fresh Complete response exits here without recording completion, while RollSchedules skips every plugged-in unfinished vehicle-limit goal. If the car remains connected across the next deadline—or an unplug/replug occurs between polls—the target time therefore remains permanently expired; once vehicle telemetry becomes stale or changes from Complete, vehicleCompletionOffer treats the deadline as overdue and commands maximum power immediately instead of rolling to and price-planning for the next daily deadline.

Useful? React with 👍 / 👎.

@frahlg

frahlg commented Sep 17, 2026

Copy link
Copy Markdown
Member Author

Addressed both review findings in 87f23c6 and 14910a8:

  • Completion now rejects several vehicle sources or connected loadpoints, readings from before the observed connection, and a Complete report that conflicts with measured charging. Those checks have regression tests.
  • Confirmed completion now lets a recurring goal roll to the next daily deadline. Fresh renewed demand reopens it; a new plug session cannot inherit the previous session's completed recurring goal. One-shot goals remain completed until explicitly saved again.
  • A charger refusal has its own command reason and cannot claim confirmed completion.

The full local make verify, focused race tests (two runs), and the final Linux arm64 VehicleLimit regression binary on the Raspberry Pi (two runs) pass. Physical EV charging is still untested because the car is disconnected. The separate storage I/O issues #1246 and #1247 remain open.

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
…ng goals

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg force-pushed the fix/ev-vehicle-limit-20260917 branch from 14910a8 to 82c5a86 Compare September 17, 2026 14:07
@frahlg
frahlg merged commit 2da8102 into master Sep 17, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant