Externe Integration - Bahn,Busverkehr

Hallo Miteinander,

ich habe mich ja breitschlagen lassen, nachdem ich hier die die guten Hilfestellungen von @prohand und @guim31 gelesen habe.

Gesagt getan. Mittels eines Dockers konnte ich die Verkehrsdaten für deutsche Haltestellen finden, dann einfach mal gesucht mit Städten in der Schweiz ( geht ), Städten in Österreich ( geht ) und dann bin ich ganz frech geworden, ich habe eine französische Stadt an der Atlantikküste gesucht, ganz weit weg vom deutschen Sprachraum… und siehe da: geht auch.

Mein entwickeltes Script geht einen speziellen Weg. Über die Geolocation des Anwenders bei seinem ZuHause in Gladys Assistent habe ich die Geodaten.
Mittels dieser Geodaten wird eine Umkreissuche gestartet, zuerst wird die näheste Bushaltestelle, dann die nächste TRAM und zum Schluss der näheste Bahnhof gefunden.

Im nächsten Schritt werden dann die zeitlich folgenden fünf Abfahrten direkt angezeigt.

So weiß der Anwender bei jedem Aufruf von Gladys Assistant dann sofort, was fährt wann wo als nächstes ab.

Hier mal die Testausgabe von La Rochelle:

[/share/Container] # /share/Container/abfahrten-ortstest.sh 46.1540 -1.1450
Suche Haltestellen rund um 46.1540, -1.1450 ...
Auswahl: BUS - Gare La Rochelle - De Gaulle (ca. 120 m)
Keine Haltestelle fuer TRAM gefunden.
Auswahl: RAIL - La Rochelle (ca. 156 m)
Keine Haltestelle fuer FERRY gefunden.

Rufe Abfahrten fuer die ausgewaehlten Stop-IDs ab ...


DIE NAECHSTEN 5 ABFAHRTEN INSGESAMT
Zeit   | Haltestelle                   | Art   | Linie    | Ziel                          | Daten     | Steig

2026-10-12T19:57:00Z | Gare La Rochelle - De Gaulle  | BUS   | 3        | Lagord - P+R Les Greffières  | Fahrplan  | -
2026-10-12T20:02:00Z | Gare La Rochelle - De Gaulle  | BUS   | 3        | Cottes Mailles - P+R Simone Veil | Fahrplan  | -
2026-10-12T20:03:00Z | La Rochelle                   | BAHN  | TER 864767 |                               | Fahrplan  | -
2026-10-13T03:28:00Z | La Rochelle                   | BAHN  | TER 864801 |                               | Fahrplan  | -
2026-10-13T03:36:00Z | Gare La Rochelle - De Gaulle  | BUS   | 3        | Lagord - P+R Les Greffières  | Fahrplan  | -

TRAM* bedeutet: Transitous meldete OTHER an einer ausgewaehlten Tram-Stop-ID.
Die angezeigten 5 Eintraege sind auf die abgefragten Stop-IDs begrenzt.
Sonderhaltestellen ohne Abfahrten in der ersten API-Suche koennen fehlen.

Jetzt stehe ich aber vor einem Problem:
Ich kann die Logik von französischen, schweizer oder österreichischen Abfahrtsstellen nicht bewerten und suche deshalb Tester.

Es ist eigentlich nur ein Docker zu installieren. YAML liegt vor. Dann mittels SSH das Script starten und die Ergebnisse gegenprüfen und etwaige Hinweise an mich zurückgeben.

Also wer hat Lust?
Einfach melden.

Wenn die Logik stimmt, dann geht es zur Integration.

Grüße
Tom

Hier noch die Testausgaben einfach so zum Mitlesen:

Österreich:

[/share/Container] # /share/Container/abfahrten-ortstest.sh 47.7260 16.2140
Suche Haltestellen rund um 47.7260, 16.2140 ...
Auswahl: BUS - Bad Erlach Gemeindeamt (ca. 182 m)
Keine Haltestelle fuer TRAM gefunden.
Auswahl: RAIL - Bad Erlach Bahnhof (ca. 620 m)
Keine Haltestelle fuer FERRY gefunden.

Rufe Abfahrten fuer die ausgewaehlten Stop-IDs ab ...

DIE NAECHSTEN 5 ABFAHRTEN INSGESAMT
Zeit   | Haltestelle                   | Art   | Linie    | Ziel                          | Daten     | Steig

2026-10-12T19:49:00Z | Bad Erlach Bahnhof            | BAHN  | R 2775   | Friedberg                     | Fahrplan  | -
2026-10-12T20:11:00Z | Bad Erlach Bahnhof            | BAHN  | R 2776   | Wiener Neustadt Hauptbahnhof  | Fahrplan  | -
2026-10-12T20:49:00Z | Bad Erlach Bahnhof            | BAHN  | R 2779   | Friedberg                     | Fahrplan  | -
2026-10-12T21:49:00Z | Bad Erlach Bahnhof            | BAHN  | R 2783   | Aspang Markt Bahnhof          | Fahrplan  | -
2026-10-13T02:13:00Z | Bad Erlach Bahnhof            | BAHN  | R 2700   | Wiener Neustadt Hauptbahnhof  | Fahrplan  | -


Schweiz:

[/share/Container] # /share/Container/abfahrten-ortstest.sh 47.1240 9.3120
Suche Haltestellen rund um 47.1240, 9.3120 ...
Auswahl: BUS - Walenstadt, Rathausplatz (ca. 216 m)
Keine Haltestelle fuer TRAM gefunden.
Auswahl: RAIL - Walenstadt (ca. 476 m)
Keine Haltestelle fuer FERRY gefunden.

Rufe Abfahrten fuer die ausgewaehlten Stop-IDs ab ...



DIE NAECHSTEN 5 ABFAHRTEN INSGESAMT
Zeit   | Haltestelle                   | Art   | Linie    | Ziel                          | Daten     | Steig

2026-10-12T19:44:00Z | Walenstadt                    | BAHN  | IR35     | Zürich HB                    | Fahrplan  | 4
2026-10-12T19:46:00Z | Walenstadt                    | S-BAHN | S17      | Sargans                       | Fahrplan  | 3
2026-10-12T19:50:00Z | Walenstadt, Bahnhof           | BUS   | 444      | Murg, Bahnhof                 | Fahrplan  | A
2026-10-12T20:13:00Z | Walenstadt                    | S-BAHN | S17      | Rapperswil SG                 | Echtzeit  | 4
2026-10-12T20:15:00Z | Walenstadt                    | BAHN  | IR35     | Chur                          | Fahrplan  | 3

oder Spezialfall Hamburg ( Linienverkehr mit Fähre ):

DIE NAECHSTEN 5 ABFAHRTEN INSGESAMT
Zeit   | Haltestelle                   | Art   | Linie    | Ziel                          | Daten     | Steig

2026-10-12T19:09:00Z | Stadthausbrücke              | BAHN  | S1       | Hamburg Airport (Flughafen)   | Echtzeit  | -
2026-10-12T19:10:00Z | Arningstraße                 | FÄHRE | 72       | Landungsbrücken              | Echtzeit  | -
2026-10-12T19:11:00Z | Stadthausbrücke              | BAHN  | S3       | Pinneberg                     | Echtzeit  | -
2026-10-12T19:11:00Z | Stadthausbrücke              | BAHN  | S3       | Neugraben                     | Echtzeit  | -
2026-10-12T19:14:00Z | Stadthausbrücke              | BAHN  | S1       | Hamburg Airport (Flughafen)   | Echtzeit  | -

Ich sage dir direkt im Integrationsteil Bescheid, aber wenn du vorher Ergebnisse brauchst, kein Problem.

Sag mir, wie ich den Test durchführen soll, und ich mache ihn gerne.

Hallo Tom :waving_hand:

Auf nach Frankreich! Ich wohne in der Nähe von Toulouse, also habe ich deine Logik direkt an der Transitous-API getestet: etwa zwanzig Orte (Toulouse und Umgebung, Bordeaux, Paris, Lyon, Marseille, Nantes, Lille, Grenoble, La Rochelle). Gute Nachricht: Die Daten sind gut, die Fahrpläne von Tisséo, SNCF und RER stimmen. Ein paar Punkte zur Logik:

  • Die Suche um den Punkt herum gibt nur 5 Haltestellen aus (wenn du reverse-geocode verwendest). Sobald es fünf Bushaltestellen in der Nähe gibt, verschwinden die Straßenbahn und der Bahnhof: In Marseille Vieux-Port verpasst sie die Straßenbahn und Saint-Charles, in Grenoble und Muret den Bahnhof. Mit map/stops in einer Zone von 1 km erhält man alle Haltestellen und es funktioniert.
  • Ziel: Nimm tripTo.name. Bei der SNCF ist headsign leer, und für den RER enthält es einen Missionscode („ZUMO“, „ERTE“…), nicht das Ziel.
  • Die U-Bahn fehlt: Füge SUBWAY (und AERIAL_LIFT für die Seilbahn von Toulouse :grinning_face_with_smiling_eyes:) hinzu.
  • Stornierungen: Die API markiert die stornierten Fahrten (cancelled). In Toulouse heute Abend waren 3 von 11 Straßenbahnen storniert, und deine Tabelle würde sie normal anzeigen.
  • Verspätungen: departure gegen scheduledDeparture, damit kann man „+5 min“ anzeigen.
  • Zeiten in UTC: Jede Haltestelle hat ihre Zeitzone (tz), die in die lokale Zeit umgewandelt werden muss.
  • Geisterhaltestellen ohne Verkehrsmittel: Diese sollten ignoriert werden.
  • Top 5 global: In der Stadt nimmt die U-Bahn die 5 Plätze ein. N Abfahrten pro Verkehrsmittel wäre besser.
  • Die nächstgelegene Haltestelle ist nicht immer die richtige: In Capitole ist es eine Haltestelle, die nur von einem Shuttle bedient wird (nächste Abfahrt morgen früh). Dem Nutzer die Wahl seiner Haltestellen zu lassen, mit automatischer Suche als Vorschlag, wäre super.

Kleine ehrliche Reserve: Die SNCF hat in Transitous keine Echtzeitdaten, also sieht man an Streiktagen Züge, die nicht fahren :sweat_smile:

Wenn du dein Skript teilst, führe ich es an denselben Orten aus, um zu vergleichen :folded_hands:

Zu 1: Habe ich berücksichtig, sprich es wird die erste näheste Haltestelle genommen. Also die näheste Bushaltestelle, dann wird weitergesucht bis zur ersten nähesten TRAM und dann weiter bis zum ersten nähesten Bahnhof.
Sprich: Wenn in der Suche fünf Bushaltestellen sind und nur ein Bahnhof dann liefert das Ergebnis eine Bushaltestelle und ein Bahnhof.
Zu 2: Ziel … das werde ich übernehmen, so eine Seilbahn ist doch schon ein extremes Beispiel, aber sehr gerne.
Zu 3: SUBWAY ist gut, übernehme ich.

Zu 4: Stornierungen… ja, dass habe ich im Hinterkopf, aber heute war ich noch mit dem Grundaufbau des Scripts beschäftigt. Das kommt gleich zum Gegenlesen.

Zu 5: Geisterhaltestellen : Das scheint eine französische Spezialität zu sein, in Deutschland gibt es ein zentrales Haltestellenverzeichnis ( wußte ich bis heute auch nicht ), da tragen alle Verkehrsbetriebe ihre bedienten Haltestellen ein.

Zu 6: Guter Hinweis, aktuell bin ich noch am richtigen Timing. Ich werde wahrscheinlich im Konfigurationsbereich der Integration dem Anwender die Auswahl überlassen: Was will er sehen ( Bus, Bahn, Tram, Fähre, Flugzeug ) und den Suchbereich konfigurierbar machen.

Hier mal mein Script zum heutigen Stand:
Das Script läuft aber gegen meinen Docker in welchem die Api drinsteckt.

Hier mal mein Script, vielleicht fällt dir noch etwas auf, ich gehe jezt mal nach 10 Stunden ne Pina Colada trinken:

#!/bin/sh

Nahe Haltestellen finden und die naechsten Abfahrten zusammenfuehren.

Aufruf: abfahrten-ortstest.sh BREITENGRAD LAENGENGRAD [RADIUS_METER]

set -eu

API="https://api.transitous.org/api/v6/stoptimes"
UA=""

if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
echo "Aufruf: $0 BREITENGRAD LAENGENGRAD [RADIUS_METER]" >&2
exit 2
fi

LAT=$1
LON=$2
RADIUS=${3:-1200}

for V in "$LAT" "$LON" "$RADIUS"; do
case "$V" in
''|[!0-9.-]|.|-|..)
echo "Ungueltiger Zahlenwert: $V" >&2
exit 2
;;
esac
done

TMP="/tmp/abfahrten-ortstest.$$"
RAW="${TMP}.json"
ROWS="${TMP}.tsv"
QUERIES="${TMP}.queries"
RESP="${TMP}.responses"
OUT="${TMP}.out"
: > "$QUERIES"
: > "$RESP"
: > "$OUT"
trap 'rm -f "$RAW" "$ROWS" "$QUERIES" "$RESP" "$OUT" "${TMP}.one"' EXIT HUP INT TERM

echo "Suche Haltestellen rund um ${LAT}, ${LON} ..."

if ! curl -fsS --get "$API" 
--data-urlencode "center=${LAT},${LON}" 
--data-urlencode "radius=${RADIUS}" 
--data-urlencode "n=100" 
--data-urlencode "language=de" 
-H "User-Agent: $UA" -o "$RAW"; then
echo "Fehler bei der Haltestellensuche." >&2
exit 1
fi

if ! jq -e '.stopTimes | type == "array"' "$RAW" >/dev/null 2>&1; then
echo "Unerwartete API-Antwort:"
jq . "$RAW" 2>/dev/null || cat "$RAW"
exit 1
fi

Distanznaeherung fuer die Sortierung naher Haltestellen.

jq -r --arg lat "$LAT" --arg lon "$LON" '
def cat:
if . == "BUS" then "BUS"
elif . == "FERRY" then "FERRY"
elif . == "TRAM" then "TRAM"
elif . == "SUBURBAN" or . == "REGIONAL_RAIL" then "RAIL"
else "OTHER:" + tostring
end;
def dist:
(((.place.lat|tonumber)-($lat|tonumber))111000) as $dy
| (((.place.lon|tonumber)-($lon|tonumber))72000) as $dx
| (($dx$dx+$dy$dy)|sqrt);
[
.stopTimes

| select(.place.name != null and .place.stopId != null)
| select(.place.lat != null and .place.lon != null)
| {
name:.place.name,
id:.place.stopId,
mode:(.mode // "UNKNOWN"),
category:((.mode // "UNKNOWN")|cat),
distance:dist
}
]
| unique_by([.name,.id,.mode])
| group_by([.name,.category])
| map({
name:.[0].name,
category:.[0].category,
distance:(map(.distance)|min),
modes:(map(.mode)|unique|join(",")),
ids:(map(.id)|unique)
})
| sort_by(.distance)
| .

| [.category,(.distance|floor|tostring),.name,.modes,(.ids|join("|"))]
| @tsv
' "$RAW" > "$ROWS"

if [ ! -s "$ROWS" ]; then
echo "Keine Haltestellen gefunden. Radius vergroessern?"
exit 0
fi

Die jeweils naechste Gruppe pro Kategorie waehlen.

for CAT in BUS TRAM RAIL FERRY; do
LINE=$(awk -F '\t' -v cat="$CAT" '$1 == cat {print; exit}' "$ROWS")
if [ -z "$LINE" ]; then
echo "Keine Haltestelle fuer $CAT gefunden."
continue
fi

NAME=$(printf '%s\n' "$LINE" | cut -f3)
DIST=$(printf '%s\n' "$LINE" | cut -f2)
IDS=$(printf '%s\n' "$LINE" | cut -f5)

echo "Auswahl: $CAT - $NAME (ca. ${DIST} m)"

OLD_IFS=$IFS
IFS='|'
for SID in $IDS; do
    printf '%s\t%s\t%s\n' "$CAT" "$NAME" "$SID" >> "$QUERIES"
done
IFS=$OLD_IFS

done

if [ ! -s "$QUERIES" ]; then
echo "Keine Stop-IDs zur Abfrage vorhanden."
exit 0
fi

echo
echo "Rufe Abfahrten fuer die ausgewaehlten Stop-IDs ab ..."

Jede Stop-ID einzeln abfragen. Die Daten werden erst danach zusammengefuehrt.

while IFS="$(printf '\t')" read -r CAT STOPNAME SID; do
[ -n "$SID" ] || continue
ONE="${TMP}.one"

if curl -fsS --get "$API" \
    --data-urlencode "stopId=$SID" \
    --data-urlencode "n=40" \
    --data-urlencode "language=de" \
    -H "User-Agent: $UA" -o "$ONE"; then

    if jq -e '.stopTimes | type == "array"' "$ONE" >/dev/null 2>&1; then
        jq -c --arg cat "$CAT" --arg stopname "$STOPNAME" --arg sid "$SID" '
          .stopTimes[]
          | {
              category:$cat,
              stop:(.place.name // $stopname),
              stopId:(.place.stopId // $sid),
              mode:(.mode // "UNKNOWN"),
              line:(.displayName // "?"),
              destination:(.headsign // "?"),
              departure:(.place.departure // .departure // null),
              scheduled:(.place.scheduledDeparture // .scheduledDeparture // null),
              track:(.place.track // "-"),
              realtime:(.realTime // false),
              cancelled:(.cancelled // false),
              trip:(.tripId // "")
            }
        ' "$ONE" >> "$RESP"
    else
        echo "Warnung: Unerwartetes Format fuer Stop-ID $SID" >&2
    fi
else
    echo "Warnung: Abfrage fehlgeschlagen fuer Stop-ID $SID" >&2
fi

done < "$QUERIES"

if [ ! -s "$RESP" ]; then
echo "Keine Abfahrten fuer die ausgewaehlten Stop-IDs erhalten."
echo "Moeglicherweise verwendet die API einen anderen Abfrageparameter."
echo "Die Haltestellensuche selbst war erfolgreich."
exit 0
fi

Zeiten fuer die Sortierung normalisieren.

ISO-Datumszeiten werden lexikografisch sortiert; Uhrzeit wird lokal angezeigt.

jq -s -r '
def rawtime:
if .departure != null then .departure
else .scheduled
end;
def clock:
if type == "string" then
(if length >= 20 and .[10:11] == "T" then
(try (("date -d @" + (.[0:19] + "Z" | fromdateiso8601 | tostring) + " +%H:%M") | .) catch "??:??")
elif length >= 5 then .[0:5]
else tostring
end)
elif type == "number" then
(try (./1000 | strftime("%H:%M")) catch tostring)
else "??:??"
end;
map(select(rawtime != null))
| map(. + {
sorttime:(rawtime|tostring),
shownTime:(rawtime|clock),
status:(if .cancelled then "ENTFAELLT"
elif .realtime then "Echtzeit"
else "Fahrplan" end),
shownMode:(if .mode == "BUS" then "BUS"
elif .mode == "FERRY" then "FÄHRE"
elif .mode == "TRAM" then "TRAM"
elif .mode == "SUBWAY" then "U-BAHN"
elif .mode == "SUBURBAN" then "S-BAHN"
elif (.mode == "HIGHSPEED_RAIL" or .mode == "HIGH_SPEED_RAIL"
or .mode == "LONG_DISTANCE") then "FERN"
elif (.mode == "REGIONAL_RAIL" or .mode == "REGIONAL_FAST_RAIL"
or .mode == "RAIL") then "BAHN"
else .mode end)
})

Bei doppelten Meldungen derselben Fahrt am selben Steig bevorzugt

den expliziten Verkehrsmodus vor OTHER.

| sort_by([.sorttime, (if .mode == "OTHER" then 1 else 0 end)])
| unique_by([.stopId,.trip,.sorttime])
| sort_by(.sorttime)
| .[:5]
| .

| [
rawtime,
.stop,
.shownMode,
.line,
.destination,
.status,
.track
]
| @tsv
' "$RESP" > "$OUT"

echo
echo "DIE NAECHSTEN 5 ABFAHRTEN INSGESAMT"
printf '%-6s | %-29s | %-5s | %-8s | %-29s | %-9s | %s\n' 
"Zeit" "Haltestelle" "Art" "Linie" "Ziel" "Daten" "Steig"
printf '%s\n' "---------------------------------------------------------------------------------------------------------------"

if [ -s "$OUT" ]; then
awk -F '\t' '{
printf "%-6s | %-29s | %-5s | %-8s | %-29s | %-9s | %s\n",
$1,$2,$3,$4,$5,$6,$7
}' "$OUT"
else
echo "Keine Abfahrten mit verwertbarer Zeit gefunden."
fi

echo
echo "TRAM* bedeutet: Transitous meldete OTHER an einer ausgewaehlten Tram-Stop-ID."
echo "Die angezeigten 5 Eintraege sind auf die abgefragten Stop-IDs begrenzt."
echo "Sonderhaltestellen ohne Abfahrten in der ersten API-Suche koennen fehlen."

Für einen beliebig in Toulouse gewählten Ort erhalte ich folgende Aussgabe;

[/share/Container] # /share/Container/abfahrten-ortstest.sh 43.581972 1.448832
Suche Haltestellen rund um 43.581972, 1.448832 ...
Auswahl: BUS - Récollets Daste (ca. 440 m)
Keine Haltestelle fuer TRAM gefunden.
Keine Haltestelle fuer RAIL gefunden.
Keine Haltestelle fuer FERRY gefunden.

Rufe Abfahrten fuer die ausgewaehlten Stop-IDs ab ...

DIE NAECHSTEN 5 ABFAHRTEN INSGESAMT
Zeit   | Haltestelle                   | Art   | Linie    | Ziel                          | Daten     | Steig

2026-10-12T20:49:00Z | Récollets Daste              | BUS   | L4       | Cours Dillon                  | Echtzeit  | -
2026-10-12T21:19:00Z | Récollets Daste              | BUS   | L4       | Cours Dillon                  | Echtzeit  | -
2026-10-12T21:47:00Z | Récollets Daste              | BUS   | L4       | Cours Dillon                  | Echtzeit  | -
2026-10-12T22:16:00Z | Récollets Daste              | BUS   | L4       | Cours Dillon                  | Echtzeit  | -
2026-10-12T22:46:00Z | Récollets Daste              | BUS   | L4       | Cours Dillon                  | Echtzeit  | -


Ob das stimmt ? Keine Ahnung ? Kopf matschig ? Garantiert … die Pina Colada ruft.:joy:

Danke Tom! 10 Stunden am Stück, der Piña Colada ist mehr als verdient :tropical_drink: Genieß es und entspann dich, das Skript kann bis morgen warten :grinning_face_with_smiling_eyes:

Ich habe dein Skript überprüft und deinen Toulouse-Test noch einmal durchgeführt. Hier ist, was ich gesehen habe (zum Durchlesen in Ruhe ^^):

Dein Toulouse-Ergebnis ist korrekt: Die Linie L4 zum Cours Dillon fährt tatsächlich um 22:49 Uhr, 23:19 Uhr, 23:47 Uhr, 0:16 Uhr und 0:46 Uhr. Allerdings wird die Zeit in UTC angezeigt (20:49Z = 22:49 Uhr bei uns).

Aber dein Punkt liegt neben Saint-Agne: Bus in 160 m, Metro B in 230 m, TER-Bahnhof in 300 m. Das Skript hat einen Bus in 440 m gefunden und weder die Metro noch den Bahnhof. Zwei Gründe:

  • Die Suche durchsucht die nächsten 100 Abfahrten im Bereich. In Toulouse um 22:49 Uhr deckt das nur eine halbe Stunde ab (22:49 Uhr → 23:17 Uhr). Eine Haltestelle, die in dieser halben Stunde nichts hat, ist unsichtbar: ein TER-Bahnhof mit einem Zug pro Stunde oder ein Bus am Abend. Um Haltestellen unabhängig von den Fahrplänen zu finden, gibt /api/v1/map/stops auf einem Rechteck um den Punkt alle Haltestellen im Bereich zurück. Anschließend stoptimes pro Haltestelle wie du es bereits tust.
  • In cat fallen U-Bahn (SUBWAY), TGV und Intercités (HIGHSPEED_RAIL, LONG_DISTANCE) in OTHER, und die Schleife durchläuft nur BUS, TRAM, RAIL und FERRY. Von den 95 Abfahrten um deinen Punkt waren 58 U-Bahnen B, alle ignoriert.

Einige Details im Skript:

  • Leere Zielangabe: .headsign // "?" funktioniert nicht bei einer leeren Zeichenkette, da in jq nur null und false den // auslösen. Daher der TER ohne Ziel in La Rochelle. .tripTo.name gibt immer den Endbahnhof.
  • Die Zeit: clock erzeugt den Text "date -d @…", aber jq kann keine Befehle ausführen, und es wird rawtime angezeigt, nicht shownTime. Einfacher: .[0:19] + "Z" | fromdateiso8601 | strflocaltime("%H:%M") mit jq gestartet mit TZ= der Zeitzone der Haltestelle (Feld place.tz). Es muss nur sichergestellt sein, dass das System die Zeitzonen hat (/usr/share/zoneinfo), sonst bleibt jq ohne ein Wort in UTC.
  • Stornierungen: Es gibt auch tripCancelled (gesamte Strecke gestrichen), zusätzlich zu cancelled.
  • Entfernung: 72.000 m pro Längengrad, das stimmt nur in Deutschland, aber in Toulouse sind es eher 81.000. 111320 × cos(latitude) funktioniert überall.
  • Leerer User-Agent: Transitous ist ein Gemeinschaftsdienst, sie schätzen einen User-Agent, der die App identifiziert (Name + Link zum Repository z. B.).

Für Geisterhaltestellen: Mit deiner Methode stören sie nicht, da sie keine Abfahrten haben. Nur filtern, wenn du über map/stops gehst :wink: Und die Seilbahn ist nicht so extrem, in Toulouse ist das eine echte Linie, die wir jeden Tag nehmen :joy:

Die Idee, die Verkehrsmittel und den Radius in der Konfiguration auswählen zu lassen, ist top :+1:

Gute Nacht!