- Python 72.4%
- HTML 25.7%
- Dockerfile 1.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| __pycache__ | ||
| templates | ||
| .dockerignore | ||
| .gitignore | ||
| app.py | ||
| config.ini.example | ||
| docker-compose.yml.example | ||
| Dockerfile | ||
| dvb_api.py | ||
| list_stops.py | ||
| README.md | ||
| requirements.txt | ||
Straßenbahn-Abfahrtszeiten Dresden (DVB/VVO)
Lokale Python-Webanwendung, die live die Abfahrtszeiten von Straßenbahnen an konfigurierten Haltestellen in Dresden anzeigt.
Die Anwendung nutzt die öffentliche DVB/VVO WebAPI (keine API-Keys erforderlich) und ist für die Anzeige auf E-Ink-Displays optimiert: minimalistisches Design mit nur Schwarz und Weiß.
Installation
- Python 3.8+ vorausgesetzt.
Hinweis: config.ini.example dient als Vorlage mit allen verfügbaren Optionen und ihren Standardwerten; sie kann bei Bedarf kopiert werden (cp config.ini.example config.ini).
- Abhängigkeiten installieren:
pip install -r requirements.txt
Konfiguration
-
Haltestellen finden:
python list_stops.py <Suchbegriff>Beispiel:
python list_stops.py PostplatzDie Ausgabe zeigt die verfügbaren Haltestellen mit ihren exakten Namen. Wähle die korrekte Haltestellenbezeichnung.
-
config.inibearbeiten:Öffne
config.iniund trage die gewünschten Haltestellennamen unter[haltestellen]ein:[haltestellen] namen = Postplatz, AlbertplatzDu kannst auch
lookahead_minutes(Vorlaufzeit für angezeigte Abfahrten) undrefresh_seconds(Aktualisierungsintervall) anpassen.
Konfigurationsoptionen in config.ini
Unter [general] können folgende Optionen konfiguriert werden:
lookahead_minutes(Standard: 60) — Zeitfenster in Minuten: nur Abfahrten bis zu dieser Zeit in der Zukunft anzeigen.refresh_seconds(Standard: 60) — Seite automatisch aktualisieren in Sekunden.port(Standard: 8080) — Port, auf dem die Web-Anwendung läuft.show_title(Standard: true) — Seitentitel "Straßenbahn-Abfahrtszeiten Dresden" oben anzeigen (true/false).show_clock(Standard: true) — Aktuelle Uhrzeit (im Format HH:MM, Zeitzone Europe/Berlin) oben unter dem Titel anzeigen (true/false). Unabhängig vonshow_title.theme(Standard: light) — Farbschema:light(schwarzer Text auf weißem Hintergrund),dark(weißer Text auf schwarzem Hintergrund), odercustom(eigene Hintergrund- und Schriftfarben, siehe unten). Beilayout = einkwirdtheme = customignoriert und auflightzurückgefallen, um E-Ink-Kompatibilität zu wahren.custom_bg_colorundcustom_fg_color(nur relevant wenntheme = custom, Standard:#ffffffund#000000) — Eigene Hintergrund- bzw. Schriftfarbe als Hex-Wert im Format#RRGGBB(z.B.#001133für dunkelblau,#ffee00für gelb). Bei ungültigem Format wirdtheme = customauflightzurückgefallen.layout(Standard: highres) — Auflösungsmodus mit Auswirkungen auf Schriftgrößen und Abstände. Erlaubte Werte:highres— Optimiert für normale Displays (16px Basisgröße).tablet— Optimiert für Tablets mit größeren Schriften (18px Basisgröße).eink— Optimiert für E-Ink-Displays, setzt die Seite auf eine feste Größe von 800×480px (16px Basisgröße). Bei ungültigem Wert wird automatisch aufhighreszurückgegriffen.custom— Eigene feste Seitengröße, kontrolliert übercustom_widthundcustom_height(siehe unten). Schriftgrößen entsprechenhighres.
custom_widthundcustom_height(nur relevant wennlayout = custom, Standard je 800 und 480) — Breite und Höhe der Anzeige in Pixel. Bei ungültigen Werten (nicht-numerisch oder ≤0) wird automatisch aufhighreszurückgegriffen.max_departures_per_stop(Standard: 6, nur wirksam beilayout = einkoderlayout = custom) — Begrenzt die Anzahl der pro Haltestellen-Spalte gerenderten Abfahrten. Verhindert, dass der Inhalt die feste Höhe übersteigt und durchoverflow: hiddenabgeschnitten wird. Hat keine Auswirkung beilayout = highresoderlayout = tablet, da diese nicht auf eine feste Höhe begrenzt sind.
Zusätzlich kann der optionale Abschnitt [richtungen] konfiguriert werden:
- Richtungsfilter — Für jede Haltestelle und Linie kann eine bestimmte Richtung vorgegeben werden. Format: Haltestellenname = Linie:Richtung, Linie:Richtung, ...
- Beispiel:
Postplatz = 1:Prohlis, 4:Radebeul Westzeigt für Linie 1 an dieser Haltestelle nur Züge mit Richtung "Prohlis" an. - Linien, die nicht angegeben sind, zeigen weiterhin alle Richtungen.
- Der gesamte Abschnitt ist optional — fehlt er, ändert sich nichts.
- Tipp: Nutze
python list_stops.py --richtungen <exakter Haltestellenname>, um die verfügbaren Richtungen für eine Haltestelle anzuzeigen.
- Beispiel:
URL-Parameter
Die Anwendung unterstützt URL-Parameter zum Überschreiben von Konfigurationsoptionen pro Aufruf (ohne dauerhafte Änderungen an config.ini). Diese Parameter haben Vorrang vor den Werten in config.ini:
?theme=light|dark|custom— Überschreibtthemeausconfig.inifür den aktuellen Aufruf.- Beispiel:
http://localhost:8080/?theme=darkaktiviert Dark Mode für diesen Aufruf. theme=customaktiviert benutzerdefinierte Farben (siehe?bg=und?fg=unten). Beilayout=einkwirdtheme=customignoriert und auflightzurückgefallen (E-Ink-Kompatibilität).
- Beispiel:
?bg=<hex>&fg=<hex>— Überschreibtcustom_bg_colorundcustom_fg_colorbeitheme=custom. Farben müssen als Hex-Werte im Format#RRGGBBangegeben werden.- Beispiel:
http://localhost:8080/?theme=custom&bg=%23001133&fg=%23ffee00setzt einen dunkelblauen Hintergrund mit gelber Schrift (URL-kodiert:#als%23). - Ungültige Hex-Werte führen dazu, dass
theme=customauflightzurückfällt.
- Beispiel:
?layout=<layout>— Überschreibtlayoutausconfig.ini. Erlaubte Werte:highres,tablet,eink,custom.- Beispiel:
http://localhost:8080/?layout=einkwechselt zum E-Ink-Layout.
- Beispiel:
?width=<pixel>&height=<pixel>— Überschreibtcustom_widthundcustom_heightbeilayout=custom. Fehlen die URL-Parameter, wird auf die Werte ausconfig.inizurückgegriffen.- Beispiel:
http://localhost:8080/?layout=custom&width=1000&height=700setzt eine benutzerdefinierte Auflösung.
- Beispiel:
?title=1|0(bzw.yes/no,true/false,on/off, case-insensitive) — Überschreibtshow_titleausconfig.inifür den aktuellen Aufruf.- Beispiel:
http://localhost:8080/?title=0versteckt den Seitentitel für diesen Aufruf.
- Beispiel:
?clock=1|0(dieselben akzeptierten Werte wie?title=) — Überschreibtshow_clockausconfig.inifür den aktuellen Aufruf.- Beispiel:
http://localhost:8080/?clock=0versteckt die Uhrzeit für diesen Aufruf.
- Beispiel:
Ungültige oder unbekannte Werte in URL-Parametern werden ignoriert; die App fällt dann auf die config.ini-Werte zurück.
Kombiniertes Beispiel:
http://localhost:8080/?theme=dark&layout=eink&title=0&clock=1
Dies aktiviert Dark Mode, das E-Ink-Layout, versteckt den Titel und zeigt die Uhr für einen Aufruf, unabhängig von den Werten in config.ini.
Beispiel mit benutzerdefinierten Farben:
http://localhost:8080/?theme=custom&bg=%23001133&fg=%23ffee00&layout=tablet
Dies verwendet ein benutzerdefiniertes Farbschema (dunkelblaue Hintergrund, gelbe Schrift) im Tablet-Layout.
Starten
python app.py
Die Anwendung startet auf dem in config.ini konfigurierten Port (Standard: 8080).
Öffne im Browser: http://localhost:8080
Features
- Nur Straßenbahnen: Busse und andere Verkehrsmittel werden gefiltert.
- Live-Daten: Direkt von der DVB/VVO API.
- Auto-Refresh: Seite aktualisiert sich automatisch (konfigurierbar).
- Fehlertoleranz: Falls eine Haltestelle nicht abrufbar ist, wird die Fehlermeldung angezeigt, die App funktioniert aber weiter.
- E-Ink-optimiert: Minimalistisches Schwarz-Weiß-Design, große lesbare Schrift.
Dateien
app.py— Flask-Hauptanwendungdvb_api.py— DVB/VVO API-Zugrifflist_stops.py— Hilfskript zur Haltestellensucheconfig.ini— Konfigurationsdateitemplates/index.html— HTML-Templaterequirements.txt— Python-Abhängigkeiten
Docker
Diese Anwendung kann auch als Docker-Container ausgeführt werden.
Image bauen
docker build -t haltestellen-info .
Container starten
Starte den Container mit der eigenen config.ini als Volume gemountet, sodass Haltestellen und Einstellungen ohne Neubau des Images geändert werden können:
docker run --rm -d -p 8080:8080 --name haltestellen-info -v "$(pwd)/config.ini:/app/config.ini:ro" haltestellen-info
Hinweis: Wird der Port in config.ini geändert (z.B. port = 9090), muss das Docker-Port-Mapping entsprechend angepasst werden:
docker run --rm -d -p 9090:9090 --name haltestellen-info -v "$(pwd)/config.ini:/app/config.ini:ro" haltestellen-info
Im Browser kann die Anwendung dann unter http://localhost:8080 (oder dem gewählten Port) aufgerufen werden.
Docker Compose
Alternativ kann die Anwendung mit Docker Compose gestartet werden, was Build und Start in einem Schritt kombiniert und automatisch die config.ini als Volume mountet.
Starten (Image wird automatisch gebaut, läuft im Hintergrund):
docker compose up -d --build
Logs ansehen:
docker compose logs -f
Stoppen:
docker compose down
Hinweis: Wird der Port in config.ini geändert (z.B. port = 9090), muss das Port-Mapping in docker-compose.yml entsprechend angepasst werden ("9090:9090" statt "8080:8080").
Richtungsfilter nutzen
Das Hilfsskript list_stops.py hat einen speziellen Modus, um die verfügbaren Richtungen für eine Haltestelle anzuzeigen:
python list_stops.py --richtungen <exakter Haltestellenname>
Beispiel:
python list_stops.py --richtungen Postplatz
Die Ausgabe zeigt eine Tabelle mit allen aktuell verfügbaren Linien und deren Richtungen an dieser Haltestelle. Du kannst diese Informationen dann im [richtungen]-Abschnitt von config.ini eintragen.
list_stops.py im Container
Das Hilfsskript list_stops.py zur Haltestellensuche ist im Docker-Image enthalten und kann per docker exec im laufenden Container ausgeführt werden:
Mit Docker Compose (Service heißt app):
docker compose exec app python3 list_stops.py <Suchbegriff>
Mit manuellem docker run (Container muss mit --name gestartet worden sein):
docker exec haltestellen-info python3 list_stops.py <Suchbegriff>
Beispiel:
docker exec haltestellen-info python3 list_stops.py Postplatz
Der gefundene exakte Haltestellenname kann dann in die lokale config.ini (auf dem Host) eingetragen werden. Diese ist read-only in den Container gemountet; Änderungen wirken sich sofort beim nächsten Laden der Webseite im Browser aus — ein Neustart des Containers ist nicht nötig.
Logging
Bei jedem Aufruf der Startseite werden auf der Server-Konsole folgende Informationen geloggt:
- Client-IP — IP-Adresse des Clients
- User-Agent — Identifizierung des Browsers/Clients
- Layout und Auflösung — Das verwendete Layout (
highres,tablet,eink, odercustom) und die konfigurierte Auflösung
Beispiel einer Logzeile:
[Aufruf] IP=192.168.1.100 User-Agent=Mozilla/5.0... Layout=eink (800x480)
Zusätzlich meldet ein kleines, optionales JavaScript-Snippet die tatsächliche Browser-Fenstergröße an einen separaten Endpunkt /log-resolution (progressive enhancement — die Seite funktioniert auch ohne JavaScript). Diese Informationen landen ebenfalls in den Logs:
[Client-Auflösung] IP=192.168.1.100 Fenstergröße=1024x768
Die Logs können je nach Betriebsumgebung angezeigt werden:
- Lokale Ausführung: direkt auf der Konsole (wo
app.pygestartet wurde) - Docker: über
docker compose logs -f(bei Compose) oderdocker logs <container-name>(bei manuellen Containern)
Lizenz
Frei verwendbar.