Skip to content

Instantly share code, notes, and snippets.

@LutfiTekin
Created May 5, 2025 17:35
Show Gist options
  • Save LutfiTekin/0b8c1251fff88fcac210b76ebf26fa8e to your computer and use it in GitHub Desktop.
Save LutfiTekin/0b8c1251fff88fcac210b76ebf26fa8e to your computer and use it in GitHub Desktop.
load departures from given station using deutsche bahn api
#!/usr/bin/env bash
set -euo pipefail
# ----------------------------------------------------------------
# departures.sh β€” fetch scheduled departures via Timetables 'plan' endpoint only
# Includes ICE, RE, S-Bahn (S), buses, etc.
# Supports filtering by ICE, RE, S and a rolling window in hours
# Deduplicates entries and limits output count
# Adds color highlighting: time (black), train number (red), destination (yellow)
# ----------------------------------------------------------------
# ANSI color codes
BLACK='\033[0;30m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# Base URL for Timetables API
BASE="https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1"
# Ensure credentials are set
: "${DB_CLIENT_ID:?Please set DB_CLIENT_ID}"
: "${DB_API_KEY:?Please set DB_API_KEY}"
# Defaults
STATION="Berlin"
FILTER="" # ICE | RE | S
WINDOW_HOURS=2 # lookahead window (hours)
MAX_ENTRIES=10 # maximum departures to list
# Parse CLI arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--station) STATION="$2"; shift 2;;
--filter) FILTER="$2"; shift 2;;
--window) WINDOW_HOURS="$2"; shift 2;;
--limit) MAX_ENTRIES="$2"; shift 2;;
*) echo "Usage: $0 --station \"Name\" [--filter ICE|RE|S] [--window hours] [--limit count]" >&2; exit 1;;
esac
done
# URL-encode station name
ENC_STATION=$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' "$STATION")
# 1) Lookup station EVA via XML
echo "πŸ” Looking up station: $STATION" >&2
station_xml=$(curl -s \
-H "Accept: application/xml" \
-H "DB-Client-Id: $DB_CLIENT_ID" \
-H "DB-Api-Key: $DB_API_KEY" \
"$BASE/station/$ENC_STATION")
eva=$(xmllint --xpath 'string(//station/@eva)' - <<<"$station_xml" 2>/dev/null || echo)
NAME=$(xmllint --xpath 'string(//station/@name)' - <<<"$station_xml" 2>/dev/null || echo)
if [[ -z "$eva" ]]; then
echo "❌ Station not found: $STATION" >&2
exit 1
fi
NAME=${NAME:-$STATION}
echo "βœ… Station: $NAME (EVA: $eva)" >&2
# 2) Compute time window
t_now=$(date +%s)
if date -v+${WINDOW_HOURS}H >/dev/null 2>&1; then
t_end=$(date -v+${WINDOW_HOURS}H +%s)
else
t_end=$(date -d "+${WINDOW_HOURS} hours" +%s)
fi
# 3) Fetch and combine 'plan' for each hour in window
echo "πŸš‰ Fetching schedule for next ${WINDOW_HOURS}h..." >&2
date_tag=$(date '+%y%m%d')
tmpfile=$(mktemp)
echo '<root>' > "$tmpfile"
for ((h=0; h<=WINDOW_HOURS; h++)); do
if date -v+${h}H >/dev/null 2>&1; then hr=$(date -v+${h}H '+%H'); else hr=$(date -d "+${h} hours" '+%H'); fi
curl -s \
-H "Accept: application/xml" \
-H "DB-Client-Id: $DB_CLIENT_ID" \
-H "DB-Api-Key: $DB_API_KEY" \
"$BASE/plan/$eva/$date_tag/$hr" >> "$tmpfile"
done
echo '</root>' >> "$tmpfile"
# 4) Extract departures and arrivals entries
entries=$(sed 's/<?xml[^>]*>//g' "$tmpfile" \
| xmlstarlet sel -T -t \
-m "//s/dp" -v "concat(@pt,'|',@pp,'|',../tl/@c,'|',../tl/@n,'|',@ppth)" -n \
-m "//s/ar" -v "concat(@pt,'|',@pp,'|',../tl/@c,'|',../tl/@n,'|',@ppth)" -n)
rm "$tmpfile"
# 5) Deduplicate while preserving order
entries=$(printf "%s\n" "$entries" | awk -F'|' '!seen[$1"|"$3"|"$4"|"$5"|"$2]++')
# 6) Output header
echo
echo "πŸ“ Next departures from $NAME (window=${WINDOW_HOURS}h)${FILTER:+, filter $FILTER}:"
# 7) Iterate and display up to MAX_ENTRIES with color
count=0
printf "%s\n" "$entries" | while IFS='|' read -r pt pp cat num path; do
[[ ! $pt =~ ^[0-9]{10}$ ]] && continue
[[ -n "$FILTER" && "$cat" != "$FILTER" ]] && continue
# Parse timestamp
year="20${pt:0:2}"; mon="${pt:2:2}"; day="${pt:4:2}"; hr="${pt:6:2}"; mn="${pt:8:2}"
ts_fmt="$year-$mon-$day $hr:$mn"
if date -j >/dev/null 2>&1; then
epoch=$(date -j -f "%Y-%m-%d %H:%M" "$ts_fmt" +%s)
else
epoch=$(date -d "$ts_fmt" +%s)
fi
(( epoch < t_now || epoch > t_end )) && continue
dest="${path##*|}"
# Color: time=black, train number=red, destination=yellow
printf "%b - %s %b%s%b to %b%s%b (Platform: %s)\n" \
"${BLACK}${ts_fmt}${NC}" "$cat" \
"${RED}" "$num" "${NC}" \
"${YELLOW}" "$dest" "${NC}" "${pp:-N/A}"
((++count>=MAX_ENTRIES)) && break
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment