Not needed any more -- the Journey screen already covers the same
regional-transit need via EFA backends, and Departures depended on
the free db-vendo-client API that DB has been actively blocking third
parties from anyway.
Removed screens/departures.py, its settings template, its route, its
registration in screens/__init__.py, and its nav link. Reworded the
docstring/hint references in journey.py and settings_journey.html
that pointed at the now-gone screen. Existing config.json entries
referencing "departures" (rotation list, pinned screen) are harmless
leftovers -- both scheduler lookups already filter to screens that
still exist, so nothing needs a migration step.
Verified: app boots clean, /settings/departures 404s, every other
settings page still loads fine, no leftover references outside of
journey.py's own unrelated "departure time" wording.
Replaces the generic "1x umsteigen" summary with the real gap per
transfer (e.g. "SchnellBus S20 ->13min-> Regionalzug RB 67"), computed
from each leg's estimated (falling back to planned) arrival/departure.
Falls back to a plain "umsteigen" label when EFA doesn't supply both
timestamps for a given transfer.
Vendor Font Awesome Free Solid (fonts/fa-solid-900.ttf, SIL OFL
licensed -- see fonts/FONT-AWESOME-LICENSE.txt) for its actual
train/bus glyphs. Confirmed live these stay sharp and recognizable
even at the tiny size the detail line renders at (~20px), unlike a
hand-drawn shape would be, and unlike emoji (not in DejaVu Sans,
would've rendered as tofu).
render_utils.py gets a load_icon_font() alongside the existing
load_font(), plus named ICON_BUS/ICON_TRAIN codepoint constants so
screens don't need to embed raw Private-Use-Area unicode literals.
Plain colored dots weren't self-explanatory. Each leg badge now shows
a "Z" (Zug) or "B" (Bus) in white inside the blue/green circle --
confirmed legible at actual render size via zoomed crop inspection.
- Origin/destination now on their own lines instead of one truncated
"A -> B" line, which cut off the destination entirely on the small
panel.
- Each leg gets a colored dot (blue = train, green = bus), using the
panel's exact palette colors so they render solid, not dithered.
Mode is read from EFA's product.name (confirmed live: "StadtBus"/
"RegioBus"/"SchnellBus" vs "S-Bahn"/"Regionalzug"/"Zug").
- Detail-line text switched from dithered gray to solid black -- the
gray was hard to read at the small font size the panel renders at.
- connection_type setting (all/regional/long_distance) restricts EFA
trip requests via inclMOT_N params, so ICE/IC/EC can be filtered out
for commutes that only care about regional/local transit.
- Surface leg.infos[].infoLinks titles (real disruption/outage notices
from EFA) on each journey row, marked with a red warning icon.
Open-Meteo's geocoder is built for city-level weather lookups -- it knows
"Münster" but has no idea what "Münster Hauptbahnhof" means, so any
specific station name failed to resolve. Reused it from Weather without
checking it actually fit this use case.
The db_info reference project sidesteps this entirely by requiring the
user to pick existing Home Assistant person/zone entities, which already
carry precise coordinates from HA's own location system -- no equivalent
here, so this needed real geocoding.
Switched geocode() to query the same EFA instances' XML_STOPFINDER_REQUEST
endpoint instead -- the same system that answers the trip request, so
location resolution is guaranteed consistent with what trip planning
understands. Verified live: correctly resolves specific stations ("Münster
Hauptbahnhof" -> "Münster (Westf), Hbf", even the "Muenster" ue-spelling
the user actually typed), plain city names (falls back to the main
station), and district/neighborhood names (e.g. "Hiltrup" -> the actual
Hiltrup stop, not just the vague suburb entry) -- all via matchQuality
scoring and the API's own isBest flag. Re-verified the full pipeline
end-to-end through the real Flask route afterward.
New screens/journey.py, ported and simplified from github.com/EiS94/db_info
(a Home Assistant integration). Deliberately uses only the regional EFA
(Elektronische Fahrplanauskunft) backends the reference project queries
(VRR prioritized for NRW routes, falling back to Bavaria/BW instances) and
drops its bahn.de-internal-API source entirely -- that's DB's own
infrastructure, exactly the kind of thing DB has been actively blocking
(see the Departures screen). EFA instances are run by regional transit
authorities, not DB, so they aren't part of that clampdown. Also dropped
the reference project's parallel async fetch-and-score-all-sources
machinery for simple sequential fallback, matching this project's fully
synchronous style.
Verified against live VRR data for a real NRW route (Münster -> Dortmund):
real trains, platforms, transfer counts, even a night bus connection.
Caught a real bug during that live test: EFA returns UTC timestamps (Z
suffix), and datetime.fromisoformat() parses that correctly into an
aware datetime, but strftime() on an aware datetime prints its own
stored hour/minute rather than converting to local time first --
everything displayed 2 hours behind (CEST is UTC+2), which looked like
"next" departures had already left. Fixed by converting with
astimezone() before formatting; re-verified live afterward that
departures now correctly show after the query time.
Settings: origin/destination resolved via Open-Meteo's free geocoding
(same pattern as Weather's city field), how many connections to show
(1/2/3/5), and its own configurable update interval.