Skip to content

feat(ev): add support for CTEK chargers - #116

Closed
erikarenhill wants to merge 13 commits into
masterfrom
feat/ctek-chargestorm-driver
Closed

erikarenhill wants to merge 13 commits into
masterfrom
feat/ctek-chargestorm-driver

Conversation

@erikarenhill

Copy link
Copy Markdown
Collaborator

No description provided.

erikarenhill and others added 7 commits April 19, 2026 02:05
Minimal driver wired around register 4608 (0x1200, holding uint16) for
charge-current setpoint — the one register verified against the CCU
firmware. Handles ev_set_current / ev_pause / ev_start / ev_resume with
an IEC-61851 6 A floor and values below it written as 0 to pause, the
same rounding the Easee driver uses.

Power / connector-state / session-energy registers are intentionally
left unmapped; driver_poll emits a placeholder ev reading with
max_a from register readback and a clear TODO block pointing at the
CCU automation ModbusTCP range (0x1000+/0x1100+ in v1, 0x2000+/0x2100+
in v2) for the next pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CTEK "Automation interface" doc (v1.0, rev 6b4af7) exposes the
full internal energy-meter register map over Modbus/TCP on CSOS 4.9.3+
— not just the charging-limit register the prior driver was using.
Split into two drivers because the CCU's api_version selector swaps
the base offsets (0x1xxx ↔ 0x2xxx) and CTEK only lets you run the
charge-control API alongside the NANOGRID™ limit-control server if
one of them is on v2.

What both drivers now emit (was placeholder zeros):
- Total active power (W), per-phase current (A) + voltage (V)
- Lifetime energy (Wh, u32 BE)
- Charging limit setpoint + max-assignment ceiling
- Serial number (anchors device_id to the EVSE hardware identity)

Control path is unchanged: write the charging-limit register
(0x1200 / 0x2200) in amperes, 0 to pause. start / pause / resume /
set_current all reach the driver via /api/ev/command — frontend
buttons already POST there, no UI change needed.

Known limitation: the CCU does NOT expose the EVSE state code
("CHRG", "PAUS", "EVRD", …) over Modbus — it's MQTT-only. We derive
`charging`/`connected` from the current + power readings instead,
which is good enough for the dispatch clamp but won't distinguish
"car plugged, awaiting authentication" from "nothing plugged in".

Requires Chargestorm Connected 2 or 3 with CSOS firmware 4.9.3 or
later AND Automation/ModbusTCPEnable=true on the CCU.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds drivers/ctek_mqtt.lua — a read-only companion to the Modbus
drivers. CTEK's MQTT automation interface (CSOS ≥ 3.11.15.1) publishes
per-outlet energy meter data + a structured EVSE state code, which the
Modbus automation interface doesn't expose: on Modbus we can only
guess "connected"/"charging" from current/power thresholds, whereas
MQTT gives us the authoritative 4-letter code (CHRG / PAUS / EVRD /
CONN / NCRQ / …). The driver snaps onto the first CBID it sees (or a
configured one), tolerates wildcard / custom base topics, and carries
last-known values between low-rate broker updates.

Pair with drivers/ctek.lua or drivers/ctek_v2.lua for control — the
MQTT interface is documented read-only by CTEK; start/pause/set
commands are explicitly refused here with a log line pointing at the
Modbus driver.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three related onboarding tweaks:

* Manual device entry: rename "I know my device IP" to "Add device
  manually" and flip the order. Clicking it now skips straight to the
  driver picker (step 4) with the full catalog (no protocol filter),
  and step 5 renders connection fields based on the chosen driver's
  declared protocols + connection_defaults. Modbus drivers get host /
  port / unit_id; MQTT drivers get host / port / username / password;
  local HTTP drivers get host / port. Cloud HTTP drivers (Easee) show
  a hint pointing at the Integrations step instead of a broken form.
  A small registry of driver-specific extras (CTEK MQTT's base_topic /
  cbid / outlet) rides on top, keyed by DRIVER.id.

* EV Charger options: step 7 now lists Easee (cloud), CTEK Chargestorm
  (Modbus) and CTEK Chargestorm (MQTT telemetry). Easee continues to
  land in the ev_charger block; the CTEK entries instead materialise
  as a driver entry in cfg.drivers so they show up in the regular
  device list + respect the clamp/dispatch stack.

* Zero-device exit: let saveConfig POST a config with zero drivers so
  operators can finish onboarding now and add hardware from /settings
  later. Backend Validate() matches — only enforces "at least one
  site meter" when drivers are present, zero drivers is fine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The driver picker was showing entries in whatever order the catalog
API returned them (which sorts by Name server-side), and concatenated
manufacturer + name mechanically — the latter made "CTEK Chargestorm"
display as "CTEK CTEK Chargestorm (API v1) (modbus, ev)" because the
driver's Name already led with its own manufacturer.

Client-side now:
- alphabetical sort by the final rendered label via localeCompare,
  defense-in-depth against API ordering changes
- only prepend manufacturer when Name doesn't already start with it

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups that make onboarding less sticky:

* Drop the "at least one driver must be is_site_meter: true" check
  from config.Validate(). Operators commonly land in a state where
  the first driver they add is an EV charger or battery (grid meter
  comes later over MQTT/Modbus), and hard-failing the whole config
  at that point forces awkward dummy-meter workarounds. Runtime
  components that actually need a site meter (dispatch, clamp,
  control loop) already gate on telemetry presence — parse time is
  the wrong place to enforce this. Per-driver checks (name, lua
  path, at least one capability, dupe name) still apply unchanged.

* Add a "Skip setup — I'll configure later" button on step 1 of
  the wizard. Posts a minimal default config (16 A / 3-phase / 230 V,
  no drivers) to /api/config, which triggers the bootstrap-to-main
  restart. Returning operators / Pi redeploys can land on the main UI
  in two clicks and finish configuration from /settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adding a site meter via /settings after the initial startup wired the
driver correctly (telemetry appears in Advanced + per-driver cards)
but never showed up on the hero flow: api.handleStatus reads
ctrl.SiteMeterDriver, and that field was captured at boot — when the
user started with no meter it stayed "" forever, so the top-level
grid_w / load_w in /api/status always read 0 regardless of what the
Zap / Ferroamp / Pixii etc. driver was actually emitting.

configreload.reload() already applies scalar control-level changes
(grid target, tolerance, slew, min interval) under ctrlMu. Adds the
same for site meter: re-derive newCfg.SiteMeterDriver() and assign
it when it differs from the current value. No restart required to
promote / demote a driver to site meter anymore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@erikarenhill
erikarenhill force-pushed the feat/ctek-chargestorm-driver branch from 5a976ff to da80cd0 Compare April 19, 2026 00:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds CTEK EV charger support to the driver catalog and setup/onboarding flow, while relaxing config validation to allow completing onboarding before any hardware (or site meter) is configured.

Changes:

  • Added three CTEK drivers (Modbus API v1, Modbus API v2, MQTT telemetry-only) and documented them in the driver catalog.
  • Reworked the setup wizard’s “add device” and driver configuration steps (manual add path, sorted/cleaner driver labels, dynamic connection/config fields, and a “skip setup” fast path).
  • Updated Go config validation + reload behavior to support empty drivers and late site-meter selection via /settings.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
web/setup.js Refactors wizard flow (manual add, dynamic connection/config field rendering, EV integration options, skip-setup).
web/setup.html Updates wizard UI to match new flow (manual add button, dynamic driver fields containers, EV charger type options, skip setup button).
go/internal/configreload/watcher.go Updates control state on config reload to promote/demote site meter driver.
go/internal/config/config_test.go Adjusts tests to accept configs with zero drivers and with no site meter.
go/internal/config/config.go Relaxes validation rules (no longer requires drivers/site meter) and documents rationale.
drivers/ctek.lua New Modbus/TCP CTEK Chargestorm Automation API v1 driver (telemetry + current-limit control).
drivers/ctek_v2.lua New Modbus/TCP CTEK Chargestorm Automation API v2 driver (offset register map).
drivers/ctek_mqtt.lua New MQTT telemetry-only CTEK driver (rich per-phase telemetry + EVSE state code).
docs/driver-catalog.md Adds CTEK entries and updates the “at a glance” summary numbers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread drivers/ctek.lua
Comment on lines +78 to +85
tested_models = { "Chargestorm Connected 2", "Chargestorm Connected 3" },
verification_status = "beta",
verification_notes = "Register map per CTEK Automation interface v1.0; charging-limit write verified against CSOS 4.9.x. Derived charging/connected flags approximate the real EVSE state since the state code is MQTT-only.",
connection_defaults = {
port = 502,
unit_id = 1,
},
}
Comment thread drivers/ctek_mqtt.lua Outdated
Comment on lines +99 to +106
authors = { "forty-two-watts contributors" },
tested_models = { "Chargestorm Connected 2", "Chargestorm Connected 3" },
verification_status = "beta",
verification_notes = "Topic layout and payload shapes per CTEK 'Automation interface' PDF v1.0 rev 6b4af7. Telemetry-only — control MUST come from the Modbus driver.",
connection_defaults = {
port = 1883,
},
}
Comment thread drivers/ctek_v2.lua
Comment on lines +78 to +85
tested_models = { "Chargestorm Connected 2", "Chargestorm Connected 3" },
verification_status = "beta",
verification_notes = "Register map per CTEK Automation interface v1.0 (API v2 offsets); charging-limit write verified against CSOS 4.9.x. Derived charging/connected flags approximate the real EVSE state since the state code is MQTT-only.",
connection_defaults = {
port = 502,
unit_id = 1,
},
}
Comment thread docs/driver-catalog.md
Comment on lines +9 to +10
22 drivers covering 17 manufacturers across 3 protocols (Modbus TCP,
MQTT, HTTP/REST). Read-only: 13. With control: 9.
Comment on lines +127 to +137
// Promote / demote the site meter. Without this, adding a meter
// driver via /settings wires up the driver itself (telemetry flows,
// shows up in Advanced) but `api.handleStatus` keeps reading
// `ctrl.SiteMeterDriver` — which was empty at boot when no meter
// was configured yet — so the hero UI's grid_w stays at 0 and the
// meter looks "missing from the flow" even though it's emitting.
newSite := newCfg.SiteMeterDriver()
if newSite != w.ctrl.SiteMeterDriver {
slog.Info("config reload: site_meter", "old", w.ctrl.SiteMeterDriver, "new", newSite)
w.ctrl.SiteMeterDriver = newSite
}
Comment thread docs/driver-catalog.md Outdated
Comment on lines +32 to +34
| CTEK Chargestorm (API v1) | CTEK | Modbus | ev | yes | Chargestorm Connected 2/3 (CSOS ≥ 4.9.3) | `drivers/ctek.lua` |
| CTEK Chargestorm (API v2) | CTEK | Modbus | ev | yes | Chargestorm Connected 2/3 (CSOS ≥ 4.9.3) | `drivers/ctek_v2.lua` |
| CTEK Chargestorm (MQTT, telemetry) | CTEK | MQTT | ev | no | Chargestorm Connected 2/3 (CSOS ≥ 3.11.15.1) | `drivers/ctek_mqtt.lua` |
Comment thread web/setup.js Outdated
Comment on lines +245 to +253
// Local HTTP driver (e.g. a REST-speaking inverter on the LAN) —
// CD advertises a host. Cloud drivers (Easee) declare http_hosts
// but no CD.host, and their creds live in config.* instead; we
// punt those to the integrations step (step 7).
if (cd.host !== undefined || (!entry.http_hosts || entry.http_hosts.length === 0)) {
fields.push({ target: 'capabilities.http.host', key: 'host', label: 'IP address', type: 'text',
placeholder: '192.168.1.10', required: true });
fields.push({ target: 'capabilities.http.port', key: 'port', label: 'Port', type: 'number',
default: cd.port != null ? cd.port : 80 });
Comment thread web/setup.js Outdated
Comment on lines +246 to +254
// CD advertises a host. Cloud drivers (Easee) declare http_hosts
// but no CD.host, and their creds live in config.* instead; we
// punt those to the integrations step (step 7).
if (cd.host !== undefined || (!entry.http_hosts || entry.http_hosts.length === 0)) {
fields.push({ target: 'capabilities.http.host', key: 'host', label: 'IP address', type: 'text',
placeholder: '192.168.1.10', required: true });
fields.push({ target: 'capabilities.http.port', key: 'port', label: 'Port', type: 'number',
default: cd.port != null ? cd.port : 80 });
} else {
erikarenhill and others added 6 commits April 19, 2026 02:26
paho.mqtt.golang only falls through to SetDefaultPublishHandler for
messages that don't match any per-subscription callback, and even
then the routing for *live* (non-retained) publishes has historically
been inconsistent when the subscription callback is nil. Symptom we
hit on the CTEK Chargestorm: the driver subscribes to
`CTEK/<CBID>/evse1/{em,status,meterinfo}` successfully (subscribe ACK
comes back), retained messages on neighbouring CCU/... topics route
through DefaultPublishHandler fine, but the live em + status publishes
that fire every ~8 s never surface — `host.mqtt_messages()` returns
empty on every poll, so the driver stays stuck at ev_w=0 / state
unknown while MQTT Explorer shows CHRG + 9 kW.

Fix: give the Capability its own handler closure, wire it to both
SetDefaultPublishHandler (so retained discovery still works) AND
every Subscribe call (so live publishes route through the exact
same path). Drains into cap.incoming the same way either route.

No driver changes required — the fix lives one layer down. CTEK
MQTT driver + any future MQTT driver benefits automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CTEK's MQTT telemetry namespace publishes assigned current on
status.assigned — which is the current the station is offering to an
active EV session, not the hardware ceiling. When no car is plugged
(state=AVAL) or a session is paused, assigned=0, and the driver was
mirroring that straight into ev.max_a. The UI shows "0 A max"
whenever the charger is idle, which reads as a config error.

Mirror the Modbus driver's config shape by adding config.max_a (with
config.min_a for consistency and an IEC 61851 floor at 6 A). When
there's an active session with assigned > 0, still report that so
dispatch math reflects the real offered current; otherwise fall back
on max_a. Hardware FuseRating lives in CCU/.../Configuration which
this driver intentionally stays out of, so this per-site config
value is the clean way to surface the ceiling over pure MQTT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In hero mode the numeric summary tiles (incl. #card-ev) are hidden,
so clicking anywhere on the hero diagram did nothing — there was no
surface to open the EV detail modal from. Backend-side the EV was
detected correctly (ctek-chargestorm-mqtt emits ev_w / state_label /
ev_max_a), the UI just had no hook to surface it.

<ftw-energy-flow> now accepts an optional `kind` per circle and fires
a composed / bubbling `ftw-flow-node-click` CustomEvent on the host
when a tagged circle is activated (click or Enter/Space). Today only
the EV circle opts in via kind="ev" + clickable=true; other node
kinds can join by flipping the same two flags.

next-app.js listens for the event on the <ftw-energy-flow> element
and routes "ev" → the same openEvModal() path the legacy #card-ev
tile used, so keyboard / click / backdrop close / refresh-every-5s
behaviour is identical between hero and numbers modes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge the telemetry-only ctek_mqtt.lua into ctek.lua and ctek_v2.lua so a
single driver per API version owns both the Modbus control path and the
richer MQTT telemetry (EVSE state code, per-phase em, grid frequency).
MQTT is optional and opportunistic — the driver falls back to Modbus for
both telemetry and cold-start coverage when the broker isn't reachable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the hard-coded Easee/CTEK options and field blocks in the setup
wizard. The EV provider list now comes from /api/drivers/catalog
filtered to entries whose capabilities include "ev"; fields render from
the same connection_defaults + extras helpers used by the main driver
step. Adding a new EV driver requires only dropping a .lua file in
drivers/ — no HTML/JS edits. Field targets drive save dispatch
(ev_charger.* → cfg.ev_charger for cloud providers, everything else →
cfg.drivers[]), and hybrid drivers auto-wire their secondary protocol
on the primary host.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Modbus interface (per CTEK Automation Interface PDF rev 6b4af7)
covers everything the EMS uses: per-phase telemetry, lifetime energy,
serial, charging-limit read+write, and max-assignment. The only thing
MQTT added was the 4-letter EVSE state code, which the dispatcher
doesn't consume — connected/charging is derived from current+power
just as well. Dropping MQTT removes the broker dependency and the
dual-source state reconciliation, leaving one config knob (host:502)
per charger. Setup wizard's per-protocol field renderer keeps the
multi-protocol path intact for any future hybrid driver that genuinely
needs it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class support for CTEK Chargestorm EV chargers and improves onboarding flows so EV drivers can be configured from the driver catalog (including “skip setup” / “no drivers yet” paths).

Changes:

  • Add new CTEK Chargestorm Modbus drivers (API v1 + v2) and document them in the driver catalog.
  • Refactor the web setup wizard to render driver connection/config fields dynamically from the catalog, add “skip setup”, and populate EV providers from /api/drivers/catalog.
  • Improve runtime robustness: MQTT subscriptions use a consistent publish handler; config reload now updates the active site meter; config validation accepts empty drivers/no site meter.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
web/setup.js Dynamic connection/config field rendering, EV provider catalog integration, skip setup, relaxed “no drivers” flow
web/setup.html UI updates for manual add, dynamic driver/EV fields, skip setup button
web/next-app.js Open EV modal from hero-mode energy-flow clicks
web/components/ftw-energy-flow.js Make EV node clickable and dispatch node-click events (mouse + keyboard)
go/internal/mqtt/client.go Reuse a shared message handler for default publish + subscriptions
go/internal/configreload/watcher.go Hot-reload now promotes/demotes SiteMeterDriver
go/internal/config/config.go Validation now allows empty drivers and no site meter
go/internal/config/config_test.go Update tests to match relaxed validation rules
drivers/ctek.lua New CTEK Chargestorm Modbus driver (API v1)
drivers/ctek_v2.lua New CTEK Chargestorm Modbus driver (API v2 offsets)
docs/driver-catalog.md Update catalog counts and add CTEK entries

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread web/setup.js
Comment on lines +431 to +432
if (f.key === 'host' && selectedDevice.ip) f.default = selectedDevice.ip;
if (f.key === 'port' && selectedDevice.port) f.default = selectedDevice.port;
Comment thread web/setup.js
Comment on lines +259 to +273
} else if (proto === 'http') {
// Local HTTP driver (e.g. a REST-speaking inverter on the LAN) —
// CD advertises a host. Cloud drivers (Easee) declare http_hosts
// but no CD.host, and their creds live in config.* instead; we
// punt those to the integrations step (step 7) via cloudHint.
if (cd.host !== undefined || (!entry.http_hosts || entry.http_hosts.length === 0)) {
fields.push({ target: 'capabilities.http.host', key: 'http_host',
label: prefix + 'IP address', type: 'text',
placeholder: '192.168.1.10', required: hostRequired });
fields.push({ target: 'capabilities.http.port', key: 'http_port',
label: prefix + 'Port', type: 'number', default: port });
} else if (isPrimary) {
fields.push({ cloudHint: true,
message: 'This driver is a cloud API. Configure its credentials in the "Optional integrations" step (step 7).' });
}
Comment thread web/setup.js
if (!entry) return [];
var out = [];
if (entry.id === 'ctek-chargestorm' || entry.id === 'ctek-chargestorm-v2') {
out.push({ target: 'config.outlet', key: 'outlet', label: 'Outlet (passed via unit_id)', type: 'number',
Comment thread web/setup.js
Comment on lines +703 to +704
var hostInp = document.querySelector('#ev-provider-fields [data-key="host"]');
var serialInp = document.querySelector('#ev-provider-fields [data-key="serial"]');
Comment thread drivers/ctek.lua
Comment on lines +76 to +85
homepage = "https://www.ctek.com",
authors = { "forty-two-watts contributors" },
tested_models = { "Chargestorm Connected 2", "Chargestorm Connected 3" },
verification_status = "beta",
verification_notes = "Register map per CTEK Automation interface v1.0; charging-limit write verified against CSOS 4.9.x. Derived charging/connected flags approximate the real EVSE state since the state code is MQTT-only.",
connection_defaults = {
port = 502,
unit_id = 1,
},
}
Comment thread drivers/ctek_v2.lua
Comment on lines +76 to +85
homepage = "https://www.ctek.com",
authors = { "forty-two-watts contributors" },
tested_models = { "Chargestorm Connected 2", "Chargestorm Connected 3" },
verification_status = "beta",
verification_notes = "Register map per CTEK Automation interface v1.0 (API v2 offsets); charging-limit write verified against CSOS 4.9.x. Derived charging/connected flags approximate the real EVSE state since the state code is MQTT-only.",
connection_defaults = {
port = 502,
unit_id = 1,
},
}
@frahlg

frahlg commented Apr 25, 2026

Copy link
Copy Markdown
Member

Hey @erikarenhill — circling back on this one. A few things have landed on master since this branch was last touched that I think you'll want to fold in before this is ready to merge:

Current state:

  • Branch is CONFLICTING against master.
  • Still in draft since 2026-04-19.
  • CI was green on the last push.

When you have a moment, would you mind:

  1. Rebasing onto current master (or merging master in, whichever you prefer),
  2. Resolving the next-app.js / ftw-energy-flow.js conflicts,
  3. Marking the PR ready for review if it's complete on your end?

Happy to help review once it's rebased — just ping me. No rush, but flagging so it doesn't drift further.

gusahlg pushed a commit to gusahlg/forty-two-watts that referenced this pull request May 14, 2026
Adopts the Modbus-TCP-only EV charger drivers from the CTEK
Chargestorm work on PR srcfl#116, branched fresh off master to drop the
unrelated MQTT / setup-wizard / energy-flow commits the original PR
accumulated.

Two drivers because CTEK's CSOS firmware exposes the same Modbus
register set at two different base offsets depending on the CCU's
api_version setting:

  - drivers/ctek.lua     — API v1 (0x1xxx range)
  - drivers/ctek_v2.lua  — API v2 (0x2xxx range)

The semantics, units, and function codes are identical; only the
addresses differ. CTEK recommends running either the charge-control
API or the NANOGRID™ limit-control on v2 if you want both enabled
simultaneously.

What both emit:
- Total active power (W), per-phase current (A), per-phase voltage (V)
- Lifetime energy (Wh, u32 BE)
- Charging limit setpoint + max-assignment ceiling
- Serial number (anchors device_id to EVSE hardware identity)

Control path: writes the charging-limit register (0x1200 / 0x2200) in
amperes, 0 to pause. Driver maps the host-API standard EV actions
(ev_set_current / ev_pause / ev_start / ev_resume / init / deinit)
onto register writes; `last_set_a` is remembered so ev_resume restores
the previous setpoint.

Known limitation: CTEK does NOT expose the EVSE state code over
Modbus — it's MQTT-only. `charging`/`connected` are derived from the
live current + power readings, which is sufficient for the dispatch
clamp but won't distinguish "car plugged, awaiting authentication"
from "nothing plugged in".

Requires Chargestorm Connected 2 or 3 with CSOS firmware 4.9.3 or
later AND Automation/ModbusTCPEnable=true on the CCU.

Verification status: beta. Register map per CTEK Automation interface
v1.0 (rev 6b4af7); charging-limit write verified against CSOS 4.9.x.

Catalog parses both entries cleanly; internal/drivers tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <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.

3 participants