#!/bin/bash # Route 5 on-demand monitor — checks real-time bus status # Usage: bash route5-status.sh set -eo pipefail VP_URL="https://data.texas.gov/download/cuc7-ywmd/application%2Fjson" TU_URL="https://data.texas.gov/download/mqtr-wwpy/application%2Fjson" # Fetch both feeds in parallel VP=$(curl -sL --max-time 10 "$VP_URL") & TU=$(curl -sL --max-time 10 "$TU_URL") & VP=$(curl -sL --max-time 10 "$VP_URL") TU=$(curl -sL --max-time 10 "$TU_URL") NOW=$(date +%s) # Route 5 eastbound vehicles VEHICLES=$(echo "$VP" | jq -c '[.entity[] | select(.vehicle.trip.routeId == "5" and .vehicle.trip.directionId == 0) | { vehicleId: .vehicle.vehicle.label, tripId: .vehicle.trip.tripId, stopId: .vehicle.stopId, status: .vehicle.currentStatus, lat: .vehicle.position.latitude, lon: .vehicle.position.longitude, speed: .vehicle.position.speed }]') # Route 5 eastbound trip updates TRIPS=$(echo "$TU" | jq -c --arg now "$NOW" '[.entity[] | select(.tripUpdate.trip.routeId == "5" and .tripUpdate.trip.directionId == 0) | { tripId: .tripUpdate.trip.tripId, firstStopDepart: ([.tripUpdate.stopTimeUpdate[] | select(.stopId == "5854") | (.departure.time // .arrival.time)] | .[0]), userStopArrive: ([.tripUpdate.stopTimeUpdate[] | select(.stopId == "964") | (.arrival.time // .departure.time)] | .[0]), userStopDelay: ([.tripUpdate.stopTimeUpdate[] | select(.stopId == "964") | (.arrival.delay // .departure.delay)] | .[0]) }] | [.[] | select(.firstStopDepart != null)] | sort_by(.firstStopDepart)') # Format output jq -n \ --argjson vehicles "$VEHICLES" \ --argjson trips "$TRIPS" \ --arg now "$NOW" '{ timestampUTC: ($now | tonumber | todate), timestampCST: (($now | tonumber - 21600) | todate), route: "5 - Woodrow/Lamar", direction: "Eastbound → Downtown", firstStop: "Anderson/Northcross (5854)", userStop: "Woodrow/Choquette (964)", scheduledTravel: "7m 26s", activeVehicles: ($vehicles | length), vehicles: $vehicles, nextDepartures: [$trips[] | { tripId, firstStopDepart: (if .firstStopDepart then (.firstStopDepart | tonumber | todate) else null end), userStopArrive: (if .userStopArrive then (.userStopArrive | tonumber | todate) else null end), delaySec: .userStopDelay, minsUntilDepart: (if .firstStopDepart then (((.firstStopDepart | tonumber) - ($now | tonumber)) / 60 | floor) else null end), minsUntilArrive: (if .userStopArrive then (((.userStopArrive | tonumber) - ($now | tonumber)) / 60 | floor) else null end) }] }'