diff --git a/rvv-regensburg.skill b/rvv-regensburg.skill new file mode 100644 index 0000000..f2611cc Binary files /dev/null and b/rvv-regensburg.skill differ diff --git a/skills/rvv-regensburg/SKILL.md b/skills/rvv-regensburg/SKILL.md new file mode 100644 index 0000000..5d57215 --- /dev/null +++ b/skills/rvv-regensburg/SKILL.md @@ -0,0 +1,45 @@ +--- +name: rvv-regensburg +description: Real-time public transport information for Regensburg (RVV). Provides departures, stop finding, and journey planning via direct API access. Use when the user asks for bus/train departures in Regensburg or needs to find a connection within the city. +--- + +# 🚌 RVV Regensburg + +Access real-time public transport data for the Regensburg transport network (RVV) using the Mentz EFA API. + +## 🚀 Quick Usage + +Use the provided `scripts/rvv_query.py` to interact with the API. Always try to find the correct Stop ID first if you don't have it. + +### 1. Find a Stop ID +If you only have a name like "Hauptbahnhof", find its global Stop ID (e.g., `de:09362:11000`): +```bash +./scripts/rvv_query.py stop "Hauptbahnhof" +``` + +### 2. Get Next Departures +To see what's leaving a stop soon (using its Stop ID): +```bash +./scripts/rvv_query.py departures "de:09362:11010" --count 5 +``` + +### 3. Plan a Journey +To find connections between two Stop IDs: +```bash +./scripts/rvv_query.py trip "de:09362:11010" "de:09362:11000" +``` + +## 🛠️ API Details (Advanced) +If `rvv_query.py` is not sufficient, you can use `curl` directly. The base URL is `https://efa.rvv.de/efa/`. + +**Endpoints:** +- `XML_STOPFINDER_REQUEST`: Resolve names to IDs. +- `XML_DM_REQUEST`: Departure board. +- `XML_TRIP_REQUEST2`: Connection search. + +Always include `outputFormat=rapidJSON` and `version=10.5.17.3` in your requests for the best experience. + +## 💡 Tips +- **Real-time:** The API provides real-time data by default (`useRealtime=1`). Look for `rt_time` in the script output. +- **Global IDs:** Prefer the global ID format (e.g., `de:09362:XXXXX`) for better accuracy. +- **Regensburg Focus:** When searching for stops, include "Regensburg" in the name to avoid matches in other cities (e.g., "Hauptbahnhof" -> "Regensburg, Hauptbahnhof"). diff --git a/skills/rvv-regensburg/scripts/rvv_query.py b/skills/rvv-regensburg/scripts/rvv_query.py new file mode 100755 index 0000000..0a1e5bd --- /dev/null +++ b/skills/rvv-regensburg/scripts/rvv_query.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +import requests +import json +import argparse +import sys + +BASE_URL = "https://efa.rvv.de/efa/" +VERSION = "10.5.17.3" + +def query_stop(name): + params = { + "name_sf": name, + "outputFormat": "rapidJSON", + "type_sf": "any", + "locationInfoActive": "1", + "version": VERSION + } + response = requests.get(f"{BASE_URL}XML_STOPFINDER_REQUEST", params=params) + return response.json() + +def query_departures(stop_id, count=10): + params = { + "name_dm": stop_id, + "type_dm": "stop", + "outputFormat": "rapidJSON", + "itdDateTimeDepArr": "dep", + "useRealtime": "1", + "mode": "direct", + "depSequence": count, + "version": VERSION + } + response = requests.get(f"{BASE_URL}XML_DM_REQUEST", params=params) + return response.json() + +def query_trip(origin_id, destination_id, count=4): + params = { + "name_origin": origin_id, + "type_origin": "stop", + "name_destination": destination_id, + "type_destination": "stop", + "outputFormat": "rapidJSON", + "itdTripDateTimeDepArr": "dep", + "calcNumberOfTrips": count, + "useRealtime": "1", + "mode": "direct", + "version": VERSION + } + response = requests.get(f"{BASE_URL}XML_TRIP_REQUEST2", params=params) + return response.json() + +def main(): + parser = argparse.ArgumentParser(description="Query RVV Regensburg API") + subparsers = parser.add_subparsers(dest="command", help="Command to run") + + # Stop Finder + stop_parser = subparsers.add_parser("stop", help="Find a stop ID") + stop_parser.add_argument("name", help="Name of the stop") + + # Departure Monitor + dep_parser = subparsers.add_parser("departures", help="Get departures for a stop") + dep_parser.add_argument("stop_id", help="Stop ID (e.g., de:09362:11010)") + dep_parser.add_argument("--count", type=int, default=10, help="Number of departures") + + # Trip Planning + trip_parser = subparsers.add_parser("trip", help="Plan a trip between two stops") + trip_parser.add_argument("origin_id", help="Origin Stop ID") + trip_parser.add_argument("destination_id", help="Destination Stop ID") + trip_parser.add_argument("--count", type=int, default=4, help="Number of trips") + + args = parser.parse_args() + + if args.command == "stop": + result = query_stop(args.name) + points = result.get("locations", []) + output = [] + for p in points: + if p.get("type") == "stop": + output.append({ + "id": p["id"], + "stopId": p.get("properties", {}).get("stopId") or p.get("id"), + "name": p["name"] + }) + print(json.dumps(output, indent=2, ensure_ascii=False)) + elif args.command == "departures": + result = query_departures(args.stop_id, args.count) + # EFA rapidJSON uses 'stopEvents' for departures + stop_events = result.get("stopEvents", []) + output = [] + for event in stop_events: + transport = event.get("transportation", {}) + departure_time = event.get("departureTimePlanned") + departure_time_rt = event.get("departureTimeEstimated") + output.append({ + "line": transport.get("number"), + "direction": transport.get("destination", {}).get("name"), + "base_time": departure_time[11:16] if departure_time else None, + "rt_time": departure_time_rt[11:16] if departure_time_rt else None, + "delay": event.get("delay", 0) + }) + print(json.dumps(output, indent=2, ensure_ascii=False)) + elif args.command == "trip": + result = query_trip(args.origin_id, args.destination_id, args.count) + # EFA rapidJSON uses 'journeys' for trips + journeys = result.get("journeys", []) + output = [] + for j in journeys: + legs = j.get("legs", []) + trip_info = { + "legs": [] + } + for leg in legs: + origin = leg.get("origin", {}) + dest = leg.get("destination", {}) + transport = leg.get("transportation", {}) + trip_info["legs"].append({ + "from": origin.get("name"), + "to": dest.get("name"), + "line": transport.get("number"), + "dep_planned": origin.get("departureTimePlanned", "")[11:16], + "dep_est": origin.get("departureTimeEstimated", "")[11:16], + "arr_planned": dest.get("arrivalTimePlanned", "")[11:16], + "arr_est": dest.get("arrivalTimeEstimated", "")[11:16] + }) + output.append(trip_info) + print(json.dumps(output, indent=2, ensure_ascii=False)) + else: + parser.print_help() + +if __name__ == "__main__": + main()