Skip to content

fix(loadpoint): solar-only auto runs on three phases above the minimum - #1853

Merged
frahlg merged 1 commit into
masterfrom
fix/surplus-auto-three-phases
Oct 25, 2026
Merged

frahlg merged 1 commit into
masterfrom
fix/surplus-auto-three-phases

Conversation

@frahlg

@frahlg frahlg commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Built on #1852 (surplus on a charger with no step table), now merged. This PR is one commit on master. It touches pickSurplusSteps and surplus3PhaseSteps, which #1852 also changed.

Fixes the site rig's known failure s5s-25: "auto stays on one phase at 9 kW surplus". s5-25 (no step table) is #1852's.

Cause

The rig's loadpoint is surplus-only, phase mode auto, with Easee's steps. It has 9.3 kW of surplus, and its plan shows no PV.

  1. Tick 1: the live surplus is above the three-phase minimum and there is no choice on file, so pickSurplusSteps picks three phases.
  2. Tick 2: the choice is on file, so the live check is skipped. The near-term dwell holds three phases. The day check then reads the plan's peak, 0 W, and sets the day's one-phase lock. Nothing ends that lock before midnight.
  3. After 30 min the near-term dwell would have gone to one phase on the plan's 0 W as well.

A second fault sits under it. surplus3PhaseSteps counted 3,680 W, which is 16 A on one phase, as a three-phase step. So the three-phase minimum in the phase choice was 3,680 W, not 4,140 W.

Change

Smaller effects: a one-phase charger left on an empty mode, and a table on a one-phase site, now pause below their smallest step (1,380 W), not below 3,680 W. A bat-SoC unlock (SurplusOnly false) still never sets the day lock.

Before and after

Check #1852 (base) this branch
TestSurplusAboveTheThreePhaseMinimumRunsThreePhases: 9.3 kW, plan without PV, 45 min order 1 of 540 is 3,680 W on 1p every order 3p, 8,970 W, past the 30 min hold
TestSurplusBelowTheThreePhaseMinimumRunsOnePhase: 3.8 kW (rig s5t) first order 4,140 W on 3p, which imports 1p throughout
TestSurplusOnePhaseYieldsAfterTheHold: 2 kW, then 9 kW, then 2 kW; dwell and day lock 1p all day 1p, 3p after 30 min, 1p after 30 more
TestSurplusOneHighReadingKeepsOnePhase: 1.5 kW and one 4,300 W reading 1p 1p (a guard for the floor)
TestSurplusOnePhaseOfferStaysWithinTheCharger, three-phase case asserted 1p at 3,680 W: the s5s behaviour 3p at 8,970 W; the 16 A per phase check stays
Field matrix (342 cases) known rows same known rows; no change to sitesim_field_known.txt

With the window average alone as the floor, timing/25A/auto/mixed/fast2s/car3p/L1/surplus failed idle. Export fell from 9 kW to 3 kW, the average held three phases for 8 s, the pause gate paused, and its 35 s hold kept the car idle. Taking the lower of the reading and the average keeps that row green.

go/test/site/scenarios_test.go: deleted s5s-25's known line, since the rig fails a known criterion that passes. #1852 already deleted s5-25's. The rig passed s5s-25 on 4ad0798, this commit on an earlier #1852 base, with both planner engines. Later rebases changed only the lines next to #1852's no-table return.

go vet and go test for ./internal/..., ./cmd/ftw/... and ./test/site/ pass, the field matrix among them (342 cases, the known rows unchanged). go test -race passes for loadpoint and control. Since #1852 merged, this PR targets master.

An independent review (Sonnet) found no blocker. It found that one reading chose the phase, fixed with the reading-and-average floor. A second look after the rebase found nothing in this diff.

Open

  • With no PV in the plan, one low reading at the end of a hold still starts a 30 min one-phase hold. Master stayed on one phase all day there.
  • Not run on real hardware. Hardware evidence is still needed: surplus on auto with an Easee above and below 4.1 kW.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 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-10-25T14:24:13.401672Z 8d6d03b 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.

@frahlg
frahlg force-pushed the fix/surplus-auto-three-phases branch from ca2cbb5 to 4ad0798 Compare October 25, 2026 14:38
frahlg added a commit that referenced this pull request Oct 25, 2026
From #1853's review: the early return computed the three-phase steps
before throwing them away, and an owner who removed the table on a day
the charger was locked to one phase, or held on one, kept that lock
until a restart. It now returns before anything else and clears the
loadpoint's one-phase lock and phase hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frahlg
frahlg force-pushed the fix/surplus-auto-three-phases branch 2 times, most recently from 4e40379 to 52c1777 Compare October 25, 2026 15:21
frahlg added a commit that referenced this pull request Oct 25, 2026
…ble (#1852)

* fix(control): phase current takes its direction from the phase's power

Fuse protection read each phase's current as a magnitude, so 20 A of
solar export counted as 20 A of import and a car never started on an
exporting site. A phase now takes its direction from the same phase's
power in the meter reading (P1 and the SDM630 report current as a
magnitude); without that phase's power the current counts as import.
An exporting phase counts at its reading for a rise and a start, and
is never over the limit for an EV cut, since less EV load would raise
its export.

The sitesim meter now reports phase power. The two "export start"
scenarios, two field-matrix rows and the rig's s2-16 pass. Six cloud
rows that stopped failing on export now start and fail on the late
readback; they carry that label. Five rows with phase_mode unset and
no step table never order a step, a separate cause, labelled nostep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(loadpoint): surplus orders any power on a charger with no step table

A charger with no step table, as the settings page saves a new one,
takes any power from its minimum to its maximum. surplus3PhaseSteps
returned [0] for it, so surplus snapped every order to 0 W and the car
never charged from solar. With no table the surplus steps are none, as
SnapChargeW reads them, and the smallest step surplus may order is the
charger's minimum: below it surplus still pauses.

Stacked on the phase-direction fix: on master the export counted as
import hid this. Two field-matrix rows pass; three cloud rows now start
and fail on the late readback, and carry that label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(control): a phase exports only when its power shows it clearly

Review of the direction fix: with no check, a driver whose phase power
had the wrong sign would turn 40 A of import into export, and the EV
would never be cut. A phase now counts as export only when every phase
power is present, they add up to the reading's total within a tenth or
250 W, and the phase's power is below -25 W and a quarter of what its
current could carry. Otherwise it counts as import, as before the fix:
a phase near 0 W, a reactive phase, a mismatched driver.

Three more field-matrix rows (25 A, phase_mode unset, no step table,
cloud chargers) never order a step either; they carry nostep, not
readback. docs/safety.md says how the direction is read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(control): the sign guard takes the site total from its caller

The check that phase powers add up to the total read meter.RawW in
fuse_heat.go, a new direct read of cached telemetry. phaseCurrentsA now
takes the total from its caller: the EV hard limit and its rooms pass
the total they already read, the battery headroom clamp reads it once
instead of four times, and SitePhases uses the health-gated
OnlineMeterW, so a stale meter gives no phases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(loadpoint): a charger with no step table never locks to one phase

Review: with phase_mode unset or auto and no step table, surplus now
reached the one-phase day lock. A one-phase order is capped at 3,680 W,
under the charger's 4,140 W minimum, so the lock gave it nothing for
the rest of the day. With no table there is no one-phase ladder, and
pickSurplusSteps returns no steps before the phase choice. The rig's
s5-25 (25 A, surplus, auto, no table) now passes: the car charges 100 %
of the time at 9.2 kW of 10 kW PV with no import, so its known line
goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(loadpoint): no step table returns first and clears an old lock

From #1853's review: the early return computed the three-phase steps
before throwing them away, and an owner who removed the table on a day
the charger was locked to one phase, or held on one, kept that lock
until a restart. It now returns before anything else and clears the
loadpoint's one-phase lock and phase hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(loadpoint): a charger with no step table holds three phases

Second look at the no-table early return: it skipped the three-phase
phase choice, so a no-table charger on auto got phase_mode auto, also at
a 0 W pause, and Easee's driver picks one phase at 0 W and flips the
contactor back on resume. The early return now records three phases as
the session's choice, after clearing a one-phase lock; an owner's pin
and a site that cannot run three phases still win in resolvePhaseMode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Site rig s5s: a surplus-only loadpoint on phase mode auto with Easee's
steps stayed on one phase at 9.3 kW of surplus. The live surplus picked
three phases at the first tick. The next tick the plan, which showed no
PV, set the day's one-phase lock, and nothing ended it that day.

Each forecast window starts now, so its peak is never below the surplus
measured now. That reading counts only up to the average the pause gate
keeps, so one high reading does not switch to three phases. The day's
lock gives way to surplus above the three-phase minimum once it has held
for 30 min, and it waits for a three-phase choice to hold as long.

Three phases start at 6 A each, as PlanningSteps counts them: 3,680 W is
16 A on one phase. Counted as three-phase, 3.7-4.1 kW of surplus picked
three phases and ordered 4,140 W, which imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frahlg
frahlg force-pushed the fix/surplus-auto-three-phases branch from 52c1777 to 3a32dc1 Compare October 25, 2026 15:32
@frahlg
frahlg changed the base branch from fix/surplus-without-step-table to master October 25, 2026 15:32
@frahlg frahlg closed this Oct 25, 2026
@frahlg frahlg reopened this Oct 25, 2026
@frahlg
frahlg merged commit b5bd2b4 into master Oct 25, 2026
22 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