Bestehende Installationen brachen beim Start mit dem Windows-Release-Binary ab: "migration 1 was previously applied but has been modified" – obwohl die Migration inhaltlich seit v0.2.0 unverändert ist. Ursache: .gitattributes enthielt nur "* text=auto". Zusammen mit core.autocrlf=true (Standard auf windows-latest-Runnern) checkt actions/checkout die .sql-Dateien als CRLF aus. sqlx::migrate! bettet den Dateiinhalt zur Compile-Zeit ein und bildet darüber eine SHA-384-Summe, die beim Start gegen _sqlx_migrations geprüft wird: 0001_initial.sql mit LF -> 0651a39f... 0001_initial.sql mit CRLF -> 90093a9a... Die GitHub-Windows-Builds v0.2.0 und v0.3.0 tragen die CRLF-Variante, lokal gebaute Exes und der Linux-Build die LF-Variante. Datenbanken, die mit einem LF-Binary angelegt wurden, kollidieren daher mit dem Windows-Release-ZIP. Zwei Änderungen: 1. .gitattributes erzwingt "*.sql text eol=lf". Ab jetzt erzeugen Windows- und Linux-Runner identische Prüfsummen. 2. db::repair_line_ending_checksums() hebt eine gespeicherte Prüfsumme vor dem Migrationslauf an – aber nur, wenn sie exakt der Zeilenenden-Variante desselben SQL entspricht, in beide Richtungen. Ohne diesen Schritt würde Punkt 1 jede Installation brechen, die mit dem CRLF-Binary aus dem GitHub-ZIP von v0.2.0/v0.3.0 angelegt wurde. Echte inhaltliche Abweichungen führen weiterhin zum Abbruch durch sqlx. sha2 wird dafür direkte Dependency (war bereits transitiv gelockt, die Auflösung ändert sich nicht). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
easy-occp
🌐 English · Deutsch · Français · Español
Simple management tool for EV charging stations (wallboxes), built for SMBs with 1–10 wallboxes.
- One binary, one SQLite file – no external database, no message broker.
- OCPP 1.6J (complete) + OCPP 2.0.1 (WebSocket scaffold, BootNotification / TransactionEvent) + OCPP 1.5 SOAP (scaffold for Boot/Heartbeat).
- Modern web UI (Askama + htmx), local users with Argon2 passwords.
- Active Directory (LDAP) and Microsoft Entra (OIDC) are prepared as configuration fields – the concrete bind/flow implementations will follow.
- Runs on Windows (primary focus) and Linux.
Features
| Area | Status |
|---|---|
| Wallbox inventory + status (online/offline, connectors, firmware) | ✅ |
| RFID chip management, enrollment via "learning window" (2 min, Authorize intercept) | ✅ |
| Chip → employee assignment, guest chips, validity expiry | ✅ |
| Remote unlocking for guests, billing to guest label | ✅ |
| Live values during charging (charged kWh, current power, SoC) | ✅ |
| Transaction list, filter by user | ✅ |
| Statistics by month / quarter / year | ✅ |
| User management (admin/user), local password | ✅ |
| Multilingual UI (Deutsch, English, Français, Español) | ✅ |
| Active Directory (LDAP) | 🟡 Config prepared |
| Entra ID (OIDC) | 🟡 Config prepared |
Getting started
Prebuilt binaries (Windows x64, Linux x64) are available under
Releases — unpack,
run easy-occp.exe or easy-occp, done (see INSTALL.md in the package).
Or build it yourself:
# once: create a config (optional)
copy config.example.toml config.toml
# Build + start
cargo run --release
Then open the UI at http://localhost:8080. Default login on first start: admin / admin – please change the password right away under "Users".
Forgot the admin password?
cargo run --release -- --reset-admin "newPassword123"
Configuring a wallbox
Wallboxes establish a WebSocket connection:
ws://<host>:8080/ocpp/<ChargePointId>
Subprotocols are negotiated automatically (ocpp1.6 or ocpp2.0.1).
For older OCPP 1.5 devices (SOAP):
POST http://<host>:8080/ocpp15
Live meter readings during charging
When an OCPP 1.6 wallbox connects, the server automatically configures it to
report the meter reading, power, and (if available) SoC every 30 seconds during
a charging session (MeterValueSampleInterval, MeterValuesSampledData).
The interval can be adjusted via config.toml; 0 disables the
auto-configuration:
[ocpp]
meter_interval_s = 30
The cockpit and the wallbox detail page automatically refresh active charging sessions every 10 seconds (htmx polling). If a wallbox does not report power, it is derived from the last two meter readings.
Data storage
Everything lives in one SQLite file at data/easy-occp.db (changeable via config.toml). Migrations reside in migrations/ and are applied automatically at startup.
Sanity checks on incoming data
- Timestamps: >24 h in the future or >10 years in the past are discarded – fallback to the server clock.
- StartTransaction / StopTransaction: Idempotent against repeats; decreasing meter values are corrected.
- StatusNotification: UPSERT per (wallbox, connector) – no duplicates.
- MeterValues: negative values are discarded, SoC validated to 0–100 %, kWh → Wh normalized.
- Enrollment: A newly captured tag is assigned to exactly one open learning-window session.
Project layout
src/
main.rs – entry point, Tokio runtime, SQLite pool
config.rs – TOML configuration
db.rs – bootstrap + helpers (Argon2, settings)
error.rs – AppError / IntoResponse
auth/ – session cookies + local login
domain/ – data models (FromRow)
ocpp/
wire.rs – OCPP JSON frame parser
hub.rs – registry of all active connections
ocpp16.rs – OCPP 1.6J (complete)
ocpp20.rs – OCPP 2.0.1 (bootstrap)
soap15.rs – OCPP 1.5 SOAP endpoint
web/ – axum router, Askama views
templates/ – HTML templates (Askama)
static/ – CSS + htmx shim (embedded via rust-embed)
migrations/ – SQLite migrations
License
MIT