Repository navigation
fix(loadpoint): surplus counts our orders, not a charger's late reading - #1850
Conversation
Easee Cloud reports a charger's draw about 10 s late. Surplus added that reading back as the car's draw, so after a pause it still counted the stopped car's old draw as spare solar, and after a raise it raised again before the first had landed. Both landed in import and the car paused about every 80 s. Surplus now counts each charger at what the meter may show of our orders. A raise on its way already claims its room: it counts at its order and no further raise goes out until a reading shows it, so only import lowers it meanwhile. A charger ordered off counts at 0. A smaller cut keeps the reading, which shows the draw the meter still has until the cut lands. Seven field-matrix readback rows and the sitesim row "9 kW surplus, readback 10 s late" pass. The three 25 A empty/none rows never order anything, also with a fast charger; they are relabelled nostep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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: 2878b1b716
ℹ️ 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".
| adjW, raisingW := c.surplusOrderAdjustW(now, lpCfg.ID) | ||
| instant := surplusW + adjW |
There was a problem hiding this comment.
Apply the order adjustment before flooring surplus at zero
When solar disappears after a restart but before the charger's readback catches up, this adjustment can hide grid import entirely. The production reader in go/cmd/ftw/main.go calls SurplusAvailableForEVW, which already floors negative surplus to zero (go/internal/loadpoint/surplus_reserve.go:127–135). For example, with a pending 6,900 W order, a late 0 W EV reading, no battery flow and 6,900 W grid import, the reader returns zero and this adjustment turns it back into 6,900 W of apparent surplus. Surplus-only charging therefore continues importing until readback catches up or the settle timeout expires, instead of reducing immediately. Apply the adjustment to the signed surplus before its zero floor so the import remains visible.
Useful? React with 👍 / 👎.
| if reading < 0.9*order { | ||
| o.below = true | ||
| } else if o.below { | ||
| o.shown = true |
There was a problem hiding this comment.
Allow small raises to settle without a ten-percent dip
For normal one-amp raises at higher charging currents, neither the old nor the new reading satisfies this condition, so o.below never becomes true and the raise cannot be acknowledged. For example, a three-phase 10 A → 11 A raise changes the order from 6,900 W to 7,590 W, but the previous 6,900 W reading is already above 0.9 * 7590 (6,831 W). Even after telemetry reports exactly 7,590 W, raisingW remains set and the new raise gate prevents using additional available solar for the full three-minute timeout. Track evidence relative to the previous order or fresh telemetry so small, successfully applied raises can settle too.
Useful? React with 👍 / 👎.
Review: a raise counted as shown only after a reading under 0.9 x the order, so a one-step raise from 9 A up (6210 -> 6900 W) never showed and held the next raise for the full 180 s, even on a fast charger. A reading now shows a raise once it passes halfway from the order before, after one short of halfway; a high reading from before a cut still does not count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…adback # Conflicts: # go/internal/control/testdata/sitesim_field_known.txt
Cause
Surplus charging measures the solar left for the car as
−grid + battery + the car's draw. The car's draw came from the charger's own reading, which Easee Cloud reports about 10 s late. Two things went wrong:In both cases the car landed in import and paused, about every 80 s.
Change
go/internal/loadpoint/controller.go: surplus now counts each charger at what the meter may show of our own orders, not at its late reading (surplusOrderAdjustW).A charger whose readings keep up with its draw ramps a little slower: each raise waits until a reading shows it. On the matrix's fast-charger surplus rows that costs about 0.2 kWh in an hour, 4.5 → 4.3 and 3.8 → 3.6 kWh. That is solar not used, not import.
Deleted: nothing in production code.
Numbers
On master 2ec5a1d, which has #1847, #1849 and #1852, the field matrix goes from 19 failing cases, all
readback, to 0 of 342. The known file now lists no rows, and its labels are gone.sitesim "9 kW surplus, readback 10 s late" (5 min), mean power to the car:
Field matrix: 342 cases. On master 30 fail (idle 23, own-raise 7); here 23 fail (idle 16, own-raise 7).
testdata/sitesim_field_known.txt, because these rows pass now:timing/25A/{3p/3p,auto/mixed}/{easee40s,cloud60s,easee120s}/car3p/L1/surplus idle readback(6 rows);probe/25A/auto/mixed/easee40s/car3p/balanced/surplus+goal+export2500W idle readback.timing/25A/empty/none/{easee40s,cloud60s,easee120s}/car3p/L1/surplusfromreadbacktonostep. Withphase_modeunset and no step table these never order anything, even with a fast charger, so readback was never their cause. The header now namesnostepand dropsreadback.With signed phase currents applied locally as well, 15 cases fail. Both 25 A readback groups pass, and so do all eight 16 A
exportrows, including the six that the direction PR finds hunting like these. What still fails: 8idlerows inempty/none, which are the nostep cause, and the 7own-raise reliefrows.Tests:
TestSurplusCountsOurOrdersNotALateReading(loadpoint).TestSiteSimRandomHouseandTestSiteSimScenariosare green, with no new fuse or own-raise failures.go vetandgo test -p 2 ./internal/loadpoint/ ./internal/control/ ./cmd/ftw/are green.Independent review (Sonnet)
No way to import more than master was found. 316 of 316 non-surplus matrix rows are unchanged; only 9 surplus rows differ.
TestSurplusCountsOurOrdersNotALateReading.SurplusReserveWstill uses the late reading, so the home battery can take room a raise on its way has claimed. There is no two-charger or battery surplus case in the matrix.computeSurplusCmdend to end with each half removed.A second look at the halfway rule found nothing blocking.
c.surplusOrderstakes no lock. It is touched only from the tick goroutine, but the neighbouring maps usesurplusMu. Another loadpoint's raise can inflate this one's room, which is part of the existing two-surplus-charger TODO.Hardware
Not yet run on a real Easee Cloud charger. Real-hardware evidence is still needed: a surplus session through a pause and restart on Easee Cloud.
🤖 Generated with Claude Code