423bc2ab16db2d3ba7340edff50b7c5e60daf35c
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a3f69fa5ef |
Home chargers: your own wallbox as a record, imported the way a car is
The Home chargers tab has been showing a hardcoded "Home charger · 11 kW · NACS" since it was drawn, and the control card asked for a serial as free text — a number printed on a box hanging in a garage, typed in by hand while the connected account already knew it. The garage solved the same problem for cars a while ago, so this is that solution aimed at the wall: pick the charger off a service you have connected, press Import, and it becomes a record of yours. A charger is a record rather than a live listing because it has to outlive the account it came from. Disconnect Anker and the wallbox is still on the wall; the integration is how the charger was found, not what it is. Hence home_chargers, owned by a person and not related to any car — it charges whichever car is plugged into it, and it outlives all of them — and hence no sharing: a charger is one household's business in a way a car shared with a partner is not. The provider layer is vehicleproviders.go's shape on purpose, down to the soft gate: a listing answers 200 with an empty list and the sentence that says what to do about a closed gate, a write answers 400, because there the caller asked for something that did not happen. Anker and Greencell are two adapters over plugins that already exist, so the next charger service is an adapter appended to chargerSources() and nothing else. What is deliberately absent is the car import's checkbox panel: a charger is a name, a serial and the hardware behind it, all of which the list already carries, so there is nothing to choose and the whole screen is pick one, press Import. Only the name is editable afterwards. The rest describes hardware and came from the service, and the provider link is written by the import endpoint alone, so renaming a charger cannot quietly orphan it from the account it tracks. Deleting one says as much in its confirmation: the charger is untouched, and importing it again brings the record straight back. The Anker gate moved into ankerGate() beside greencellGate(), because the same four-case switch was about to exist in a third place. Behaviour is unchanged — the same sentences, and the probe still skips the personal opt-in, since checking credentials is what you do before switching the integration on. The phone app still has the old tab; parity there is a separate change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e138fad3f4 |
Anker: every charger on the account, not just the ones outside a station
get_user_bind_and_not_in_station_evchargers is the only list the connector ever asked for, and its name says exactly what it withholds. A charger that belongs to a system is not in it. Its userBindEvChargersCount, though, counts every charger bound to the account — so an owner with two chargers in a system got "authenticated; 2 EV charger(s) bound to account" from the health probe and an empty list from the capability that is supposed to show them. A working login that finds nothing. So the capability now asks every view the cloud has and merges them by serial. The standalone list still answers for chargers standing on their own; get_site_list walks the systems and reads each one through get_scen_info, falling back to get_system_running_info where that is silent — the power-service / HES split charger-state already knows; and get_relate_and_bind_devices contributes model, firmware and the Wi-Fi flag, and discovers anything in the A519 family that the first two missed. Whichever way a charger was registered, one of the three has it. The merge is first-writer-wins per field rather than last view overwriting: the standalone record knows the name, the site record knows the live state, and neither should blank what the other established. A view that fails is a warning on the document instead of an error on the call, because one dead endpoint should not cost the chargers the other two found. Only losing all three is a failure. When nothing comes back at all the response says so in its own words and names the remaining suspect — country picks the regional server, and the wrong one authenticates happily and shows an empty account. The other half of "not showing any chargers" was that neither client ever showed a list. The serial was a text box, and the number is printed on a charger hanging on a wall. Both apps now list what the account holds — name, serial, model, site, state, an offline badge — and hand the serial to the OCPP control card instead of asking anyone to go and read it. Where control is off the list still stands on its own, as the answer to the first question an owner has after entering credentials. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ee4ac441be |
Plugins: drop the plugins.json migration, and the volume it needed
The project has no public installs, so there is nothing to migrate from. MigrateLegacyFile, the file-backed Store it read through, PLUGINS_FILE and the legacy path threaded through the Server all go. What is left is one store, PocketBase, and a plugins package that touches no filesystem at all. That was the last thing keeping api_data alive, so the volume goes too. All four compose files now declare exactly one volume, pb_data, and the standalone API Server compose declares none - it talks to an external PocketBase and has nothing of its own to keep. Backing up the stack is backing up one path again. Both images get simpler for it. The API Server image loses VOLUME /data and the su-exec entrypoint that existed only to fix a mounted volume's ownership, so it goes back to a plain USER app; its working directory is now /app and holds nothing. The AIO image loses its second volume and chowns only /pb/pb_data. One consequence worth stating plainly, because it is a small regression rather than a no-op. The panel's Settings -> PocketBase and Settings -> Web App screens write .env in the working directory, which is now ephemeral. In the multi-container stack that changes nothing: compose sets all five of those keys as container environment, and loadDotEnv only applies a key that is not already set, so the file could never win a restart there anyway. In the AIO image it did win for POCKETBASE_ADMIN_EMAIL/_PASSWORD, which are not in that container's environment - so a service account fixed from the panel now lasts only until the container is recreated. Both READMEs say so. Moving those two screens into the app_settings singleton would close it properly; the PocketBase URL and credentials cannot follow, since they are how the database is reached in the first place. go build, go vet and go test ./... pass; the compose files parse and each resolves to a single pb_data volume. Not verified: no Docker CLI here, so neither image was built. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
19a7d48feb |
Harden Anker Solix OCPP control (token hashing, step-up, audit, TLS)
Security pass over the OCPP charger-control feature added in
|
||
|
|
a1519f6e89 |
Add OCPP control for the Anker Solix EV charger (Own/Proxy CSMS)
The Anker Solix connector was read-only (cloud monitoring only). Add an
OCPP 1.6J control path with a per-user, cascading control mode:
- off monitoring only (default, unchanged behavior)
- own DriverVault is the charger's Central System (full control)
- proxy DriverVault relays to Anker's cloud and injects commands
New internal/ocpp subsystem (stdlib-only, hand-rolled RFC 6455): a CSMS
with session management, inbound dispatch, and typed control commands
(RemoteStart/Stop, SetChargingProfile current limit, ChangeAvailability,
Reset, UnlockConnector, TriggerMessage, Get/ChangeConfiguration). Own- and
proxy-mode paths are verified end-to-end against a simulated charge point.
The charger connects to /ocpp/{serial}, authenticated with OCPP Basic auth
(serial + a per-charger control token) resolved to the owning user via an
in-memory token index. Control REST endpoints mirror the monitoring ones and
reuse the same cascade gate plus a live-session check. controlMode is a new
cascade field (global -> org -> user) advertised as a select on the plugin.
Frontend: control-mode select + provisioning card in Settings, and a real
Start/Stop/limit/reset control panel in Charging, gated on the active mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
52d9614bad |
Wire the Anker Solix EV charger into the per-user integration cascade
Expose the anker-solix plugin to end users the same way as Toyota: a global -> org -> user settings cascade so everyone can run it under their own Anker account, with a superadmin (and org admin) able to impose settings from above. API Server: integrations_ankersolix.go mirrors integrations.go with the Anker fields (email + password resolve as a pair from the highest layer, country resolves on its own), plus GET/PUT/health/chargers routes under /api/integrations/anker-solix. Secrets and inherited emails are masked; the live probe runs server-side under the resolved credentials. Web App: api.js client calls, a second Integrations card in Settings.vue (scope switch, enable toggle, email/password/country with locked-field inheritance notes, save + test), and the en.json strings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |