Reach the charger where it is, instead of waiting for it to call
OCPP asks the charger to dial us: a public endpoint, a TLS certificate, and a route in through the customer's router. Our own handler then demanded two more things the V1 does not offer — TLS on a charger that connects over ws://, and Basic auth credentials the Anker app has no field for — so every connection was turned away before the upgrade. Anker publishes a Modbus TCP register map for this charger, and it inverts the problem: we dial the charger, on its own network, with no inbound reachability to arrange. That works for a charger behind a router that OCPP cannot reach at all. internal/modbus is the protocol, hand-rolled against the spec like the MQTT and WebSocket clients beside it. The plugin's modbus.go is the V1's map: the same 0-8 status enum the cloud already reports, per-phase measurements, and the writable registers behind start, stop, current limit, boost and phase mode. A new "modbus" control mode routes the existing control endpoints down it, so the REST surface, the rate limit, the confirmation step and the audit trail are the ones already there. The commands the register map has no equivalent for say so by name rather than failing as unknown, and a current below the charger's 6 A floor is refused because it pauses the charge rather than slowing it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e9a82a1cca
commit
f7472bada3
+18
-9
@@ -31,6 +31,7 @@ internal/
|
||||
├── config/config.go # env + .env load, .env write-back
|
||||
├── models/models.go # domain types + derived-field computation
|
||||
├── mqtt/ # hand-rolled MQTT 3.1.1 client (Greencell EVSE telemetry)
|
||||
├── modbus/ # Modbus TCP client (Anker Solix local charging control)
|
||||
├── ocpp/ # OCPP 1.6J Central System (Anker Solix charging control)
|
||||
├── pb/client.go # PocketBase superuser client (runtime-retargetable)
|
||||
└── plugins/ # plugin system — see plugins/README.md
|
||||
@@ -120,7 +121,7 @@ other users `read` or `write` access. Every car/service/part handler is gated by
|
||||
| `car_shares` | grants another user access to a car | car, user, `permission` (read \| write) |
|
||||
| `organizations` | tenants | name (unique) |
|
||||
| `home_chargers` | the chargers a user owns, imported from a connected charger service | name, serial, vendor, model, site_name, power_kw, connector, `provider`, `provider_charger_id`, owner |
|
||||
| `control_audit` | OCPP control-command audit trail | user, charger, action, result, timestamp |
|
||||
| `control_audit` | charger control-command audit trail | user, charger, action, result, timestamp |
|
||||
| `users` | login + profile (built-in auth collection) | name, email, avatar, `role` (user \| admin \| superadmin), `organization`, bio, theme, locale, date_format, currency, font_size, deletion_requested_at |
|
||||
|
||||
Every record collection except `car_shares` / `organizations` / `control_audit`
|
||||
@@ -184,10 +185,12 @@ GET /api/integrations/greencell PUT /api/integrations/greencell POST /a
|
||||
GET /api/integrations/greencell/chargers
|
||||
GET /api/integrations/greencell/chargers/{sn}/state
|
||||
|
||||
# Anker Solix OCPP charging control (own/proxy mode + a live CSMS session)
|
||||
# Anker Solix charging control (Modbus TCP locally, or OCPP own/proxy mode)
|
||||
GET /api/integrations/anker-solix/chargers/{sn}/control
|
||||
POST /api/integrations/anker-solix/chargers/{sn}/control/token
|
||||
DELETE /api/integrations/anker-solix/chargers/{sn}/control/token
|
||||
PUT /api/integrations/anker-solix/chargers/{sn}/control/address # local Modbus address
|
||||
DELETE /api/integrations/anker-solix/chargers/{sn}/control/address
|
||||
POST /api/integrations/anker-solix/chargers/{sn}/{action}
|
||||
GET /ocpp/{serial} # charger dials in here (OCPP Basic auth, not bearer)
|
||||
|
||||
@@ -314,13 +317,19 @@ can be registered at runtime with no rebuild.
|
||||
|
||||
Beyond the superadmin plugin registry, the built-in connectors are exposed
|
||||
per-user through `/api/integrations/*` under a **superadmin → org admin → user**
|
||||
cascade (each layer supplies defaults the next can override). For Anker Solix
|
||||
chargers the server additionally runs an **OCPP 1.6J Central System**
|
||||
(`internal/ocpp`): when the owner sets a control mode of own/proxy, the charger
|
||||
dials back in at `GET /ocpp/{serial}` (authenticated with OCPP Basic auth using a
|
||||
per-charger control token, not a bearer token) and the owner can start/stop and
|
||||
set charge limits, with every command rate-limited and written to a
|
||||
`control_audit` trail.
|
||||
cascade (each layer supplies defaults the next can override).
|
||||
|
||||
Anker Solix chargers can be controlled two ways, chosen per user with the control
|
||||
mode. **Modbus TCP** (`internal/modbus`) dials the charger on the local network
|
||||
using the register map Anker publishes for the V1; the owner enables it in the
|
||||
Anker app under Settings > Integrations and saves the address it shows. It needs
|
||||
no inbound connectivity, which is what makes it the workable path for a charger
|
||||
behind a customer's router. The two **OCPP 1.6J** modes instead run a Central
|
||||
System (`internal/ocpp`) that the charger dials back into at `GET /ocpp/{serial}`
|
||||
(authenticated with OCPP Basic auth using a per-charger control token, not a
|
||||
bearer token), which requires the charger to be able to reach this server. Either
|
||||
way the owner can start/stop and set charge limits, with every command
|
||||
rate-limited and written to a `control_audit` trail.
|
||||
|
||||
**Greencell** takes the other route. The HabuDen wallbox has no cloud API: it is
|
||||
commissioned over Bluetooth in the Greencell GC app, pointed at an MQTT broker
|
||||
|
||||
Reference in New Issue
Block a user