Skill: xenofex7/swiss-transport (version 1.0.0)

--- name: swiss-transport description: Swiss Public Transport real-time information. Use when querying train, bus, tram, or boat schedules in Switzerland. Supports station search, departure boards, journey planning from A to B, and connection details. Use for queries like "When does the next train leave from Zürich?" or "How do I get from Bern to Geneva?" or "Show departures at Basel SBB". homepage: https://transport.opendata.ch --- # Swiss Public Transport Query Swiss public transport (SBB, BLS, ZVV, etc.) using the official transport.opendata.ch API. ## Quick Commands ### Search stations ```bash curl -s "https://transport.opendata.ch/v1/locations?query=Zürich" | jq -r '.stations[] | "\(.name) (\(.id))"' ``` ### Get next departures ```bash curl -s "https://transport.opendata.ch/v1/stationboard?station=Zürich%20HB&limit=10" | \ jq -r '.stationboard[] | "\(.stop.departure[11:16]) \(.category) \(.number) → \(.to)"' ``` ### Plan journey from A to B ```bash curl -s "https://transport.opendata.ch/v1/connections?from=Zürich&to=Bern&limit=3" | \ jq -r '.connections[] | "Departure: \(.from.departure[11:16]) | Arrival: \(.to.arrival[11:16]) | Duration: \(.duration[3:]) | Changes: \(.transfers)"' ``` ### Get connection details with sections ```bash curl -s "https://transport.opendata.ch/v1/connections?from=Zürich%20HB&to=Bern&limit=1" | \ jq '.connections[0].sections[] | {from: .departure.station.name, to: .arrival.station.name, departure: .departure.departure, arrival: .arrival.arrival, transport: .journey.category, line: .journey.number}' ``` ## API Endpoints ### `/v1/locations` - Search stations ```bash curl "https://transport.opendata.ch/v1/locations?query=<station-name>" ``` Parameters: - `query` (required): Station name to search - `type` (optional): Filter by type (station, address, poi) ### `/v1/stationboard` - Departure board ```bash curl "https://transport.opendata.ch/v1/stationboard?station=<station>&limit=<number>" ``` Parameters: - `station` (required): Station name or ID - `limit` (optional): Number of results (default 40) - `transportations[]` (optional): Filter by type (ice_tgv_rj, ec_ic, ir, re_d, ship, s_sn_r, bus, cableway, arz_ext, tramway_underground) - `datetime` (optional): Date/time in ISO format ### `/v1/connections` - Journey planner ```bash curl "https://transport.opendata.ch/v1/connections?from=<start>&to=<destination>&limit=<number>" ``` Parameters: - `from` (required): Starting station - `to` (required): Destination station - `via[]` (optional): Intermediate station(s) - `date` (optional): Date (YYYY-MM-DD) - `time` (optional): Time (HH:MM) - `isArrivalTime` (optional): 0 (departure, default) or 1 (arrival) - `limit` (optional): Number of connections (max 16) ## Helper Script Use `scripts/journey.py` for formatted journey planning: ```bash python3 scripts/journey.py "Zürich HB" "Bern" python3 scripts/journey.py "Basel" "Lugano" --limit 5 ``` ## Notes - All times are in Swiss local time (CET/CEST) - Station names support autocomplete (e.g., "Zürich" finds "Zürich HB") - API returns JSON by default - No API key required - Real-time data includes delays and platform changes

path: scripts/journey.py
content: #!/usr/bin/env python3
"""
Swiss Public Transport Journey Planner
Usage: python3 journey.py <from> <to> [--limit N] [--time HH:MM] [--date YYYY-MM-DD]
"""
import sys
import json
import urllib.request
import urllib.parse
from datetime import datetime

def format_time(iso_time):
    """Extract HH:MM from ISO datetime"""
    try:
        return iso_time[11:16]
    except:
        return iso_time

def format_duration(duration):
    """Format duration from 00d00:12:00 to readable format"""
    try:
        parts = duration.split(':')
        days_hours = parts[0].split('d')
        days = int(days_hours[0]) if len(days_hours) > 1 else 0
        hours = int(days_hours[-1])
        mins = int(parts[1])
        
        if days > 0:
            return f"{days}d {hours}h {mins}m"
        elif hours > 0:
            return f"{hours}h {mins}m"
        else:
            return f"{mins}m"
    except:
        return duration

def get_connections(from_station, to_station, limit=3, time=None, date=None):
    """Query connections from transport.opendata.ch API"""
    params = {
        'from': from_station,
        'to': to_station,
        'limit': limit
    }
    
    if time:
        params['time'] = time
    if date:
        params['date'] = date
    
    url = f"https://transport.opendata.ch/v1/connections?{urllib.parse.urlencode(params)}"
    
    try:
        with urllib.request.urlopen(url, timeout=10) as response:
            data = json.loads(response.read())
            return data
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

def print_connections(data):
    """Print connections in a readable format"""
    connections = data.get('connections', [])
    
    if not connections:
        print("No connections found.")
        return
    
    from_station = data['from']['name']
    to_station = data['to']['name']
    
    print(f"\n🚂 {from_station} → {to_station}\n")
    print("=" * 80)
    
    for i, conn in enumerate(connections, 1):
        dep_time = format_time(conn['from']['departure'])
        arr_time = format_time(conn['to']['arrival'])
        duration = format_duration(conn['duration'])
        transfers = conn['transfers']
        platform = conn['from'].get('platform', 'N/A')
        
        print(f"\n{i}. Departure: {dep_time} (Platform {platform})")
        print(f"   Arrival:   {arr_time}")
        print(f"   Duration:  {duration}")
        print(f"   Changes:   {transfers}")
        
        # Print sections
        if 'sections' in conn:
            print(f"\n   Route:")
            for j, section in enumerate(conn['sections'], 1):
                if section.get('journey'):
                    journey = section['journey']
                    from_name = section['departure']['station']['name']
                    to_name = section['arrival']['station']['name']
                    category = journey.get('category', 'N/A')
                    number = journey.get('number', '')
                    
                    print(f"   {j}. {category} {number}: {from_name} → {to_name}")
                elif section.get('walk'):
                    walk_duration = section['walk'].get('duration')
                    if walk_duration:
                        print(f"   {j}. 🚶 Walk ({walk_duration} min)")
        
        print()

def main():
    if len(sys.argv) < 3:
        print("Usage: python3 journey.py <from> <to> [--limit N] [--time HH:MM] [--date YYYY-MM-DD]")
        print("\nExample:")
        print("  python3 journey.py 'Zürich HB' 'Bern'")
        print("  python3 journey.py 'Basel' 'Lugano' --limit 5")
        print("  python3 journey.py 'Zürich' 'Genève' --time 14:30")
        sys.exit(1)
    
    from_station = sys.argv[1]
    to_station = sys.argv[2]
    
    # Parse optional arguments
    limit = 3
    time = None
    date = None
    
    i = 3
    while i < len(sys.argv):
        if sys.argv[i] == '--limit' and i + 1 < len(sys.argv):
            limit = int(sys.argv[i + 1])
            i += 2
        elif sys.argv[i] == '--time' and i + 1 < len(sys.argv):
            time = sys.argv[i + 1]
            i += 2
        elif sys.argv[i] == '--date' and i + 1 < len(sys.argv):
            date = sys.argv[i + 1]
            i += 2
        else:
            i += 1
    
    # Get and print connections
    data = get_connections(from_station, to_station, limit, time, date)
    print_connections(data)

if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)