Skip to content

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

Merged
frahlg merged 11 commits into
masterfrom
fix/surplus-without-step-table
Oct 25, 2026
Merged

frahlg merged 11 commits into
masterfrom
fix/surplus-without-step-table

Conversation

@frahlg

@frahlg frahlg commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Based on master 0216fb6, which has #1849 (phase current takes its direction from the phase's power). On master before #1849, the export-as-import bug hid this one.

Cause

A charger with no step table takes any power from its minimum to its maximum. That is what the settings page saves for a new charger, and it is the home box's own setup. surplus3PhaseSteps returned [0] for it, so surplus snapped every order to 0 W and the car never charged from solar.

A trace of timing/16A/empty/none/fast2s/car3p/L1/surplus showed 6,000 W of surplus, steps [0], smallest step 0, and no order for 30 min. On master the export-as-import bug hid this, because those rows already failed as export.

Change

  • With no table, the surplus steps are none, which SnapChargeW reads as any power between the minimum and the maximum.
  • surplusMinStepW: with no table, the least surplus may order is the charger's minimum, so surplus still pauses below it, and the one-phase lock and the wake kick read the same minimum.
  • With no table there is no one-phase ladder, so pickSurplusSteps returns before the phase choice and never locks the charger to one phase. A one-phase order would be capped at 3,680 W, under the 4,140 W minimum.
  • A table without three-phase levels keeps its old behaviour.

Deleted: the nostep label and its rows.

Before and after

Check master 0216fb6 this branch
TestSurplusWithoutAStepTableOrdersTheSurplus 6,000 W of surplus orders 0 W 6,000 W; 2,000 W still pauses
TestSurplusWithoutAStepTableNeverLocksToOnePhase (phase_mode "" and auto, forecast 0, 9,310 W surplus, through Tick) one-phase order of 3,680 W no one-phase order; about 9.3 kW
rig s5-25 (25 A, surplus, auto, no table) known fail: never starts passes: 100 % of the time at 9.2 kW of 10 kW PV, no grid import (meter mean −115 W), battery 0 W; known line deleted
Field matrix (342 cases) 21 fail, all idle: 13 readback, 8 nostep 19 fail, all idle readback; the nostep label is gone
timing/{16A,25A}/empty/none/fast2s/car3p/L1/surplus idle, never orders pass
timing/{16A,25A}/empty/none/{cloud60s,easee40s,easee120s}/car3p/L1/surplus (6 rows) idle, never orders start, then hunt on the late readback (286–348 orders, 2.0–2.2 kWh); relabelled readback for #1850

go vet and go test -p 2 for loadpoint, control and cmd/ftw pass. TestSiteSimScenarios and TestSiteSimRandomHouse pass.

Independent review (Sonnet)

  • Fixed in e2e302e. With phase_mode unset or auto and no table, surplus reached the one-phase day lock and capped the order at 3,680 W, under the minimum. The real stack showed it in s5-25: car 3.7 kW of 8.4 kW available. Fixed by the early return; the Tick test fails on the earlier head.
  • No panic, no order below MinChargeW, no more import than a stepped charger: a no-table order is the exact surplus. The wake kick offers 4,140 W.
  • The label is fair. The six readback rows hunt exactly like the stepped rows at the same timestamps.
  • fix(loadpoint): solar-only auto runs on three phases above the minimum #1853 (the s5s fix) is based on this branch and touches the same function. See the note to its author on keeping the two consistent.

Limits

  • One-phase surplus for a charger with no step table is out of scope, by decision: a no-table charger charges from surplus on three phases from its minimum, or pauses. That is a strict improvement on master, which never charged a no-table charger from surplus. fix(loadpoint): solar-only auto runs on three phases above the minimum #1853 keeps this early return and drops its own no-table branch.

Hardware

Not run on real hardware. Real-hardware evidence is still needed: surplus charging on a charger with no step table, for example the home box's Easee.

🤖 Generated with Claude Code

frahlg and others added 2 commits October 25, 2026 14:54
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>
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>
@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:09:04.773713Z 5ba9a07 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 and others added 2 commits October 25, 2026 15:11
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>
…tep-table

# Conflicts:
#	go/internal/control/testdata/sitesim_field_known.txt
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>
frahlg and others added 6 commits October 25, 2026 15:29
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>
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>
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>
…-step-table

# Conflicts:
#	go/internal/control/ev_battery_headroom.go
#	go/internal/control/fuse_heat.go
#	go/internal/control/testdata/sitesim_field_known.txt
@frahlg
frahlg changed the base branch from fix/fuse-phase-direction-v2 to master October 25, 2026 15:18
@frahlg frahlg closed this Oct 25, 2026
@frahlg frahlg reopened this Oct 25, 2026
@frahlg
frahlg merged commit 2ec5a1d 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