# ÖBB Train Planner Checks the ÖBB real-time schedule, picks the best train for your upcoming calendar events, and shows a live "leave by" countdown from your current location. ## What's included ``` oebb-planner/ ├── server/ │ ├── index.js ← Node.js proxy server (ÖBB API + ICS parser) │ └── package.json ├── oebb-planner.jsx ← React front-end (use in Claude.ai artifacts or any React app) └── README.md ``` --- ## Quick start ### 1 — Start the proxy server The server bypasses ÖBB's CORS restrictions and parses ICS calendar files. ```bash cd server npm install npm start # → ÖBB Planner API running on http://localhost:3001 ``` > **Requires Node.js ≥ 18** (uses built-in `fetch` and `AbortSignal.timeout`). > For development with auto-reload: `npm run dev` ### 2 — Open the React app Paste `oebb-planner.jsx` into a Claude.ai artifact (or your own React app). The app detects whether the server is online and shows a status indicator in the header. --- ## Calendar integration The app reads any standard ICS calendar. Only events that have a **location field** and fall within the **next 14 days** are imported. ### Option A — ICS URL (Google Calendar, Outlook, Fastmail…) 1. In the app, open the **Calendar** panel → **ICS URL** tab 2. Paste your calendar's secret ICS address **Google Calendar:** Settings → click your calendar → scroll to *"Secret address in iCal format"* → copy the URL **Apple Calendar (iCloud):** Calendar app → right-click calendar → "Share Calendar" → enable public calendar → copy the URL (change `webcal://` to `https://` or paste as-is — the server handles both) **Outlook:** Settings → View all Outlook settings → Calendar → Shared calendars → Publish → ICS link ### Option B — Upload .ics file Export your calendar as a `.ics` file from any app (File → Export in Apple Calendar, etc.) and upload it via the **Upload File** tab. --- ## How the train matching works 1. Your **current GPS location** is used to find the nearest ÖBB station (via the HAFAS `LocGeoPos` API). 2. Each event's **location field** is used to search for the nearest destination station (`LocMatch`). 3. The HAFAS `TripSearch` endpoint finds journeys departing up to 2 hours before your event. 4. The **"leave by" time** = next catchable train departure − 12 minutes (default walk time to station). 5. Data refreshes automatically every **60 seconds**. --- ## Server endpoints | Method | Path | Description | |--------|------|-------------| | `POST` | `/api/hafas` | Proxy to `fahrplan.oebb.at/bin/mgate.exe` | | `GET` | `/api/calendar?url=` | Fetch & parse a remote ICS calendar | | `POST` | `/api/calendar/parse` | Parse raw ICS content sent in request body | | `GET` | `/api/health` | Liveness check | --- ## Customisation | Variable | Location | Default | Description | |----------|----------|---------|-------------| | `WALK_MINS` | `oebb-planner.jsx` | `12` | Walk time from your location to origin station | | `PORT` | `server/index.js` | `3001` | Server port | | `horizonDays` | `server/index.js` | `14` | How far ahead to look for calendar events | | `PROXY` | `oebb-planner.jsx` | `http://localhost:3001` | Server URL | --- ## Deploying beyond localhost To run this on a server or share it with others: 1. Set `PORT` via environment variable: `PORT=8080 npm start` 2. Update `PROXY` in `oebb-planner.jsx` to your server's URL 3. Restrict CORS in `server/index.js` if needed: ```js app.use(cors({ origin: "https://your-app.example.com" })); ``` --- ## Troubleshooting **"Server offline" shown in the app** → Make sure `npm start` is running in the `server/` directory. **No trains found for an event** → Check that the destination in your calendar event is a recognisable ÖBB station name (e.g. "Graz Hbf", "Salzburg Hauptbahnhof"). Verbose postal addresses are trimmed automatically. **Calendar import returns 0 events** → Only events with a `LOCATION` field set are imported. Make sure your events have a location and fall within the next 14 days. **ÖBB returns errors** → The HAFAS API occasionally changes. Check `fahrplan.oebb.at` is reachable from your server.