feat(easee_cloud): emit request_active so the host can tell car-declined from box-paused - #103
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: 9707f7ba83
ℹ️ 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".
| -- its own charge limit held reason 50 all night while the box kept | ||
| -- offering 11 kW and paged the operator twice about it. | ||
| local request_active = true | ||
| if connected and not charging and (reason_code == 50 or op_mode == 4) then |
There was a problem hiding this comment.
Register the Easee enumeration as upstream documentation
This new interpretation of Easee reason 50 and operation mode 4 depends on vendor-defined enumeration semantics, but manifests/easee_cloud.yaml still has no upstream_docs entry. Because .github/workflows/watch-upstream-docs.yml only watches URLs declared in manifests, changes or removal of the documentation underlying request_active will go unnoticed; add the durable Easee enumeration/API reference to the manifest.
AGENTS.md reference: AGENTS.md:L60-L66
Useful? React with 👍 / 👎.
* feat(ev): say why the charger is (not) charging and when it will Field report (#1002): a car plugged in against a schedule sits at 0 W until the cheap slots arrive, the modal looks dead, and the operator presses Start — which overrides the plan for the whole session. The grid-plan deferral (deadline past published prices) is worse: it forces surplus-only semantics with only an INFO log to show for it, so it reads as a PV-only mode nobody chose. GET /api/loadpoints now carries the answer: the next planned charge window (mpc.LoadpointPlanWindows, same MaxPlanAge cutoff as dispatch so a stale plan promises nothing), the grid-deferral flag, and what the controller last commanded (commanded_w/commanded_known, so "offering power the car won't take" is distinguishable from "pausing on purpose"). The EV modal renders one sentence from those fields, refreshed every poll: planned window with clock times and kWh, waiting- for-prices, car-declined with the charger's own reason, paused-by-box, PV-surplus-only, or the do-nothing default with the three ways out. Closes nothing on its own — #1002 also wants Start to become a bounded "charge now" and the Easee driver to emit request_active (srcfl/device-drivers#103); those land separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1d4uknzs7QUv7nE9rW4fi * fix(web): 24-hour clock in the EV plan strip Match plan-brief.js's formatClock — the plan UI speaks 24 h regardless of browser locale; toLocaleTimeString gave 01:00 PM on en-US browsers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1d4uknzs7QUv7nE9rW4fi --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ned from box-paused
Easee reason 50 ("secondary unit not requesting current") and op_mode 4
("completed") are the vehicle's side of the conversation; 52/53/100,
pending authorization and schedule states are ours. Emitting the
distinction lets the FTW host's existing 90 s debounce do its job:
the session-completion latch stops the planner allocating energy to a
full car, a manual Start hold auto-releases instead of offering power
all night, and the charging-interrupted notification stops firing on
the car's own renegotiation bursts.
Editing this FTW-promoted driver wakes the full catalog suite, so the
driver now also meets it: every host.http_get and host.json_decode is
pcall-wrapped (safe_http_get / safe_json_decode) so a raising host or
a garbage API response degrades to (nil, err) instead of killing the
poll.
The catalog manifest had already drifted to 1.1.1 while the DRIVER
block said 1.0.1; both now agree on 1.2.0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C1d4uknzs7QUv7nE9rW4fi
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
9707f7b to
457cfec
Compare


Why
Field report (srcfl/ftw#1002, 2026-08-29): a car at its own charge limit held Easee
reasonForNoCurrent=50all night while the FTW box kept offering 11 kW and paged the operator twice with spuriouscharging.interruptednotifications. The host could not tell "the car declined" from "we paused it" because this driver never emitsrequest_active— the field the loadpoint manager's session-completion latch, manual-hold auto-release and interruption suppression all key on. Today onlyctek_hybridemits it.What
request_activein theevtelemetry: false only when the vehicle side has explicitly stopped requesting current — reason 50 ("secondary unit not requesting current"; the vehicle is the secondary unit) orop_mode4 ("completed"), while connected and not charging. Every box-ordered pause (52/53/100 "dynamic limit too low", pending authorization, schedules, fuse limits) keeps the default true, so a pause we ordered is never mistaken for the car declining. The host debounces 90 s before acting, so a brief 50 during session handshake is harmless.host.http_get/host.json_decodegoes through pcall-wrappedsafe_http_get/safe_json_decode, so a raising host or a garbage API response degrades to(nil, err)instead of killing the poll.DRIVERblock said 1.0.1. Both now agree on 1.2.0. Manifest sha/size andindex.yamlregenerated with the repo tools.Host-side effect (no host change needed)
request_active=falseonly ever pauses optimism — it never commands charge. With it, the FTW host's existing logic starts working for Easee: the session-completion latch stops the planner allocating energy to a full car, a manual Start hold auto-releases after 90 s instead of pinning all night, andcharging.interruptedstops firing on the car's own ~900 W renegotiation bursts.Reaching FTW's bundled snapshot needs the usual follow-up there: move the pin in
drivers/BUNDLED_SOURCE.jsonand runscripts/sync-bundled-drivers.sh.Checks
make test-driver ID=easee_cloud— 36 passedmake check— 4035 passed, 1309 skipped🤖 Generated with Claude Code
https://claude.ai/code/session_01C1d4uknzs7QUv7nE9rW4fi
Note
Medium Risk
Changes EV loadpoint semantics for all Easee Cloud sites and tightens poll resilience; misclassification of reason 50 vs box pauses could affect planning and notifications, though host debouncing limits blast radius.
Overview
easee_cloud 1.2.0 adds
request_activeonevtelemetry so the FTW host can distinguish the vehicle stopping its charge request from a pause the charger or controller ordered.request_activestays true by default and goes false only when the car is connected, not charging, and Easee reportsreasonForNoCurrent50 (secondary unit not requesting current) orop_mode4 (completed). Box-driven stalls (e.g. 52/53/100, schedules, authorization) keep true, which lets existing host logic (session-completion latch, manual-hold auto-release,charging.interruptedsuppression) behave correctly on Easee without host changes.Poll-path HTTP GET and JSON decode now use
pcall-wrappedsafe_http_get/safe_json_decodeso bad responses or raising bindings fail the poll instead of crashing the driver. Catalog metadata (manifest, index, devices, support status, CHANGELOG) is bumped to 1.2.0 with updated artifact hash/size.Reviewed by Cursor Bugbot for commit 457cfec. Bugbot is set up for automated code reviews on this repo. Configure here.