Zum Inhalt

SNLO – Spittank.net Ladeorganisation – Konfiguration

Die Laufzeitkonfiguration von SNLO – Spittank.net Ladeorganisation erfolgt über .env. Ausgangspunkt ist .env.example; python3 scripts/init-env.py erzeugt daraus eine sichere lokale Konfiguration mit zufälligen Geheimnissen.

Grundkonfiguration

Variable Bedeutung Standard/Beispiel
APP_ORIGIN öffentliche Basis-URL ohne abschließenden Pfad https://snlo.de
APP_TIMEZONE Zeitzone für Anzeige und Abrechnung Europe/Berlin
APP_CURRENCY Währung EUR
HTTP_PORT veröffentlichter Host-Port 8080
COOKIE_SECURE Cookies nur über HTTPS senden produktiv true
SESSION_TTL_HOURS normale Browser-Sitzung 24
PWA_SESSION_TTL_DAYS langlebige installierte PWA-Sitzung; wird beim App-Start erneuert 3650
LOG_LEVEL Python-Loglevel INFO

Hinter einem Reverse Proxy muss mindestens gelten:

APP_ORIGIN=https://snlo.de
COOKIE_SECURE=true

Der Proxy muss X-Forwarded-Proto und X-Forwarded-For korrekt setzen. Die Anwendung akzeptiert Zustandsänderungen nur von der konfigurierten Origin beziehungsweise ihrer effektiv erkannten eigenen Origin.

Datenbank und Geheimnisse

Variable Bedeutung
POSTGRES_DB Datenbankname
POSTGRES_USER Datenbanknutzer
POSTGRES_PASSWORD Datenbankpasswort
POSTGRES_VOLUME_NAME expliziter Name des persistenten Docker-Volumes
CREDENTIAL_ENCRYPTION_KEY Base64-URL-kodierter Schlüssel mit 32 Byte Nutzdaten
BOOTSTRAP_ADMIN_EMAIL E-Mail des initialen Administrators
BOOTSTRAP_ADMIN_NAME Anzeigename des initialen Administrators
BOOTSTRAP_ADMIN_PASSWORD initiales Passwort

CREDENTIAL_ENCRYPTION_KEY darf bei Updates nicht geändert werden. Andernfalls können bereits gespeicherte EVCC-, KEBA- und RFID-Geheimnisse nicht mehr entschlüsselt werden.

Vor Updates sollte die Datenbank gesichert werden:

docker compose exec -T db \
  pg_dump -U "${POSTGRES_USER:-snlo}" "${POSTGRES_DB:-snlo}" \
  > snlo-backup-$(date +%F).sql

Containerquelle

Der Compose-Stack kann dasselbe Image lokal bauen oder aus der Forgejo-Registry beziehen:

Variable Bedeutung Lokal
SNLO_IMAGE Image für migrate, api und worker snlo:local
SNLO_PULL_POLICY Compose-Pull-Policy build

Lokaler Build:

SNLO_IMAGE=snlo:local
SNLO_PULL_POLICY=build

Registry-Release:

SNLO_IMAGE=dev.spittank.org/daniel/snlo:0.5.12
SNLO_PULL_POLICY=always

Anschließend genügt jeweils:

docker compose up -d

Collector

Variable Bedeutung Standard
EVCC_POLL_INTERVAL_SECONDS Intervall für Livezustände 5
EVCC_SESSION_SYNC_INTERVAL_SECONDS Intervall für abgeschlossene Sitzungen 60
EVCC_REQUEST_TIMEOUT_SECONDS HTTP-Timeout je EVCC-Anfrage 10
BATTERY_MAX_SAMPLE_GAP_SECONDS größte noch verwertete Messlücke 15
CHARGING_POWER_THRESHOLD_W Mindestleistung für tatsächliches Laden 100

Jede EVCC-Instanz wird im Adminbereich dynamisch angelegt. Als URL wird nur die Basis-URL angegeben:

http://192.168.10.20:7070

Nicht angeben:

http://192.168.10.20:7070/api

Der EVCC-API-Key ist für die aktuell genutzten Status-, Sitzungs- und Ladepunktfunktionen normalerweise nicht erforderlich. Ist einer hinterlegt, sendet SNLO ihn als Bearer-Token.

Fahrzeuge und zahlende Nutzer

Die technische Fahrzeugerkennung ist von der Abrechnung getrennt:

EVCC-Fahrzeug
    -> internes Fahrzeug
        -> mehrere berechtigte Nutzer
        -> genau ein zahlender Nutzer je Zeitraum

Ein internes Fahrzeug kann mit Fahrzeugen aus beliebig vielen EVCC-Instanzen verknüpft werden. Sitzungen werden anhand des EVCC-Fahrzeugnamens oder -titels dem internen Fahrzeug zugeordnet. Der zum Sitzungsbeginn gültige zahlende Nutzer bestimmt die Abrechnung.

Tarife

Tarife sind benannte Vorlagen ohne unmittelbare Nutzer- oder Zeitraumzuordnung, beispielsweise:

Privat
Privat Plus
Gast
Dienstwagen

Im Nutzerbereich der Administration wird danach festgelegt, welcher Tarif in welchem Zeitraum gilt:

Alfred bis einschließlich 31.03.2026 -> Privat
Alfred ab 01.04.2026                 -> Privat Plus

Eine nutzerbezogene Zuordnung überschreibt den globalen Standard. Überlappende Zeiträume innerhalb desselben Geltungsbereichs sind nicht erlaubt.

Sitzungsdauer von Browser und PWA

Normale Browser-Anmeldungen verwenden SESSION_TTL_HOURS. Eine über den installierten PWA-Start ausgelöste Anmeldung wird als eigene PWA-Sitzung markiert und verwendet PWA_SESSION_TTL_DAYS. Bei jedem erfolgreichen Start der installierten PWA wird diese Laufzeit erneut ab dem aktuellen Zeitpunkt gesetzt. Standardmäßig sind das 3650 Tage, sodass die App auf einem persönlichen Gerät praktisch bis zum aktiven Abmelden angemeldet bleibt. Eine normale Browser-Sitzung wird nicht ohne erneute Anmeldung in eine PWA-Sitzung umgewandelt. Bei Browser und installierter PWA derselben Origin wird der HTTP-Cookie-Speicher je nach Plattform geteilt; nach einer PWA-Anmeldung kann die langlebige Sitzung deshalb auch im normalen Browser derselben Geräte-/Browserumgebung gelten.

Bei OIDC wird das ID-Token weiterhin während des Login-Vorgangs vollständig validiert. Die danach erzeugte langlebige PWA-Sitzung ist eine SNLO-Anwendungssitzung; Gruppen- und Rollenanpassungen beim Identity Provider werden deshalb spätestens bei der nächsten OIDC-Anmeldung synchronisiert. Administrativ deaktivierte SNLO-Konten verlieren den Zugriff dagegen sofort.

KEBA

Die optionale KEBA-P30-UDP-Anbindung verwendet je Ladepunkt Host, UDP-Port und Timeout. RFID-UIDs werden je Nutzer und KEBA-Verbindung verschlüsselt gespeichert. Die im KEBA-Webinterface übliche 8-stellige UID wird unverändert an die Wallbox übergeben. Intern ergänzt SNLO – wie die Home-Assistant-KEBA-Integration – den festen RFID-Class-Wert 01010400000000000000; Nutzer müssen keinen Klassifikator verwalten. Alte, durch SNLO 0.5.9 irrtümlich null-erweiterte Werte werden automatisch erkannt und mit der kurzen UID zuerst versucht. Die P30-UDP-Schnittstelle selbst benötigt keinen API-Key; ein optionales geheimes Feld ist nur für spätere Connectoren vorgesehen.

Zum Sperren versucht SNLO immer zuerst den dokumentierten Befehl mit der echten Nutzerkarte und prüft anschließend report 2/Authreq. Bleibt eine noch nicht ladende Wallbox trotz bestätigtem Befehl entsperrt, kann SNLO als gerätespezifischen Fallback eine absichtlich unbekannte RFID über start simulieren. Der Code wird mit KEBA_LOCK_FALLBACK_RFID konfiguriert (Standard 44556677). Der Fallback wird während einer aktiven Ladung nie verwendet und darf nicht als echte aktive RFID in SNLO hinterlegt sein.

SNLO betreibt für KEBA einen persistenten UDP-Listener auf Port 7090 im Dienst keba-gateway. Der Dienst verwendet Linux-Host-Networking, damit bei einem normalen rootful Docker Engine keine Docker-Bridge-NAT-Stufe zwischen dem gebundenen Socket und dem Hostnetz liegt. KEBA_GATEWAY_TOKEN schützt den internen API/Gateway-Kanal; neue Installationen erzeugen ihn mit scripts/init-env.py. Bei Upgrades muss ein mindestens 32 Zeichen langer Zufallswert ergänzt werden. UDP 7090 muss auf dem Docker-Host frei und aus dem Wallbox-Netz erreichbar sein.

KEBA-Netzwerkdiagnose

Live-Log:

docker compose logs -f keba-gateway

Ein Test erzeugt mindestens diese Zeilen:

KEBA HTTP exchange requested ...
KEBA UDP TX ... socket=0.0.0.0:7090 route_source_ip=...

Kommt die Antwort im Gateway an, folgt:

KEBA UDP RX peer=<wallbox-ip>:7090 local_port=7090 ...

Fehlt KEBA UDP RX, obwohl ein Paketmitschnitt eine Antwort an Port 7090 zeigt, wird das Paket vor dem Gateway verworfen oder in einen anderen Netzwerk-Namespace zugestellt. Prüfe dann insbesondere Rootless-Docker sowie NAT/Firewall zwischen Docker-Host und Wallbox.

Docker-Modus prüfen:

docker info --format '{{json .SecurityOptions}}'
docker version --format '{{.Server.Version}}'

Zum vollständigen Ausschluss von Docker kann der sichere Read-only-Test direkt auf dem Host ausgeführt werden. Vorher den Gateway stoppen, damit UDP 7090 frei ist:

docker compose stop keba-gateway
python3 scripts/keba-udp-probe.py <KEBA-IP>
docker compose start keba-gateway

Der Probe sendet ausschließlich report 1 und zeigt den vom Host-Kernel verwendeten lokalen Socket an.

OpenID Connect

Die vollständige Konfiguration steht unter OpenID Connect. Für den produktiven Betrieb sind insbesondere APP_ORIGIN, COOKIE_SECURE, Gruppenclaims und der lokale Notfallzugang zu prüfen.

TrueNAS SCALE

Siehe TrueNAS SCALE. Reverse Proxies werden auf die feste TrueNAS-Host-IP und Port 8080 gerichtet, nicht auf die wechselnde interne Docker-IP des API-Containers.