Zum Inhalt springen
Alles Automatisch

Dashboard

Bus & Bahn in Home Assistant: Abfahrten im Dashboard

Für alle ab
Für Mitglieder freigeschaltet

Früher Zugang für Kanalmitglieder

Das Video läuft schon, nur noch nicht für alle.

Kanalmitglieder sehen es jetzt. Für alle erscheint es am Sonntag, 27. September, um 9 Uhr. Die komplette Anleitung kannst du heute schon lesen, sie steht direkt darunter.

Mitglieder seit 21. Sep.Für alle am So., 27. Sep.
morgen um 9 Uhr

Ich weiß nicht, wie oft ich schon knapp die S-Bahn verpasst habe, weil ich zu spät auf die Uhr geschaut hab. Seit ich die nächsten Abfahrten direkt auf dem Home-Assistant-Dashboard stehen habe, passiert mir das deutlich seltener. Kein Account, kein API-Key, keine dritte App. Es reicht eine HACS-Integration, die an fast jeder Haltestelle in Deutschland funktioniert und dabei auch noch über 60 andere Länder abdeckt. Ich zeige dir Schritt für Schritt, wie du die Fahrplandaten in Home Assistant holst, daraus eine Abfahrtstafel baust und dir eine Benachrichtigung schickst, bevor du losmüsstest.

Was du brauchst

Bevor wir anfangen, kurz die Voraussetzungen:

  • Home Assistant 2026.9 oder neuer, mit Zugriff auf die Einstellungen
  • HACS, installiert und eingerichtet
  • Die Integration Public Transport Departures, die Fahrplandaten über Transitous holt
  • Optional die Departures Card als fertige Dashboard-Karte
  • Die Home-Assistant-App auf dem Handy, wenn du die Benachrichtigung nutzen willst

Kein API-Schlüssel, keine Registrierung, kein Konto. Das ist eigentlich das Schönste daran.

Schritt 1: Integration über HACS installieren

Du öffnest HACS und suchst nach Public Transport Departures. Die Integration ist direkt im Standard-Store verfügbar, du musst kein Custom Repository hinzufügen. Einfach herunterladen, dann Home Assistant neu starten.

Danach gehst du auf Einstellungen, dann Geräte & Dienste, klickst auf Integration hinzufügen und suchst wieder nach Public Transport Departures. Die Integration taucht dort auf, sobald Home Assistant nach dem Neustart wieder läuft.

Schritt 2: Haltestelle über den Umkreis finden

Der Einrichtungsdialog führt dich durch vier Seiten, und die sind eigentlich selbsterklärend.

Seite 1, Ort und Radius. Du trägst eine Adresse oder Koordinaten ein und wählst einen Radius in Metern. Die Integration listet dann alle Haltestellen auf, die sie in diesem Umkreis findet. Ich empfehle, mit 800 Metern anzufangen. 3 km ist ein bisschen viel, wenn du gezielt eine Haltestelle willst.

Seite 2, Haltestelle auswählen. Du wählst eine aus der Liste. Für jede weitere Haltestelle fügst du einen neuen Eintrag derselben Integration hinzu.

Seite 3, Verbindungen auswählen. Jetzt siehst du alle Linien, die an dieser Haltestelle halten, je Richtung getrennt. Du wählst nur die aus, die dich tatsächlich betreffen. Die Linie zum Bahnhof und die Rückrichtung nach Hause zum Beispiel.

Seite 4, Name vergeben. Dieser Name wird Teil jeder Entity-ID. Also lieber kurz und eindeutig halten. Aus einem langen Haltestellennamen wie Lückstr./Weitlingstr. (Berlin) und der Linie 240 in Richtung S Ostbahnhof entsteht dann sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof. Je kürzer der Name, desto handlicher die ID im YAML danach.

Für jede ausgewählte Verbindung entsteht ein Sensor. Sein Zustand ist die nächste Abfahrt als Zeitpunkt. Die folgenden Fahrten stehen im Attribut times, jede davon mit diesen Feldern:

AttributBeschreibung
plannedPlanmäßige Abfahrtszeit
estimatedVoraussichtliche Abfahrtszeit (Echtzeit)
cancelledOb die Fahrt ausfällt
head_signText auf dem Zielanzeiger des Fahrzeugs
trip_idInterne Fahrt-ID
alertsServicemeldungen zu dieser Fahrt

Dazu kommen auf Sensor-Ebene noch line_name, direction, transport, data_provider sowie latitude und longitude der Haltestelle.

Bestehende Haltestellen lassen sich jederzeit über das Zahnrad in der Integrationsübersicht anpassen. Linien nachträglich hinzufügen oder entfernen geht dort problemlos.

Schritt 3: Abfahrtstafel mit der fertigen Departures Card

Die schnellste Variante ist die Departures Card. Sie ist als Custom Repository verfügbar und kommt nicht über den HACS-Standard-Store:

  1. HACS öffnen, drei Punkte oben rechts, Benutzerdefinierte Repositories wählen.
  2. https://github.com/alex-jung/ha-departures-card eintragen, Typ Dashboard auswählen.
  3. Departures Card herunterladen, danach die Seite neu laden.
  4. Im Dashboard Karte hinzufügen wählen, nach Departures suchen.

Die Karte bringt neun eingebaute Themes mit, zeigt Verspätungen, Ausfälle und Echtzeit-Updates direkt an, und beim langen Drücken auf eine Abfahrt öffnet sich sogar eine interaktive Karte mit der Fahrzeugposition und dem Streckenverlauf. Das ist wahnsinnig praktisch, wenn man wissen will, ob der Bus gerade wirklich schon weg ist.

Ein Beispiel für die Konfiguration:

yaml
1type: custom:departures-card
2title: Haltestelle Lückstr./Weitlingstr.
3cardOrientation: vertical
4departuresToShow: 4
5sortDepartures: true
6theme: Basic
7layout:
8  - icon
9  - line
10  - destination
11  - time-diff
12  - delay
13entities:
14  - sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof
15  - sensor.luckstr_weitlingstr_berlin_240_s_storkower_str

Die Entity-IDs sind meine. Deine findest du unter Einstellungen, Geräte & Dienste, Eintrag der Integration, oder über Einstellungen, Werkzeuge, Reiter Zustände.

Schritt 4: Die Tafel ohne Custom Card

Wer keine zusätzliche Karte installieren will, baut die Abfahrtstafel mit der eingebauten Markdown-Karte. Das Template holt sich beide Sensoren, mischt alle Abfahrten in eine gemeinsame Liste, sortiert sie nach Abfahrtszeit und rechnet die Verspätung selbst aus, indem es estimated gegen planned hält.

Wichtig dabei: Der YAML-Blockstil ist |-, nicht >-. Mit >- faltet YAML alle Zeilen zu einer einzigen zusammen, und dann zerfällt die Tabelle.

yaml
1type: markdown
2title: Nächste Abfahrten
3content: |-
4  {%- set sensoren = [
5    'sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof',
6    'sensor.luckstr_weitlingstr_berlin_240_s_storkower_str'
7  ] %}
8  {%- set ns = namespace(rows=[]) %}
9  {%- for s in sensoren %}
10    {%- for t in state_attr(s, 'times') or [] %}
11      {%- set ab = as_datetime(t.estimated or t.planned) %}
12      {%- if ab > now() %}
13        {%- set ns.rows = ns.rows + [{'ab': ab, 'plan': as_datetime(t.planned),
14          'linie': state_attr(s, 'line_name'), 'ziel': state_attr(s, 'direction'),
15          'aus': t.cancelled}] %}
16      {%- endif %}
17    {%- endfor %}
18  {%- endfor %}
19  {%- if ns.rows | count == 0 %}
20  Keine Abfahrten in den nächsten Stunden.
21  {%- else %}
22  |  |  |  |
23  |:--|:--|--:|
24  {% for r in (ns.rows | sort(attribute='ab'))[:8] -%}
25  {%- set rest = ((r.ab - now()).total_seconds() / 60) | round(0, 'floor') | int -%}
26  {%- set spaet = ((r.ab - r.plan).total_seconds() / 60) | round(0) | int -%}
27  {%- set uhr = r.ab | as_timestamp | timestamp_custom('%H:%M') -%}
28  {%- set zusatz = ' *fällt aus*' if r.aus else (' *+' ~ spaet ~ '*' if spaet > 0 else '') -%}
29  {%- set wann = 'jetzt' if rest < 1 else 'in ' ~ rest ~ ' min' -%}
30  | **{{ r.linie }}** | {{ r.ziel }}{{ zusatz }} | **{{ wann }}** · {{ uhr }} |
31  {% endfor %}
32  {%- endif %}

Einen Hinweis zur Uhrzeit: strftime gibt es in Home Assistant nicht als Template-Filter. Die Uhrzeit kommt über | as_timestamp | timestamp_custom('%H:%M'). {{ ab | strftime('%H:%M') }} sieht auf den ersten Blick richtig aus, ist aber kein Filter, den Home Assistant kennt. Die Vorlage bricht dann ab und die Karte bleibt leer.

Weil now() im Template vorkommt, rechnet die Karte jede Minute neu. Der Countdown läuft also weiter, auch wenn die Integration gerade nichts Neues nachgeladen hat.

Schritt 5: Aus der Liste wird eine echte Abfahrtstafel

Mit card-mod bekommt dieselbe Markdown-Karte den Anstrich einer echten Anzeigetafel: dunkler Hintergrund, Linie in Türkis, Countdown in Weiß, Verspätung in Rot. card-mod lässt sich direkt über den HACS-Standard-Store installieren.

Das $ hinter ha-markdown im Style-Block ist wichtig. Es schiebt das CSS in den Shadow-DOM der Karte. Ohne es erreicht das Styling die Tabelle nicht.

yaml
1card_mod:
2  style:
3    .: |
4      ha-card {
5        background: linear-gradient(155deg, #1d2942 0%, #10172B 100%) !important;
6        border: none !important;
7        border-radius: 16px !important;
8        box-shadow: 0 6px 22px rgba(16,23,43,0.28) !important;
9        color: #e8eefc !important;
10        padding: 6px 10px !important;
11      }
12    ha-markdown$: |
13      thead { display: none !important; }
14      table { width: 100% !important; border-collapse: collapse !important; }
15      td { border: none !important;
16           border-bottom: 1px solid rgba(232,238,252,0.10) !important;
17           padding: 11px 6px !important; font-size: 15px !important;
18           font-variant-numeric: tabular-nums; }
19      tr:last-child td { border-bottom: none !important; }
20      td:first-child { width: 54px !important; color: #89c9ba !important; }
21      td:last-child { white-space: nowrap !important;
22                      color: rgba(232,238,252,0.62) !important; }
23      td:last-child strong { color: #ffffff !important; font-weight: 700 !important; }
24      em { color: #f0665b !important; font-style: normal !important; font-weight: 700 !important; }

Der Markdown-Block braucht eine Kopfzeile mit drei leeren Spalten (| | | |), weil Markdown sonst keine Tabelle erkennt. thead { display: none } im CSS nimmt diese Zeile dann wieder weg, sodass sie im fertigen Dashboard nicht sichtbar ist.

Schritt 6: Benachrichtigung, bevor du losmusst

Der Sensor der Integration trägt keine Geräteklasse timestamp. Deshalb funktioniert der bequeme Zeit-Trigger mit entity_id und offset hier nicht, und es braucht einen Template-Trigger. Der rechnet sich bei jedem Minutenwechsel neu, weil now() darin vorkommt.

yaml
1alias: Bus 240 zum Ostbahnhof, 10 Minuten vorher
2description: Meldet die nächste Abfahrt, solange ich zu Hause bin
3triggers:
4  - trigger: template
5    value_template: >-
6      {% set ab = states('sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof') %}
7      {% if ab in ['unknown', 'unavailable', 'none', ''] %}
8        false
9      {% else %}
10        {% set rest = (ab | as_timestamp) - (now() | as_timestamp) %}
11        {{ 0 < rest <= 600 }}
12      {% endif %}
13conditions:
14  - condition: state
15    entity_id: person.daniel_boberg
16    state: home
17  - condition: time
18    weekday:
19      - mon
20      - tue
21      - wed
22      - thu
23      - fri
24actions:
25  - action: notify.send_message
26    target:
27      entity_id: notify.jakku
28    data:
29      message: >-
30        Bus {{ state_attr('sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof', 'line_name') }} nach
31        {{ state_attr('sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof', 'direction') }} fährt um
32        {{ states('sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof') | as_timestamp | timestamp_custom('%H:%M') }} Uhr.
33mode: single

notify.jakku ist mein iPhone. Deine Entität heißt nach deinem Gerät. Du findest sie unter Einstellungen, Werkzeuge, Reiter Zustände, mit dem Filter notify.. Die Entity-ID von person.daniel_boberg tauschst du gegen deine eigene aus, wenn du die Prüfung auf „zu Hause" behalten willst. Die Wochentage in den Conditions passen du auf deine Situation an oder lässt sie ganz weg.

Du kannst die Automation auch komplett ohne YAML im Automationseditor zusammenklicken. Den YAML-Code hier nehme ich als Abkürzung, damit du den Aufbau direkt nachbauen kannst.

Schritt 7: Standortabhängig die richtige Tafel zeigen

Der häufigste Wunsch dazu: Home Assistant soll selbst erkennen, wo man ist, und automatisch die passende Haltestelle anzeigen. Das kann die Integration nicht. Sie fragt feste Haltestellen ab, die du einmal einrichtest.

Was geht, ist die Umkehrung. Du richtest deine zwei oder drei Haltestellen ein und blendest je nach Aufenthaltsort die passende Karte ein. Das läuft über eine Conditional Card:

yaml
1type: conditional
2conditions:
3  - condition: state
4    entity: person.daniel_boberg
5    state: home
6card:
7  type: custom:departures-card
8  title: Von zu Hause zum Bahnhof
9  entities:
10    - sensor.luckstr_weitlingstr_berlin_240_s_ostbahnhof

Die zweite Karte bekommt dieselbe Bedingung mit state_not: home und zeigt die Rückrichtung. Dasselbe funktioniert mit jeder Zone, die du angelegt hast, also auch mit Arbeit. So hast du mit relativ wenig Aufwand eine standortabhängige Abfahrtstafel, die sich selbst umschaltet.

Tipps und häufige Probleme

Die Karte bleibt leer. Erst prüfen, ob der Sensor überhaupt einen Wert hat: Einstellungen, Werkzeuge, Reiter Zustände, Entity suchen. Steht dort unknown, liefert die Datenquelle für diese Linie gerade nichts. Das passiert nachts, wenn keine Fahrten mehr geplant sind.

Weiter hinten in der Liste fehlt die Echtzeit. Die Integration hat einen Zeithorizont: Echtzeit gibt es nur für die nächste halbe Stunde. An der Haltestelle Lückstr./Weitlingstr. in Berlin-Lichtenberg trugen von zwölf abgefragten Abfahrten die ersten sieben ein Echtzeit-Kennzeichen, die fünf danach nicht mehr. Die Grenze lag bei 35 Minuten. Eine Karte mit vier Einträgen zeigt deshalb mehr Echtzeit als eine mit zwölf.

Ob dein Verbund überhaupt Echtzeit liefert, siehst du an der Abweichung zwischen planned und estimated. Am selben Tag lieferte die Haltestelle Prüm, Bahnhof in der Eifel zehn von zwölf Abfahrten mit Echtzeit, der Bahnhof Berlin-Lichtenberg sechzehn von sechzehn. Das ist also keine Frage von Stadt oder Land.

Verspätung steht nirgends als eigenes Feld. Doch, aber indirekt: estimated minus planned ist die Verspätung. Sind beide gleich, ist die Fahrt entweder pünktlich oder sie liegt jenseits des Echtzeit-Zeithorizonts.

Für die Deutsche Bahn gibt es noch DB Infoscreen. Die Integration liest die Ist-Verspätung der Bahn samt Gleis. Sie steht hier bewusst nicht im Ablauf, weil die öffentliche Instanz nur eine Anfrage pro Bahnhof und Minute erlaubt und auf 30 Sensoren begrenzt ist. Wer sie ernsthaft nutzt, hostet sie selbst. Das ist allerdings ein eigenes Thema.

Es gibt noch zwei weitere Wege. Die eingebaute Core-Integration GTFS läuft ohne HACS, braucht aber eine GTFS-Datei von Hand und kann laut eigener Doku keine Echtzeit. Für Hamburg gibt es HVV Abfahrten und für Frankfurt RMV direkt in Home Assistant, jeweils ohne HACS.

Nutzt du die Benachrichtigung, die standortabhängige Tafel oder etwas ganz anderes damit? Ich bin neugierig, was ihr daraus gebaut habt.

Diskussion im Forum

Hast du Fragen oder Ideen zu diesem Thema?

Diskutiere im Forum

Video teilen

Community

Unterstütze den Kanal

Hat dir der Beitrag geholfen? Es gibt zwei Wege, etwas zurückzugeben.