Skip to content

fix(easee_cloud): start a 6 A order at 7 A (1.3.7) - #190

Merged
frahlg merged 4 commits into
mainfrom
fix/easee-cloud-6a-start-boost
Oct 22, 2026
Merged

frahlg merged 4 commits into
mainfrom
fix/easee-cloud-6a-start-boost

Conversation

@frahlg

@frahlg frahlg commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Problem

Göran (22 Oct 2026, Easee Home via Easee Cloud, Audi e-tron, 3Φ, Core v0.155.0-beta.1 / v0.155.1-beta.1): after resume_charging + dynamicChargerCurrent=6, Easee reports a positive pilot only 5 min 2 s – 5 min 47 s later (four runs, 0 W meanwhile). The delay also appears with one single resume, so repeated resumes are not the cause. Starting at 7 A (or 8 A) and lowering to 6 A once the car draws current starts at once and keeps charging at ~4.13–4.23 kW. Core's own fuse_limit wait (fixed in v0.155.1) is excluded from these numbers. Easee has not confirmed the cause; Göran's support ticket is open.

What evcc does

evcc's Easee charger (charger/easee.go, evcc-io/evcc#30774, June 2026) notes "Easee firmware delays ~5 min when starting at 6A. Vendor docs recommend 7A minimum". In Enable(), when the action is ChargeStart and the current is below 7 A, it first writes the dynamic charger current to 7 A, then sends the start command. It does not step down itself; its loadpoint sets the offered current again on the next control interval. It applies only to ChargeStart (not ChargeResume), checks no headroom beyond the charger's max current, and has no opt-out.

License: evcc's charger/easee.go header says "This module is NOT covered by the MIT license. All rights reserved." No evcc code is copied here. This is an independent implementation of the observed behaviour, citing evcc only as prior art.

Change (easee_cloud 1.3.7)

  • On ev_set_current, when the order maps to exactly 6 A/phase and the charger was not charging at the last poll, the driver writes 7 A instead, but only when 7 A fits. All three must allow 7 A:
    • Core's cap_amps_per_phase (new in feat(loadpoint): tell the driver Core's current cap per phase ftw#1809): floor(cap_amps_per_phase) >= 7. This is the most current per phase Core allows this charger right now: its room below the fuse and the import limit after other chargers' share. A Core that does not send it gets no boost.
    • the order's max_amps_per_phase (the fuse rating, or a hold's limit): floor >= 7;
    • the charger's static maxChargerCurrent, read from /config (unknown → no boost).
  • Every 6 A order during a boost checks the gate again. If it no longer fits (lower cap or lower max_amps_per_phase), the driver writes 6 A at once and ends the boost. It does not boost again in the same start. (Codex P1)
  • The boost ends at the first poll that sees op_mode 3 (charging), or after 90 s. The 90 s limit holds in driver_command too, and driver_poll checks it before reading observations, so a failed observation read cannot keep 7 A. A failed step-down write is retried on the next poll or order. (Codex P2)
  • One boost per start: not restarted while active, not repeated after it ends; re-armed by a 0 A order, ev_pause, default mode, or the session leaving charging.
  • Orders above 6 A, a running session lowered to 6 A, and 0 A are unchanged. No extra resume is sent; the existing auto-resume path is untouched.
  • Works per phase, so 1Φ 6 A → 7 A on one phase.
  • Opt-out: driver config start_boost: false.

Why the cap and not max_amps_per_phase

The first version gated on max_amps_per_phase. Core (go/internal/loadpoint/controller.go) fills that with the site's main fuse rating (e.g. 25 A) or a manual hold's limit, not the room left. When Core ordered 6 A because the phase only had room for 6.4 A, the car took 7 A until the next reading. Core then saw an overage, cut to 0 W and paused, and the next start boosted again: on/off cycling in exactly the tight case. With the cap, the boost happens only when Core itself would allow 7 A on that phase.

Tests

drivers/tests/lua_harness/test_easee_cloud_start_boost.lua (+ pytest wrapper). Sections 1–9 keep the first version's cases; the boost cases now send cap_amps_per_phase. New sections:

Section Case Before (97471da) After
10 no cap ⇒ no boost; cap 6.4 ⇒ no boost; cap 7.9 and 15 ⇒ boost fails ("boost without Core's cap") passes
11 cap falls to 6 during a boost ⇒ 6 A at once, no new boost in that start fails ("boost kept 7 A after Core's cap fell to 6 A") passes
12 max_amps_per_phase falls to 6 during a boost (Codex P1) ⇒ 6 A at once fails passes
13 observations fail past 90 s (Codex P2) ⇒ poll writes 6 A; next order 6 A fails ("boost outlived its window while observations failed") passes
14 no poll past 90 s ⇒ the order writes 6 A fails ("order past the window wrote 7 A") passes

Each "before" row is that section run alone against the driver at 97471da.

  • make check: passed (manifests, index, devices, support status, history, baseline, host API, sandbox), pytest 4984 passed, 936 skipped.
  • Not tested on hardware with this driver. Göran's 7 A→6 A runs were manual.

Pairing

  • Needs feat(loadpoint): tell the driver Core's current cap per phase ftw#1809 (cap_amps_per_phase on every ev_set_current). Until a Core with that field runs, this driver never boosts and behaves like 1.3.6 at 6 A, which is the safe default. The two can merge in either order.
  • After merge, FTW's drivers/BUNDLED_SOURCE.json pin needs to move (scripts/sync-bundled-drivers.sh) for the bundled copy.

Open PRs on the same file

#159, #172, #183 and #189 (all drafts) also edit drivers/lua/easee_cloud.lua and bump its version, so whichever lands second rebases the version line, CHANGELOG, index.yaml and the manifest hash.

Do not merge without Fredrik's explicit yes.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 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-22T15:51:03.929991Z 828201b 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 828201b82c

ℹ️ 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".

Comment thread drivers/lua/easee_cloud.lua Outdated
Comment on lines +908 to +910
if boost_until_ms ~= nil then
-- Still within the boost: keep 7 A, do not restart the window.
write_a, boosting = START_BOOST_A, true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recheck the current ceiling while a boost is active

If Core lowers max_amps_per_phase to 6 A during an active boost, this branch still writes 7 A because it bypasses the ceiling checks used to start the boost. In the Lua harness, a boosted 4140 W order followed by the same order with a 6 A ceiling writes 7 A again. This overrides the newly reduced safety ceiling until charging is observed or the boost expires. Revalidate the latest ceiling on every order and cancel the boost when it no longer permits 7 A.

Useful? React with 👍 / 👎.

Comment thread drivers/lua/easee_cloud.lua Outdated
Comment on lines +776 to +778
if boost_until_ms ~= nil then
if charging or now >= boost_until_ms then
local werr = write_setting(charger_serial, {dynamicChargerCurrent = EASEE_MIN_A})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce boost expiry independently of successful observations

If observations fail or become invalid after a boost starts, driver_poll() returns before reaching this timeout check, while subsequent 6 A commands continue writing 7 A whenever boost_until_ms is non-nil. The Lua harness reproduces this with an invalid observation response after 91 seconds: the next order still writes 7 A. Thus the temporary extra current can persist beyond its advertised 90-second bound even when settings writes remain available. Check expiry before the observation-dependent return and in the command path so expired boosts cannot be reasserted. This also preserves the repository's bounded-control requirement.

AGENTS.md reference: AGENTS.md:L34-L35

Useful? React with 👍 / 👎.

Lödkolven added 3 commits October 22, 2026 18:38
Signed-off-by: Lödkolven <lodkolven@users.noreply.github.com>
Signed-off-by: Lödkolven <lodkolven@users.noreply.github.com>
Signed-off-by: Lödkolven <lodkolven@users.noreply.github.com>
@frahlg
frahlg force-pushed the fix/easee-cloud-6a-start-boost branch from 828201b to 97471da Compare October 22, 2026 16:38
frahlg added a commit that referenced this pull request Oct 22, 2026
The 7 A start for a 6 A order now needs Core's cap_amps_per_phase
(srcfl/ftw#1809) to allow 7 A, besides max_amps_per_phase and the
charger's static limit. max_amps_per_phase is the fuse rating, not
the room left: on a phase with room for 6.4 A the car drew 7 A, Core
cut to 0 W, and the next start boosted again. A Core that sends no cap
gets no boost.

Review findings on #190:
- P1: every 6 A order during a boost checks the gate again; one that no
  longer fits writes 6 A at once and ends the boost.
- P2: the 90 s window ends in driver_command too, and driver_poll ends
  it before reading observations, so a failed read cannot keep 7 A.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 7 A start for a 6 A order now needs Core's cap_amps_per_phase
(srcfl/ftw#1809) to allow 7 A, besides max_amps_per_phase and the
charger's static limit. max_amps_per_phase is the fuse rating, not
the room left: on a phase with room for 6.4 A the car drew 7 A, Core
cut to 0 W, and the next start boosted again. A Core that sends no cap
gets no boost.

Review findings on #190:
- P1: every 6 A order during a boost checks the gate again; one that no
  longer fits writes 6 A at once and ends the boost.
- P2: the 90 s window ends in driver_command too, and driver_poll ends
  it before reading observations, so a failed read cannot keep 7 A.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg force-pushed the fix/easee-cloud-6a-start-boost branch from a847464 to 8c4104e Compare October 22, 2026 17:09
frahlg added a commit to srcfl/ftw that referenced this pull request Oct 22, 2026
Each ev_set_current order now carries cap_amps_per_phase: the most
current per phase Core allows this charger on the phases the order
names. It is the charger's share of the EV fuse/import cap and of the
busiest phase's room after the split between chargers, within the
order's max_amps_per_phase and the charger's maximum, and not limited
by what the plan or a hold wants. Room that no charger took goes to
the first charger with a share, so two chargers are never told the
same room. Omitted on a 0 W order, without a fuse budget, or without
a voltage.

max_amps_per_phase is the fuse rating. easee_cloud (srcfl/device-drivers#190)
needs the room left to start a 6 A order at 7 A only when 7 A fits.
No order changes.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@frahlg
frahlg merged commit 04c7e2e into main Oct 22, 2026
5 checks passed
@frahlg
frahlg deleted the fix/easee-cloud-6a-start-boost branch October 22, 2026 22:38
frahlg added a commit that referenced this pull request Oct 23, 2026
Findings L1 and L2 from the review of #190.

L1: before the first poll last_op_mode is nil, so a reloaded driver
offered 7 A to a car that may already charge at 6 A. A boost now needs
a poll that saw the charger not charging.

L2: a pause or the stand-down cleared the boost but left 7 A in
last_amps_set, so the phaseMode re-assert on the next poll wrote 7 A
without the cap or the 90 s window. Clearing a running boost now sets
last_amps_set to what the charger should hold: the 6 A order after a
pause, 0 A after the stand-down. A pause keeps 6 A, not 0 A, because
Core's contactor cycle sends ev_resume after ev_pause with no new order
between.

Harness cases 15-17 fail on 1.3.7 and pass here.

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
frahlg added a commit to srcfl/ftw that referenced this pull request Oct 23, 2026
…#1811)

Move the device-drivers pin from 5cc7bb1a to 219c462e
(srcfl/device-drivers#190 and #191). Between the two pins only
easee_cloud changed.

An Easee Cloud charger given a 6 A order now starts at 7 A per phase
when Core's cap_amps_per_phase (#1809) allows 7 A, which avoids
Easee's roughly five-minute delay at 6 A, and steps down to 6 A once
the charger reports charging or after 90 s. 1.3.8 adds two review
fixes: no 7 A before the first poll, and a pause or stand-down cannot
leave 7 A for the phaseMode re-assert.

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