Skip to content

fix(control): say Easee limits the charger to N A, not "Not following" - #1537

Draft
frahlg wants to merge 9 commits into
masterfrom
easee-load-balancer-limited
Draft

frahlg wants to merge 9 commits into
masterfrom
easee-load-balancer-limited

Conversation

@frahlg

@frahlg frahlg commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Problem

Göran (@segran2) traced an Easee Home on 23 October 2026 with every FTW driver POST logged (#1763 comment, #1762 comment). FTW ordered 15 A throughout and the charger read back 15 A. Easee's own circuit allocation and the current it signalled to the car fell to 12 A at the start, then 14 A and 13 A while the home battery charged. The car took 8.2–8.9 kW instead of 10.4 kW. FTW sent no lower order, yet the EV sheet said "Not following … The charger has not said why".

Change

This PR now carries four things:

  1. fix(control): say Easee limits the charger to N A, not "Not following" #1537 on current master, as Göran integrated it in chore(feedback): integrate current master into #1537 #1823 (e225339). Commit e06b728 applies chore(feedback): integrate current master into #1537 #1823's diff against its master base onto master 286ac14; it keeps master's one-phase and hourly-import feedback beside the load-balancer reason. Credit: Co-authored-by Segran. The driver pin from fix(control): say Easee limits the charger to N A, not "Not following" #1537 and chore(feedback): integrate current master into #1537 #1823 is left out: neither pointed at a merged driver commit.
  2. The rework for easee_cloud 1.4.1 (fix(easee_cloud): report Easee's load balancing below FTW's order (1.4.1) device-drivers#194), commit 5c2f2ec. The driver now reports current_limit_a, the current the charger signals to the car while it is below the order the charger holds and answers that order, and current_limited_by = "load_balancer" only when Easee's allocation shows the cause. Core:
    • reads both, and puts limit_a, limited_by and the charger's make (from the driver's reported identity) on the control row;
    • once the order has stood its response time, the charger holds FTW's order (max_a), and the car takes about that limit (within 10 % below it, or as the car follows a step up to 2 A but at most 15 % of the limit; never above), says limited/load_balancer_limit (info), or limited/offered_current_lower (warning) when no cause is shown. A charger holding a lower order than FTW sent still reads offered_current_lower from max_a first;
    • keeps "Not following" when the car takes far less than the limit allows, nothing, or more than it allows. fix(control): say Easee limits the charger to N A, not "Not following" #1537's first version also called a car at zero limited on the reason alone; that is gone. A vendor reason code is never used as the cause.
  3. A merge commit (f5a3432) on top of the old branch head. Its tree is the rebased tree; it keeps the earlier history instead of rewriting the branch.
  4. Review fixes (22ee466 and the second pass after it), from an independent review: the held-order check comes first, the upper bound, a margin for a balancer stepping up while the cloud still reports the old draw (2 A, at most 15 % of the limit), a positive limit, make cleared with other stale fields, and words that never say the charger offers both 13 A and 15 A; a lower held order is named before the driver's current.

The EV sheet now reads:

Limited by the charger’s load balancing
Easee limits the charger to 13 A (its load balancing). FTW asked for 15 A; the car takes 8.8 kW.
If this happens often, compare the charger’s load balancing settings with FTW’s main fuse setting.

Without a make: "The charger’s load balancing limits it to 13 A. …". Numbers list "Requested current 15 A", "Charger offer 15 A" (the order the charger holds), "Charger limit 16 A" and "Load balancing allows 13 A"; "Measured" shows what the car takes. With no shown cause the sentence reads "The charger lets the car take 13 A, below the 15 A FTW asked for."

Evidence

Each test failed first on the code before it:

  • TestEaseeLoadBalancingReadsAsLimited replays Göran's case through controlFeedback, with the charger reading back 15 A: a named 13 A limit with the car at 8,850 W reads limited/load_balancer_limit/info and is never "not following" on the way; the limit without a cause, offered_current_lower; no limit, power_below_target; the car at 0 W, no_power_response; at 4 kW, power_below_target; at 7.5 kW (2.1 A under the limit), power_below_target; at 3.8 kW under a 7 A limit, power_below_target; at 10.3 kW (the order), following; a limit at the order, power_below_target; a zero limit, with or without a readback, no_power_response; a charger holding 12 A, offered_current_lower. On fix(control): say Easee limits the charger to N A, not "Not following" #1537's code five of the first six cases fail; "car at the order" and "charger holds 12 A" also fail on 5c2f2ec, before the review fixes.
  • TestEaseeLoadBalancingStepsUp: the balancer steps from 12 to 14 A while the cloud still reports the car at 8,280 W; the verdict stays load_balancer_limit. It fails on 5c2f2ec with not_following/power_below_target.
  • TestChargerLimitCarriesTheMake: a row with a limit carries the make "Easee" from a real driver registry; a row without does not, and a stale row drops limit, cause and make. It fails on fix(control): say Easee limits the charger to N A, not "Not following" #1537's code.
  • Field matrix: two probe rows for Göran's charger (pr1763/…/planned+balancer18A, …/chargenow+balancer18A): Easee keeps each phase under 18 A after the house, so the car gets 12 A of a 15 A order, and the sim's adapter reports it as the driver does. The adapter sends max_a and the limit for these rows as the driver does. The verdict check now demands load_balancer_limit while the car is held at the reported limit (both verdicts pass in the first 30 s after the limit moves). On master's Core the Charge now row fails: the car draws 8280 W against an expected 10350 W (5m30s), the verdict says not_following/power_below_target. 344 cases, none fail. The existing pr1763 rows still model a car that holds itself at 8.3 kW; their comment now says the trace showed Easee did it on that site.
  • web/control-feedback.test.mjs checks the words, both make cases, the numbers, the unexplained case and a charger holding a lower order. npm test: 886 passed.
  • make verify passed except internal/drivers (the worktree's generated driver snapshot was stale; passes after make drivers) and TestLiveBackupPastItsLimitNamesTheLimitAndTable (a 1 s backup time limit hit under load; passes on rerun). Both pass on rerun.
  • Browser: the status block rendered with the PR's web/ CSS and module in headless Chrome at phone width, for the named, unnamed and no-cause rows. A human still needs to look at it in the real EV sheet, and nobody has seen this with a load-balanced Easee yet.

When Easee releases the car, the driver keeps the last limit for up to 20 s while the car still draws below the order, so the sheet does not flash "Not following" before the cloud reports the new draw.

Found on the way, not fixed here: if Easee's load balancing counts the home battery and sits below FTW's fuse setting, it can stop the car while FTW charges the battery (in the sim the car sat idle for 2 h and got 5.3 of 9.9 kWh). That is EV priority over battery charging (#1763, #1789).

Pairing and order

  1. fix(easee_cloud): report Easee's load balancing below FTW's order (1.4.1) device-drivers#194 (easee_cloud 1.4.1). This PR works without it: no driver sends the fields until then.
  2. Merge this PR. srcfl/ftw-webapp vendors web/control-feedback.js and needs a fresh copy; until then the app shows "The box reports “load_balancer_limit”, which this version cannot describe."
  3. A separate pin PR moves drivers/BUNDLED_SOURCE.json to feat(ci): attach rpi-image PR builds as draft preview releases #194's merge commit.

#1823 stays open for its author; this PR took in its integration.

🤖 Generated with Claude Code

An Easee charger's load balancer can cut the car below FTW's request.
The EV sheet then said "Not following" and that the charger had not said
why, with requested current, charger offer and charger limit all equal.

Read the driver's vendor-neutral current_limited_by. When it names the
load balancer and the car draws less than asked, report
load_balancer_limit as limited, with words that name the load balancer.
More power than asked still warns.

Move the bundled driver pin to srcfl/device-drivers#172, which keeps the
load-balancing reason while the car charges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
frahlg and others added 4 commits October 25, 2026 19:12
#1537 (602aa0f, e33726b) on current master, as Segran integrated it
in #1823 (e225339): the feedback code keeps master's one-phase and
hourly-import paths beside the load-balancer reason. The driver pin
from both PRs is left out; it moves only to a merged driver commit.

Co-authored-by: Segran <goran@andersson-stehag.se>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Göran's trace (#1763, 23 Oct 2026): FTW ordered 15 A throughout, the
charger read back 15 A, while Easee's own load balancing signalled 12,
14 and 13 A to the car. FTW said "Not following".

easee_cloud 1.4.1 reports current_limit_a while the charger's offer to
the car is below the order, and current_limited_by = "load_balancer"
when Easee's allocation shows the cause. Core reads both. Once the
order has stood its response time and the car takes about that limit,
the verdict is limited/load_balancer_limit (info), or
offered_current_lower (warning) when no cause is shown. A car taking
far less than the limit, nothing, or more than asked keeps its
not-following verdict. The row carries limit_a, limited_by and the
charger's make, so the sheet says "Easee limits the charger to 13 A
(its load balancing). FTW asked for 15 A; the car takes 8.8 kW." and
the numbers list the allocated current beside requested and offered.

This replaces #1537's reason-only branch, which also called a car at
zero limited. The field matrix gains Göran's charger as two probe rows;
its Charge now row fails on master with not_following.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
The two commits below, e06b728 and 5c2f2ec, are #1537 on master
286ac14 as Segran integrated it in #1823, then the rework for easee_cloud
1.4.1. This merge keeps the branch's earlier history instead of
rewriting it; its tree is the rebased tree. The driver pin returns to
master's: it moves only to a merged driver commit.

Co-authored-by: Segran <goran@andersson-stehag.se>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fixes to the load-balancer verdict:

- Check the order the charger holds (max_a) before the driver's lower
  current, so a charger holding 12 A of a 15 A order still reads
  offered_current_lower.
- A car taking more than the limit allows is not limited by it.
- Allow 2 A below the limit as well as 10 %, so a balancer stepping up
  from 12 to 14 A does not flash "Not following" while the cloud still
  reports the car's old draw.
- Require a positive limit; clear make with the other stale fields.
- Words: "Load balancing allows 13 A" and "The charger lets the car take
  13 A" for the driver's current, so a card never says the charger
  offers both 13 A and 15 A.
- The field matrix's balancer rows send max_a like the driver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg frahlg changed the title fix(control): show a charger's load balancer as the limit fix(control): say Easee limits the charger to N A, not "Not following" Oct 25, 2026
- The margin below the limit is 10 %, or as the car follows a step up
  to 2 A but at most 15 % of the limit, so a car with its own cap well
  under a low limit stays not following.
- When the charger holds a lower order and the driver also reports a
  lower current, the sentence names the held order.
- Tests for the positive-limit guard without a readback, for stale rows
  dropping limit, cause and make, and for both margin edges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
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