Repository navigation
fix(easee_cloud): start a 6 A order at 7 A (1.3.7) - #190
Conversation
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: 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".
| 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 |
There was a problem hiding this comment.
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 👍 / 👎.
| if boost_until_ms ~= nil then | ||
| if charging or now >= boost_until_ms then | ||
| local werr = write_setting(charger_serial, {dynamicChargerCurrent = EASEE_MIN_A}) |
There was a problem hiding this comment.
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 👍 / 👎.
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>
828201b to
97471da
Compare
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>
a847464 to
8c4104e
Compare
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>
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>
…#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>
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 ownfuse_limitwait (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 isChargeStartand 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 toChargeStart(notChargeResume), checks no headroom beyond the charger's max current, and has no opt-out.License: evcc's
charger/easee.goheader 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)
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: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.max_amps_per_phase(the fuse rating, or a hold's limit):floor >= 7;maxChargerCurrent, read from/config(unknown → no boost).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)driver_commandtoo, anddriver_pollchecks 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)ev_pause, default mode, or the session leaving charging.start_boost: false.Why the cap and not
max_amps_per_phaseThe 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 sendcap_amps_per_phase. New sections:max_amps_per_phasefalls to 6 during a boost (Codex P1) ⇒ 6 A at onceEach "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.Pairing
cap_amps_per_phaseon everyev_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.drivers/BUNDLED_SOURCE.jsonpin 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.luaand bump its version, so whichever lands second rebases the version line, CHANGELOG,index.yamland the manifest hash.driver_command. That is next to the boost code, so a textual conflict is likely there.driver_command. The boost keys on the resulting 6 A, so it should still apply. Core's cap is per phase on the phases the order names.driver_pollreporting and observation handling. The boost's early timeout check sits beforeget_observations.Do not merge without Fredrik's explicit yes.
🤖 Generated with Claude Code