fix(loadpoint): start EV in 3Φ when live surplus already covers it - #300
Merged
Conversation
pickSurplusSteps decided 3Φ-only vs all-allowed based on FORECAST peak surplus (next 30 min, then whole-day). A pessimistic forecast that underestimates current sun would make the controller pick the all-allowed step set; the wake-kick then fires at 1380 W (1Φ min) and the EV starts in 1Φ even when the live meter has 5+ kW free. Adds a live-surplus override: when there's no prior phase decision this session AND the live PV-surplus reader returns ≥ 4140 W, lock to 3Φ-only immediately and record the decision so the dwell logic downstream keeps us there. Forecast-based logic still owns the mid-session and "configured low-PV day" cases. User-observed regression: Easee started in phaseMode=1 → wake-kick at 1380 W → contactor opened to switch phases → EV gave up before the 3Φ ramp completed. With this change the first kick is 4140 W already on a sunny start. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the loadpoint controller’s pickSurplusSteps selection so an EV can start in 3Φ immediately when live PV surplus already meets the 3Φ minimum, even if the forecast (near-term) is pessimistic, avoiding an initial 1Φ start that can later disrupt charging when switching phases.
Changes:
- Add a session-start live-surplus override that immediately selects the 3Φ-only step set and records a dwell decision.
- Emit an informational log when the live-surplus override triggers.
- Add unit tests covering “live surplus high overrides pessimistic near-term forecast” and “live surplus low preserves 1Φ-inclusive steps”.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| go/internal/loadpoint/controller.go | Adds live-surplus override and records phase decision to enforce 3Φ at session start. |
| go/internal/loadpoint/controller_phase_handoff_test.go | Adds tests validating step-set selection with live-surplus override behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1564
to
+1579
| if !hasPrev { | ||
| if liveSurplus, ok := c.siteSurplusForEVW(); ok && | ||
| !math.IsNaN(liveSurplus) && !math.IsInf(liveSurplus, 0) && | ||
| liveSurplus >= minStep3 { | ||
| c.phaseLockMu.Lock() | ||
| if c.phaseSelected3P == nil { | ||
| c.phaseSelected3P = map[string]bool{} | ||
| c.phaseSelectedAt = map[string]time.Time{} | ||
| } | ||
| c.phaseSelected3P[lpCfg.ID] = true | ||
| c.phaseSelectedAt[lpCfg.ID] = now | ||
| c.phaseLockMu.Unlock() | ||
| slog.Info("loadpoint surplus_only: 3Φ at session start (live surplus override)", | ||
| "lp", lpCfg.ID, "live_surplus_w", liveSurplus, "min_3p_step_w", minStep3) | ||
| return steps3 | ||
| } |
Comment on lines
+135
to
+141
| func TestPickSurplusSteps_LiveSurplusOverridesForecastAtSessionStart(t *testing.T) { | ||
| cfg := phaseLoadpoint("auto", 0, 0) | ||
| dir := &Directive{SlotStart: time.Now(), SlotEnd: time.Now().Add(15 * time.Minute)} | ||
| sender := &fakeSender{} | ||
| samples := map[string]EVSample{cfg.DriverName: {Connected: true, PowerW: 0}} | ||
| c := newTestController(t, []Config{cfg}, dir, samples, sender) | ||
|
|
| c.phaseSelected3P[lpCfg.ID] = true | ||
| c.phaseSelectedAt[lpCfg.ID] = now | ||
| c.phaseLockMu.Unlock() | ||
| slog.Info("loadpoint surplus_only: 3Φ at session start (live surplus override)", |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pickSurplusStepsdecides 3Φ-only vs all-allowed (1Φ + 3Φ) step set based on forecast peak surplus (nearTermPeakSurplusWfor next 30 min, thenpeakRemainingSurplusWfor whole-day). When the forecast under-estimates current sun, the controller picks the all-allowed set, the wake-kick fires at1380 W(1Φ min), and the EV starts in 1Φ even when live surplus is 5+ kW. Switching the contactor to 3Φ later can interrupt the ramp and stop the EV before steady state.Adds a live-surplus override: at session start (no prior phase decision recorded today), if the live PV-surplus reader returns ≥ 4140 W, lock the step set to 3Φ-only and record the decision so the dwell-hold logic downstream keeps us there. Forecast-based logic still owns the mid-session, day-rollover, and configured low-PV-day cases.
What this does NOT change
phaseSwitchMinHolddwell + day-long lock logic owns the choice.peakRemainingSurplusWstill locks 1Φ for the whole day when today's forecast peak can't sustain 3Φ.pick_phases— the Easee driver still gets to pickphaseModefrom the operator'smode+ the requested W. This just changes which step set the controller offers to snap to.Test
TestPickSurplusSteps_LiveSurplusOverridesForecastAtSessionStart— pessimistic forecast (3 kW peak) but live surplus 5.5 kW → smallest step = 4140 W (3Φ).TestPickSurplusSteps_LowLiveSurplusKeeps1PSteps— same forecast + live surplus 2.5 kW → smallest step = 1380 W (1Φ, preserves current behavior).Test plan
loadpoint surplus_only: 3Φ at session start (live surplus override) live_surplus_w=… min_3p_step_w=4140.phaseMode → 3and the wake-kick fires at 4140 W (not 1380 W).phaseSwitchMinHoldelapses (no flap).🤖 Generated with Claude Code