Skip to content

Loadpoint: estimate remaining charge for plan goal while plan slot is active - #35266

Merged
andig merged 4 commits into
masterfrom
fix/plan-remaining-estimate
Oct 25, 2026
Merged

andig merged 4 commits into
masterfrom
fix/plan-remaining-estimate

Conversation

@naltatis

Copy link
Copy Markdown
Member

fixes #35228

Inside an active plan slot, charging already continues past the session limit up to the plan goal. The charge end and remaining energy estimate now follow that goal instead of stopping at the limit.

  • Charge end and remaining energy use the plan SoC while a plan slot is active and the plan goal is above the limit
  • Outside plan slots (e.g. PV charging), the estimate keeps using the limit, since charging stops there
  • Vehicle charge limit still caps the estimate

🤖 Generated with Claude Code

@github-actions github-actions Bot added ux User experience/ interface vehicles Specific vehicle support labels Oct 24, 2026

@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 found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="core/loadpoint.go" line_range="2382" />
<code_context>
-	limitSoc := min(apiLimitSoc, lp.EffectiveLimitSoc())
+	limitSoc := lp.EffectiveLimitSoc()
+	// an active plan slot charges past the session limit, see Update()
+	if lp.planActive {
+		limitSoc = max(limitSoc, lp.EffectivePlanSoc())
+	}
</code_context>
<issue_to_address>
**Plan estimates race with mode changes**

When a mode change overlaps publishing the estimate, `publishSocAndRange` reads `lp.planActive` while `setPlanActive` writes it under the loadpoint lock, causing a Go data race and allowing the estimate to use inconsistent state.

Read `lp.planActive` under the loadpoint lock, using a consistent state snapshot for the estimate.
</issue_to_address>

### Comment 2
<location path="core/loadpoint.go" line_range="2382-2383" />
<code_context>
-	limitSoc := min(apiLimitSoc, lp.EffectiveLimitSoc())
+	limitSoc := lp.EffectiveLimitSoc()
+	// an active plan slot charges past the session limit, see Update()
+	if lp.planActive {
+		limitSoc = max(limitSoc, lp.EffectivePlanSoc())
+	}
+	limitSoc = min(apiLimitSoc, limitSoc)
</code_context>
<issue_to_address>
**Estimate exceeds actual charge target**

When a plan slot is active while `optimizerPlan` is false, `publishSocAndRange` uses the plan SoC whenever `planActive` is true, but `Update` only lets charging pass the session limit when `optimizerPlan` is true. With an active slot but no optimizer suggestion (or outside smart mode), the estimate runs to the plan goal even though charging stops at the session limit.

Use the same condition that allows charging past the session limit when selecting the plan SoC for the estimate.
</issue_to_address>

### Comment 3
<location path="core/loadpoint.go" line_range="2382" />
<code_context>
-	limitSoc := min(apiLimitSoc, lp.EffectiveLimitSoc())
+	limitSoc := lp.EffectiveLimitSoc()
+	// an active plan slot charges past the session limit, see Update()
+	if lp.planActive {
+		limitSoc = max(limitSoc, lp.EffectivePlanSoc())
+	}
</code_context>
<issue_to_address>
**Charge estimates lag plan slots**

When a plan slot starts or ends between `Update` calls, `publishSocAndRange` reads the previous `lp.planActive` value because `Update` recalculates it later in `plannerActive`, so the remaining-charge and charge-end estimates use the old target until the next update.

Recalculate `lp.planActive` before calling `publishSocAndRange`.
</issue_to_address>

### Comment 4
<location path="core/loadpoint.go" line_range="2381" />
<code_context>

-	limitSoc := min(apiLimitSoc, lp.EffectiveLimitSoc())
+	limitSoc := lp.EffectiveLimitSoc()
+	// an active plan slot charges past the session limit, see Update()
+	if lp.planActive {
+		limitSoc = max(limitSoc, lp.EffectivePlanSoc())
</code_context>
<issue_to_address>
**Caller reference in comment**

The comment explicitly directs readers to `Update()`, the caller of `publishSocAndRange`, violating the rule against caller references in comments.

Remove the `see Update()` reference while retaining the reason for the behavior.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread core/loadpoint.go Resolved
Comment thread core/loadpoint.go Resolved
Comment thread core/loadpoint.go Resolved
Comment thread core/loadpoint.go Resolved
@andig

andig commented Oct 25, 2026

Copy link
Copy Markdown
Member

Inside an active plan slot, charging already continues past the session limit up to the plan goal

Wdym? What has plan goal to do with active slot?

@andig
andig enabled auto-merge (squash) October 25, 2026 11:28
@andig
andig merged commit 75493e6 into master Oct 25, 2026
12 checks passed
@andig
andig deleted the fix/plan-remaining-estimate branch October 25, 2026 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ux User experience/ interface vehicles Specific vehicle support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prognostiziertes Ladeende nutzt allgemeines Ladelimit statt Limit des aktiven Ladeplans

2 participants