feat(ocpp): OCPP 1.6J + 2.0.1 support — chargers connect with no driver - #732
feat(ocpp): OCPP 1.6J + 2.0.1 support — chargers connect with no driver#732HuggeK wants to merge 22 commits into
Conversation
Brings back go/internal/ocpp, retired as unused in srcfl#578, so EV chargers can connect to FTW directly instead of through a vendor cloud. The package is restored unchanged and still builds, vets and passes its own tests against the current tree. github.com/lorenzodonini/ocpp-go resolves to v0.19.0 at @latest — the same version that was removed, since upstream has not cut a release since August 2025. Nothing is wired into main.go yet, so this changes no runtime behaviour. Two known gaps are carried over from the original and must be closed before the server is enabled: - Config.Bind is advisory only. ocpp-go does not expose a bind address, so the listener takes 0.0.0.0 regardless, and Phase 1 has no TLS. - Handlers are read-only. Charge Amps needs SetChargingProfile-based control, because its RemoteStopTransaction is unreliable in the field. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
…argers Wires the restored OCPP 1.6J Central System into the process behind a new opt-in ocpp config section, and documents the part that is easy to miss: an OCPP charger needs no driver at all. OCPP is vendor-neutral, so one server in core covers every charger that speaks it. Chargers dial FTW rather than being polled, so there is nothing to add under drivers: — a charge point becomes a device on its first BootNotification, keyed by the last segment of the URL it connected to. Enabling the server requires a username and password, and config validation rejects an enabled section without them. This is deliberate. ocpp-go builds its listen address from the port alone, so the socket is reachable on every interface and cannot be pinned to one; basic auth is the only gate in front of it. That is a mitigation, not a fix, and the docs say so. Documented in docs/ocpp.md, with pointers from the README, config.example.yaml and writing-a-driver.md so nobody starts a Lua driver for a charger that does not need one. Tests cover the fail-closed credential rule and assert the shipped example config still parses, ships disabled and validates. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
… stop FTW can now throttle, pause and resume an OCPP charger. Command has the same signature as drivers.Registry.Send and speaks the vocabulary every EV driver already implements, so main.go routes by name and loadpoints never learn that an OCPP charger is not a Lua driver. Every command is a current limit, never a remote start or stop. RemoteStopTransaction is unreliable on Charge Amps hardware — units acknowledge the stop and then resume charging on their own — while a 0 A charging profile is honoured consistently. It also leaves the transaction open, so the session meter keeps counting across a pause instead of splitting into two sessions. Two safety decisions worth calling out: - Below the IEC 61851 minimum of 6 A the charger is told 0 A rather than rounded up. When the allocator has less headroom than that to give, rounding up draws current the site fuse was never asked to carry, so refusing to charge is the safe direction of error. - A pause records no limit, so resuming returns to the last non-zero rate rather than the fallback ceiling. Caught by TestResumeRestoresLastLimit. Also splits charger state in two. connected already meant "a connector has a vehicle on it" and was never set by OnConnect, so it could not gate control: a default charging profile is exactly the thing you set on an idle charger. online now tracks the WebSocket session and is what control gates on. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
MaskSecrets covered every other credential section but not the new ocpp one, so GET /api/config returned the password in plaintext to any UI client. That is the one credential that must not leak: the OCPP listener is reachable on every interface and basic auth is the only thing in front of it. Masking alone would have traded a leak for a wipe, because the settings tab posts the config back and the masked password returns empty. PreserveMaskedSecrets now keeps the stored value when the incoming one is blank, while a genuinely new password still wins. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Newer chargers now connect without a driver too. Each version listens on its own port, because a charger picks its dialect during the WebSocket handshake before any message is sent, and ocpp-go's ws.Server keeps a single message handler per listener — one port cannot serve both. Set ocpp.port_v201 to enable 2.0.1; leaving it unset keeps 1.6J only. Only the encoding differs. Both dialects share one charger map, one telemetry path and one control path, so a 2.0.1 charger is metered, throttled and paused exactly like a 1.6 one and dispatch cannot tell them apart. Which listener a charger reached is recorded on connect, and control encodes the profile to match. 2.0.1 restructures more than the names suggest: Start/StopTransaction collapse into one TransactionEvent, transaction ids become strings, connector status loses its charging meaning, and meter samples arrive inside transaction events as well as alone. The new handler normalises all of it back to the same state. OCPP 2.1 is deliberately absent. No production-grade Go implementation exists — ocpp-go covers 1.6 and 2.0.1 only, and the Go projects claiming 2.1 are early-stage validators and emulators rather than servers. Adding it later is one handler and one listener; the version-neutral core is unaffected. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Records the boundary between the vendored protocol layer and FTW's own code, so a reader knows which side of it a bug is on: transport, OCPP-J framing, message types and schema validation are ocpp-go; handlers, telemetry mapping, control semantics and safety clamps are ours. Also corrects a false claim carried over from the retired package. The doc comment said ocpp-go was "also used by SteVe" — SteVe is a Java project (GPL-3.0), so it cannot depend on a Go library, and ocpp-go's README names no production users at all. Replaced with facts that can be checked: MIT, v0.19.0, no release since August 2025, and upstream's own description of its 2.0.1 support as needing more real-world testing. The package doc was stale in other ways too — it still described a 1.6-only, read-only server whose Phase 2 would add control, all of which has since landed. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
…tore # Conflicts: # config.example.yaml # go/cmd/ftw/main.go # go/go.mod # go/go.sum # go/internal/config/config.go
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
The brand-cleanup workflow inventories "MIT licensed" as classified copy, so the new provenance paragraph tripped it. Same fact, different wording. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
…tore Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Rename the Loadpoints settings tab and dashboard section to Chargers — the thing an operator recognises is the charger, not the internal loadpoint binding (config keys keep their spelling). The tab gains an OCPP panel: the exact backend URL to enter on a charger, how to enable the server when it is off, live state for every connected charge point, and a warning to reserve the FTW host's IP in the router before commissioning — chargers store the URL and some whitelist addresses, so a DHCP move silently orphans them. Charge points seen by the OCPP server now appear in the charger-driver dropdown, so binding an OCPP charger to the planner is a dropdown pick instead of a YAML edit. Backed by GET /api/ocpp/chargers, which reports the effective ports plus a per-charger view extended with online state, dialect, vendor/model and the last accepted limit. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
The UI now answers "where do I add an OCPP charger?"The Loadpoints tab and dashboard section are renamed Chargers, and the tab gains an OCPP panel. There is deliberately no "add OCPP charger" button — the charger adds itself the moment it dials in — so what the panel does is tell you the exact URL to enter on the charger, then show every charge point that has connected, live: Everything in that screenshot is a real session: a simulated Charge Amps Dawn connected over OCPP 1.6J to a Raspberry Pi running this branch, charging at 7.4 kW. The charge point also appears in the Charger driver dropdown as On the dashboard the same charger is a first-class element of the power flow: How this was tested (hardware-free, reproducible)
Force-started charging through Why the DHCP-reservation advice sits in the panelChargers store the backend URL at commissioning time, and some hardware additionally whitelists which addresses may open a connection to it. An FTW host that later gets a different DHCP lease silently orphans every charger pointed at it — nothing errors, the chargers just never reconnect. The panel and Also in this push: dashboard "loadpoint" strings read "charger", |
A charge point that authenticates but is not named by any charger entry (loadpoint) now connects as "pending": visible in Settings -> Chargers and GET /api/ocpp/chargers so it can be adopted, but its telemetry never reaches the site - no DerEV reading, no driver health, no metrics - and it is never commanded. Without this, any device holding the shared basic-auth secret could fabricate EV load and suppress home-battery discharge through the dispatch clamp. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Update: unadopted charge points are now quarantined as pendingA gap flagged during bench review: any device holding the shared OCPP password could connect under an invented identity, and its Proven live on the bench Pi with two simulated charge points connected simultaneously, with the same password:
What the quarantine deliberately does not stopImpersonation of an adopted id. Identity is client-chosen and the credential is shared, so password plus a real charger's id still gets through. Per-charger credentials (1.6 allows per-connection basic auth) or TLS client certificates (2.0.1 security profiles) would close that; both are listed as future work in the description. 🤖 Generated with Claude Code |
Loadpoints hot-reload on config save, so the quarantine's approved set must follow in the same applier — otherwise adopting a pending charger would arm the loadpoint controller immediately while telemetry stayed blocked until a restart. Revoking an adoption now also pushes a zero DerEV reading, for the same reason OnDisconnect does: the last power figure must not linger in a sum the charger is no longer part of. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
… 2.0.1/15118 Note on the config field, the settings tooltip and docs/ocpp.md: a charger entry's vehicle_capacity_wh models the one car the charger usually serves, and a wrong value costs planning accuracy, never safety. Detecting which car is plugged in - to switch capacity automatically - needs vehicle identity from the protocol: OCPP 1.6's idTag names the RFID card, not the car; real identity (MacAddress autocharge / eMAID) and NotifyEVChargingNeeds are OCPP 2.0.1 + ISO 15118 territory, listed as future work. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
…policy A vehicles: config list (and a Vehicles section on Settings -> Chargers) gives each car a capacity, identifiers and a charging policy: PV-surplus-only and/or a target SoC the planner fills toward in the cheapest hours. When a charging session identifies the car - the RFID idTag on 1.6, a MacAddress (autocharge) or eMAID (ISO 15118) idToken on 2.0.1 - the loadpoint switches to that car's capacity and policy for the session. Capacity reverts on plug-out and survives config hot-reloads. A session matching no profile changes nothing (the visitor default), and its identity shows in the Chargers table so the operator can paste it into a profile. Quarantine applies: pending chargers store identity for display but never fire the profile hook. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Shortly after a charger connects, core reads SupportedFeatureProfiles (1.6) or SmartChargingCtrlr.Available (2.0.1), stores the raw answer and derives a tri-state verdict: smart charging, telemetry only, or not reported. Surfaced as steerable/feature_profiles on GET /api/ocpp/chargers and as a Control column on Settings -> Chargers, with a warning before an operator binds a metering-only charger to a charger entry and waits for a planner that will never move it. Advisory, never a gate: commands are still attempted, because vendors under-report and firmware changes the answer. What decides remains the response to a real SetChargingProfile, handled by the actuation tracker. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Connect and boot both trigger the probe milliseconds apart, long before any answer can arrive, so every charger was asked twice. An in-flight marker collapses that into one request, cleared on every callback path (and on a send that never left) so an unanswered probe still retries on the next connect - which is how a firmware update that adds SmartCharging is noticed. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
A probe in flight when the socket dies may never get its callback, which would leave the in-flight marker set and block every later probe for that charger. The reconnect is exactly when we want to ask again, so OnDisconnect clears it. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Two conflicts, both from work that landed while this branch was open. api.go: srcfl#744 gave every route an access tier. GET /api/ocpp/chargers is Local rather than Read — the list carries vehicle_id, which on 1.6 is the RFID idTag the card presented, and that token authorizes a charge at the charger. Its only caller is the settings page, which is Local already, so nothing is lost. Added to TestNoSecretBearingReadCrossesTheSession so the tier is enforced rather than merely declared. config.go: Validate() gained FleetPing alongside this branch's OCPP and vehicle checks. Both kept. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>



Adds OCPP 1.6J and 2.0.1 support so EV chargers connect to FTW directly
instead of through a vendor cloud, with full control, and documents the part
that is easy to miss: an OCPP charger needs no driver.
OCPP is vendor-neutral, so one server in core covers every charger that speaks
it. This is the inverse of the rest of FTW, where every device needs a driver
precisely because every vendor invented its own protocol.
Where the Go code comes from
The protocol layer is
github.com/lorenzodonini/ocpp-gov0.19.0, MIT. It is an ordinary Go module dependency — pinned ingo/go.mod, checksum-verified ingo/go.sum:Nothing in this PR is copied or forked from it. The split:
go/internal/ocpp(~1,400 lines + ~860 lines of tests)That boundary is the first thing to check when something misbehaves: a malformed message or dropped connection is upstream, a wrong power figure or current limit is ours.
Upstream health, stated plainly because we now depend on it for a control path: 367 stars, MIT, no release since August 2025, and it describes its own 2.0.1 support as "examples working, but will need more real-world testing". So treat the 2.0.1 path here as less proven than 1.6J regardless of the tests below.
On making it a git submodule
Considered, and it does not work for a Go dependency.
go buildresolves imports throughgo.modand the module cache, so a submodule checkout would sit inert unless paired with areplacedirective pointing at it — and that would additionally breakgo installand any clone made without--recurse-submodules, while forcing submodule-aware checkout into CI,make build-arm64, Docker and the release flow. FTW uses no submodules today.If the goal is the dependency source living in this repository, auditable and pinned, the Go-native answer is
go mod vendor: it commits the dependency tree undervendor/, the toolchain uses it automatically, and no build path changes. Happy to do that if you want it — say the word.If the goal is carrying patches upstream will not take, the answer is a
srcfl/ocpp-gofork plusreplace github.com/lorenzodonini/ocpp-go => github.com/srcfl/ocpp-go. Still an ordinary module. Given upstream's release cadence, that is the likely destination anyway.A provenance claim in the retired code was false, and is corrected here
The package doc carried over from #578 said ocpp-go was "also used by SteVe". SteVe is a Java project (GPL-3.0, 1,090 stars) — it cannot depend on a Go library — and ocpp-go's README names no production users at all.
I repeated that line unverified when restoring the package. It is now replaced with facts that can be checked.
Protocol versions
port, default 8887port_v201, off unless setEach version needs its own port. A charger picks its dialect in the
WebSocket handshake before any message is sent, and
ws.Serverkeeps a singlemessage-handler slot, so one listener cannot dispatch both.
Only the encoding differs. Both dialects share one charger map, one telemetry
path and one control path — dispatch cannot tell them apart.
Why 2.1 is absent, and what it would take
No production-grade Go implementation of OCPP 2.1 exists. Survey of every Go
OCPP project on GitHub:
lorenzodonini/ocpp-goChargePi/ocpp-managershiv3/gocppChargePi/chargeflowruslan-hut/ocpp-emuEverything claiming 2.1 is single-digit stars and mostly validators or
emulators rather than central systems. Depending on one for the safety path
would be a downgrade from a 367-star library.
Not a blocker in practice: every charger on the bench speaks 1.6J only, and
2.0.1 is what current hardware is migrating to. OCA published 2.1 in Jan 2025
and field adoption is still thin.
Adding it later is one handler file and one listener — the version-neutral
core does not change.
shiv3/gocppis worth re-checking as it matures, andruslan-hut/ocpp-emuis worth keeping in mind for HIL testing.Why this exists
Bench testing six chargers turned up the gap: Charge Amps has no FTW driver at
all, and three of six units are Charge Amps. Every current model speaks OCPP
1.6J since Charge Amps migrated off its proprietary CAPI protocol.
192.168.250.1→ Settings → OCPPAllow OCPP 1.6JEasee and Zaptec need a one-time vendor-portal step; after that the cloud is out
of the runtime path. Charge Amps needs no cloud at all.
How a charger becomes a device
Drivers poll outward. OCPP runs the other way — the charger dials FTW and
pushes. So there is nothing to add under
drivers:; a charge point appears onits first
BootNotification, keyed by the last segment of the URL it dialled:Appearing is not joining. A charge point that no charger entry (loadpoint)
names connects as pending: listed on Settings → Chargers with vendor,
dialect and live state so it can be adopted, but its telemetry is withheld
from the site — no DerEV reading, no driver health, no metrics — and it is
never commanded. Adopting it = picking its id as the charger driver in a
charger entry and saving. Charger entries hot-reload, so the config applier
re-derives the approved set on the same save — adoption admits the very next
reading, and removing the entry returns the charger to pending, with a zero
DerEV pushed so its last power figure cannot linger in the sum.
Control: current limits, never remote stop
Commandhas the same signature asdrivers.Registry.Sendand speaks thevocabulary every EV driver already implements, so
main.goroutes by name andloadpoints never learn an OCPP charger is not a Lua driver.
Every command is a current limit.
RemoteStopTransactionis unreliable onCharge Amps hardware — units acknowledge the stop and resume on their own — while
a 0 A charging profile is honoured consistently, and it keeps the transaction
open so the session meter is not split in two.
Two safety decisions worth review:
Rounding up would draw current the site fuse was never asked to carry.
TestResumeRestoresLastLimitcaughtthe first version recording the pause's zero and resuming at the ceiling.
On loss of contact the charger holds its last granted limit, matching every EV
driver in FTW.
2.0.1 restructures more than the names suggest
StartTransaction+StopTransactioncollapse into oneTransactionEventwith a Started/Updated/Ended trigger.
StatusNotificationreports per-EVSE connector status and loses its"charging" meaning — that now comes from the transaction event.
handlers_v201.gonormalises all of it back to the same charger state.Why charger state needed an extra flag
connectedalready meant "a connector has a vehicle on it" — set byStatusNotification, never byOnConnect. It could not gate control, because adefault charging profile is exactly what you set on an idle charger.
onlinenow tracks the WebSocket session and is what control gates on.
Security: fail closed
Enabling the server requires a username and password; validation rejects an
enabled section without them, and the password is masked out of
GET /api/configwhile surviving a settings round-trip.This is a mitigation, not a fix.
ocpp-gobuilds its listen address from theport alone —
— so the socket is reachable on every interface and cannot be pinned to one,
with no TLS on this path yet.
docs/ocpp.mdsays so plainly and tells operatorsto keep the port closed at the router.
Behind the password sits a second gate: unadopted charge points are
quarantined. Every charger shares one basic-auth secret and picks its own
identity (the URL segment), so "it authenticated" proves the password, not the
device. Before this gate, anything on the LAN holding the password could invent
EV load — and the dispatch clamp would obligingly stop the home battery
discharging into a charge that does not exist. Now a stolen password gets an
attacker a pending row in the Chargers table, not influence over the site.
TestPendingChargerIsQuarantinedFromTelemetryproves a booted, "charging"charge point at 7.2 kW leaves no trace in
telemetry.Store.What the quarantine deliberately does not stop
A device that knows the password and an adopted charger's id can still
impersonate it — identity is client-chosen and the credential is shared. Fixing
that needs per-charger credentials (1.6 allows per-connection basic auth) or
TLS with client certificates (2.0.1 security profiles), both future work. The
quarantine closes the injection hole that needed no knowledge beyond the
password; impersonation additionally requires knowing a real charger's id.
A third gate arrived while this branch was open: #744 gave every route an
access tier.
GET /api/ocpp/chargersis registeredLocal, notRead.Why a charger list is a secret-bearing read
The response carries
vehicle_id. On 1.6 that is the RFID idTag the cardpresented — the token that authorizes a charge at the charger itself. The
tier doc warns that the method was wrong twice in one review by judging a
route from its verb, and its cautionary example is a GET that hands out a
credential. A card number belongs in that company more than it belongs
alongside a power reading.
Nothing is lost by the stricter tier: the endpoint's only caller is the
settings page, which already needs
LocalforGET /api/config. The routeis listed in
TestNoSecretBearingReadCrossesTheSession, so a viewer reachingthrough the app passthrough is refused with
E_LOCAL_ONLY— the tier isenforced, not merely declared. Downgrade it to
Readif you read the idTagdifferently; the test is the one line to change.
Verification
Both dialects are covered end-to-end against real
ocpp16.ChargePointandocpp201.ChargingStationclients that record the profiles they receive,including a test that runs both listeners at once and steers one charger of
each kind without confusing them.
Pre-existing test failures, unrelated to this change
internal/configpath-separator tests and theinternal/apiMyUplink OAuth,backup-lifecycle and version-update tests fail on Windows. Confirmed identical
on a clean
masterworktree.The UI: a Chargers tab
The Loadpoints settings tab and dashboard section are renamed Chargers,
and the tab gains an OCPP panel. There is deliberately no "add OCPP charger"
button — the charger adds itself when it dials in — so the panel shows the
exact backend URL to enter on the charger, warns to give the FTW host a DHCP
reservation first (chargers store the URL at commissioning, some whitelist
addresses), and lists every connected charge point live — unadopted ones
dimmed and marked
· pending, with a note explaining they are ignored untiladopted. Connected charge points also appear in the charger-driver dropdown as
<name> (OCPP), so binding one to the planner is a dropdown pick instead of aYAML edit — and that same pick is what adopts a pending charger.
Backed by
GET /api/ocpp/chargers: effective ports plus a per-charger viewextended with online state, dialect, vendor/model and the last accepted limit.
The charger on the dashboard, and how the screenshots were taken
The same charge point is a first-class element of the dashboard power flow:
Both screenshots are a real session: a simulated Charge Amps Dawn connected
over OCPP 1.6J to a Raspberry Pi running this branch, charging at 7.4 kW,
captured with headless Edge over the DevTools protocol against the live host.
Can this charger be steered? Ask it, record it, show it
Not every OCPP charger accepts control, and finding out by watching a planner
do nothing is a bad first experience. FTW now probes each charger once shortly
after it connects —
GetConfiguration(SupportedFeatureProfiles)on 1.6, theSmartChargingCtrlr.Availablevariable on 2.0.1 — records the raw answer, andreports a tri-state verdict in a Control column and on
GET /api/ocpp/chargers(steerable,feature_profiles):SmartCharging; FTW can throttle and pause it.Advisory, never a gate. Commands are still attempted: vendors under-report,
and firmware updates change the answer without changing the advertisement. What
actually decides remains the response to a real
SetChargingProfile, handled bythe existing actuation tracker. The probe only buys honesty up front.
All three states above are one live screenshot from the bench Pi, with three
simulated chargers connected at once. The probe fires exactly once per charger
(an in-flight marker collapses the connect and boot triggers, verified on the
wire:
dawn asks: 1,meter asks: 1), and an unanswered probe deliberatelydoes not stick — that is how a firmware update adding
SmartChargingis evernoticed.
Vehicle profiles: which car is plugged in
A
vehicles:config list (also edited on the Chargers tab) gives each car abattery capacity, identifiers and a charging policy — PV-surplus-only
and/or a target SoC the planner fills toward in the cheapest tariff hours.
When a charging session identifies the car — the RFID
idTagon 1.6, aMacAddress(autocharge) oreMAID(ISO 15118) idToken on 2.0.1 — theloadpoint switches to that car's capacity and policy for the session. Capacity
reverts on plug-out and survives config hot-reloads. A session matching no
profile changes nothing (the visitor default), and the identity it
presented is shown in the Chargers table's new Vehicle column for copy-paste
into a profile. Quarantine still means no influence: pending chargers store
identity for display but never fire the profile hook.
Proven on the bench Pi: the simulator's
StartTransactiontagBENCHTAG01matched a
bench-carprofile —— after which
/api/loadpointsreportedvehicle_name: "Bench Car"with the80 % target set.
An honest caveat about the plug-out revert
The capacity revert runs in the loadpoint controller's
Observepath. Thatcontroller only exists on planner-enabled sites —
main.gogates itsconstruction on
mpcSvc != nil(pre-existing, not introduced here) — so thebench (which runs no planner) can apply a profile but never observes the
plug-out. The revert is pinned by
TestApplyVehicleProfileSessionScopedinstead, including surviving a mid-session config hot-reload. On any site
with a planner, loadpoint observation runs every control tick and the revert
is live. Without a planner the capacity is only cosmetic anyway — there is no
DP for it to mislead.
Not yet
/api/status,GET /api/ocpp/chargersand the Chargers panel, but/api/devices(thehardware-identity registry) is driver-only, and the
ocpp:server sectionitself is still configured in YAML.
impersonated with the shared password alone.
NotifyEVChargingNeeds, ISO 15118) is not consumedyet — with it, the planner could size a session on what the car actually
asked for instead of a configured capacity. Tracked with OCPP 2.1 in Track OCPP 2.1: upstream ocpp-go PR #371, and what 2.0.1/2.1 unlock over 1.6J #835.
Amps quirks come from field reports.
Bench commissioning and factory-reset detail per model: discussion #747 (moved here from a draft PR on
srcfl/device-drivers, which is closed — it is operator knowledge about hardware, not driver source, and Discussions are disabled on that repo).🤖 Generated with Claude Code