Compare commits

..

No commits in common. "main" and "5.25.0" have entirely different histories.
main ... 5.25.0

1386 changed files with 115805 additions and 57797 deletions

View file

@ -1,2 +0,0 @@
.git
node_modules

36
.eslintrc.json Normal file
View file

@ -0,0 +1,36 @@
{
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"ignorePatterns": ["node_modules", "*example.js"],
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "none",
"ignoreRestSiblings": false
}
]
},
"overrides": [
{
"files": [
"test/**"
],
"rules": {
"no-unused-vars": "off"
}
}
]
}

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
test/e2e/fixtures/* binary linguist-vendored

View file

@ -1,59 +0,0 @@
name: Build NPM Package & Docker
on:
push:
tags:
- 'v*'
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
id: push
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-pkg:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View file

@ -1,101 +1,57 @@
name: test
on: [push, pull_request, workflow_call]
env:
npm_config_cache: /tmp/npm-cache
on: [push, pull_request]
jobs:
lint-and-spellcheck:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: "Checkout code"
uses: actions/checkout@v4
- name: "Use Node.js"
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm install
- name: Run lint check
run: npm run lint
- name: Run spell check
run: npm run test-spelling
unit-tests:
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
- 18.x
- 20.x
- 22.x
node-version: [10.x, 12.x, 14.x, 15.x]
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: setup Node.js v${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- id: cache-npm
name: restore npm cache
uses: actions/cache@v4
with:
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-unit-tests
path: ${{ env.npm_config_cache }}
- run: npm install
- run: npm run lint
- run: npm run test-unit
integration-tests:
needs: unit-tests
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
- 18.x
- 20.x
- 22.x
node-version: [10.x, 12.x, 14.x, 15.x]
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: setup Node.js v${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- id: cache-npm
name: restore npm cache
uses: actions/cache@v4
with:
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-integration-tests
path: ${{ env.npm_config_cache }}
- run: npm install
- run: npm run lint
- run: npm run test-integration
e2e-tests:
needs: [unit-tests, integration-tests]
needs: integration-tests
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
node-version: [10.x]
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: setup Node.js v${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- id: cache-npm
name: restore npm cache
uses: actions/cache@v4
with:
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-e2e-tests
path: ${{ env.npm_config_cache }}
- run: npm install
- run: npm run lint
- run: npm run test-e2e

3
.gitignore vendored
View file

@ -5,5 +5,4 @@ Thumbs.db
node_modules
npm-debug.log
/.tap
*.ign.*
package-lock.json

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule "tools/transport-apis"]
path = tools/transport-apis
url = https://github.com/public-transport/transport-apis.git
branch = v1

View file

@ -1,22 +0,0 @@
FROM node:18-alpine
LABEL org.opencontainers.image.title="db-vendo-client"
LABEL org.opencontainers.image.description="A clean REST API wrapping around the new Deutsche Bahn API."
LABEL org.opencontainers.image.authors="Traines <git@traines.eu>"
LABEL org.opencontainers.image.documentation="https://github.com/public-transport/db-vendo-client"
LABEL org.opencontainers.image.source="https://github.com/public-transport/db-vendo-client"
LABEL org.opencontainers.image.licenses="ISC"
WORKDIR /app
# install dependencies
#RUN apk add --update git
ADD package.json package-lock.json /app/
RUN npm install && npm cache clean --force
# add source code
ADD . /app
EXPOSE 3000
ENV PORT 3000
CMD ["node", "api.js"]

52
api.js
View file

@ -1,52 +0,0 @@
import {createClient} from './index.js';
import {profile as dbProfile} from './p/db/index.js';
import {profile as dbnavProfile} from './p/dbnav/index.js';
import {profile as dbwebProfile} from './p/dbweb/index.js';
import {profile as dbrisProfile} from './p/dbris/index.js';
import {profile as dbbahnhofProfile} from './p/dbbahnhof/index.js';
import {profile as dbregioguideProfile} from './p/dbregioguide/index.js';
import {mapRouteParsers} from './lib/api-parsers.js';
import {createHafasRestApi as createApi} from 'hafas-rest-api';
const config = {
hostname: process.env.HOSTNAME || 'localhost',
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
name: 'db-vendo-client',
description: 'db-vendo-client',
homepage: 'https://github.com/public-transport/db-vendo-client',
version: '6',
docsLink: 'https://github.com/public-transport/db-vendo-client',
openapiSpec: true,
logging: true,
aboutPage: true,
enrichStations: true,
etags: 'strong',
csp: 'default-src \'none\'; style-src \'self\' \'unsafe-inline\'; img-src https:',
mapRouteParsers,
};
const profiles = {
db: dbProfile,
dbnav: dbnavProfile,
dbweb: dbwebProfile,
dbris: dbrisProfile,
dbbahnhof: dbbahnhofProfile,
dbregioguide: dbregioguideProfile,
};
const start = async () => {
const vendo = createClient(
profiles[process.env.DB_PROFILE] || dbnavProfile,
process.env.USER_AGENT || 'link-to-your-project-or-email',
config,
);
const api = await createApi(vendo, config);
api.listen(config.port, (err) => {
if (err) {
console.error(err);
}
});
};
start();

View file

@ -4,4 +4,11 @@ Thanks for helping! 🙏
## Adding integration/end-to-end tests
Refer to the [testing docs](docs/tests.md).
The [end-to-end/system tests](https://en.wikipedia.org/wiki/System_testing) in [`test/e2e`](test/e2e), executing via `npm run test-e2e`, are querying real HAFAS endpoints, expecting valid & *reasonable* responses.
The [integration tests](https://en.wikipedia.org/wiki/Integration_testing) (`npm run test-integration`) are the same `test/e2e` tests, running against *mocked* HAFAS responses from [`test/e2e/fixtures`](test/e2e/fixtures).
1. Add a new end-to-end test that tests your feature/bugfix.
2. Make sure the test passes. You can run your test exclusively using `test.only`.
3. Record the HAFAS responses as [fixtures](https://en.wikipedia.org/wiki/Test_fixture) using `npm run test-integration:record`.
4. Make sure the passes against *only* mocked responses using `npm run test-integration`.

View file

@ -1,644 +0,0 @@
{
"version": "0.2",
"language": "en",
"words": [
"Abfahrt",
"abfahrten",
"abfahrts",
"abfrage",
"abgangs",
"Abgelaufen",
"Abonnement",
"Abschnitte",
"abschnitts",
"Adelsheim",
"Adenauerplatz",
"agilis",
"Ahorn",
"Ahrensfelde",
"Aktualisierung",
"Alexanderplatz",
"alterseingabe",
"Altstadt",
"Alzey",
"andere",
"Anfang",
"anfrage",
"Anfrage",
"anfragezeit",
"Angebot",
"angebote",
"angebots",
"angebotsbeziehung",
"angebotseinholung",
"ANGEBOTSINFORMATION",
"Angebotsoption",
"angefragten",
"Angeltürn",
"ankuenfte",
"ankunft",
"ankunfts",
"Anmeldung",
"ANRUFPFLICHTIG",
"ANRUFPFLICHTIGEVERKEHRE",
"Anteil",
"anzahl",
"anzeige",
"Anzeigen",
"APPLEPAY",
"ARGUMENTE",
"arrs",
"Arverio",
"Aschaffenburg",
"attribut",
"Atze",
"Auerbach",
"AUSFALL",
"ausgegeben",
"auslastung",
"auslastungs",
"Auslastungsinformation",
"auslastungsmeldungen",
"auslastungstexte",
"Ausreserviert",
"außergewöhnlich",
"Ausserhalb",
"außerhalb",
"Ausstattung",
"Auswahl",
"autonome",
"bahnbonus",
"BAHNCARD",
"Bahnhof",
"bahnhofs",
"Bahnhofsinfo",
"bahnhofstafel",
"bahnhofstafeln",
"BARRIEREFREI",
"Bayerischer",
"BEFÖRDERER",
"begrenzt",
"behaviour",
"Behindertengerechte",
"Behindertengerechtes",
"Benoit",
"Beratzhausen",
"bereits",
"besonderer",
"Besucherpark",
"betrag",
"Beusselstraße",
"bezeichnung",
"Bietigheim",
"Bismarckstr",
"Bissingen",
"bitmasks",
"Blaschkoallee",
"Blissestr",
"BNWNZF",
"Böhme",
"BONVOYO",
"Bordbistro",
"Bordrestaurant",
"Boxberg",
"Breckerfeld",
"Britz",
"brokentrip",
"brutto",
"Buch",
"Buchbar",
"buchbarkeit",
"BUCHEN",
"BUCHUNG",
"buchungs",
"Bundesbahnen",
"Bundesplatz",
"BUSSE",
"bzgl",
"capacitorjs",
"Chaussee",
"checkin",
"childrens",
"CITYTICKET",
"clie",
"cncl",
"codeshares",
"conds",
"consumability",
"Consumability",
"Creglingen",
"crosssell",
"Ctrf",
"customisation",
"customisations",
"Damm",
"Daten",
"Dauer",
"dauerhaft",
"dbnav",
"dbregioguide",
"dbris",
"dbweb",
"Deldicque",
"DELFI",
"derhuerst",
"Deutz",
"dhid",
"differenzpreis",
"Dinkelsbühl",
"distanz",
"Dombühl",
"echtzeit",
"Eggmühl",
"ehemals",
"Eicholzheim",
"einchecken",
"eine",
"einfache",
"Einrichtung",
"Einstieg",
"einstiegs",
"Einstiegshilfe",
"Einstiegstyp",
"Eisenacher",
"emis",
"empfehlen",
"Ende",
"entfällt",
"Entgelt",
"ereignis",
"Erforderlich",
"erforderlicher",
"Ergoldsbach",
"Erlenbach",
"ERMAESSIGUNG",
"ermaessigungen",
"erster",
"ERWACHSENER",
"erwarten",
"erwartet",
"Eubigheim",
"Eurocity",
"eventuell",
"externe",
"Fahrkarte",
"fahrplan",
"Fahrplanperiode",
"Fahrpreis",
"Fahrradbeförderung",
"FAHRRADMITNAHME",
"Fahrt",
"FAHRZEUG",
"Fahrzeuggebundene",
"FAMILIENKIND",
"Fehler",
"Fehrbelliner",
"Fernbf",
"Fernverkehr",
"Flexpreis",
"Flix",
"Fltr",
"Flughafen",
"FPTF",
"Freising",
"Friedrichshall",
"Friedrichstr",
"frueher",
"Frwd",
"FUSSWEG",
"Fußweg",
"FVFFLPI",
"FVFSPPI",
"FVFSSPI",
"FVKBACI",
"GARE",
"Garten",
"Gastronomie",
"Gattung",
"gekauft",
"Geltungzeitpunkt",
"GENERALABONNEMENT",
"geolocation",
"geopositions",
"Geringe",
"gesamt",
"Gesundbrunnen",
"gleis",
"Gneisenaustr",
"Goerdelersteg",
"grafisch",
"Greifswalder",
"Grenzallee",
"Grünanlagen",
"gruppen",
"Gütersloh",
"GUTSCHEIN",
"haben",
"hafas",
"Hagelstadt",
"Halbtax",
"HALBTAXABO",
"Halemweg",
"Halensee",
"Hallerstraße",
"halte",
"Haltestellen",
"Hansering",
"Hansestadt",
"Haselhorst",
"Hauptbahnhof",
"Heidelberger",
"Hennef",
"Hermannplatz",
"Hermannstraße",
"hessen",
"Heuchelhof",
"HINFAHRT",
"Hinweis",
"Hinweise",
"HOCH",
"HOCHGESCHWINDIGKEITSZUEGE",
"Hohe",
"Hohenstadt",
"Hohenzollerndamm",
"Hüngheim",
"IBNR",
"Ihren",
"Ihrer",
"Informationen",
"INKLUSIVE",
"Innsbrucker",
"Instanz",
"INTERCITYUNDEUROCITYZUEGE",
"INTERREGIOUNDSCHNELLZUEGE",
"inventarsystem",
"irregulaere",
"Jakob",
"Jannis",
"Jannowitzbrücke",
"jetzt",
"Johannisthaler",
"journeystop",
"JUGENDLICHER",
"Jungfernheide",
"Kanal",
"KATALOG",
"kategorie",
"Kategorien",
"kategorisierung",
"kein",
"KEINE",
"Kennung",
"Kennzeichen",
"Kirche",
"klasse",
"KLASSENLOS",
"KLEINKIND",
"Kleistpark",
"Klima",
"KLIMATICKET",
"Köfering",
"Köln",
"kombinations",
"komfort",
"konditionen",
"konditions",
"Konstanzer",
"kontext",
"Kontingente",
"KRCC",
"KREDITKARTE",
"Kristjan",
"Kurz",
"kurztext",
"Landsberger",
"Landshut",
"langtext",
"LASTSCHRIFT",
"Lauda",
"letzte",
"letztes",
"leuchtturm",
"Lichtenberg",
"liegt",
"linien",
"Lipschitzallee",
"Loesungs",
"Ludwigsburg",
"luxon",
"materialisierungs",
"Maxnet",
"MBAAA",
"Mehringdamm",
"Meidling",
"Meldungen",
"Merchingen",
"Messe",
"Mierendorffplatz",
"Millis",
"Minden",
"mitteltext",
"mittlere",
"Mobilitätseingeschränkte",
"Mobilitätsservice",
"Möckernbrücke",
"Möckmühl",
"modul",
"moeckmuehl",
"Moeglich",
"möglich",
"Montabaur",
"Moosburg",
"Mosbach",
"Movas",
"München",
"Musiktheater",
"mwst",
"mxtxm",
"Nachgelagert",
"Nachricht",
"Nahreisezug",
"NAHVERKEHRSONSTIGEZUEGE",
"Naturkundemuseum",
"Neckarsulm",
"netto",
"Neufahrn",
"Neukölln",
"Neumarkt",
"NICHT",
"Niederbay",
"NIEDRIG",
"noch",
"noopener",
"Nord",
"noreferrer",
"Notiz",
"Notizen",
"NULLPREIS",
"nummer",
"nutzungs",
"Obereubigheim",
"Oberpf",
"Oberschefflenz",
"Obertraubling",
"Oberwittstadt",
"Ohne",
"orte",
"Ostbahnhof",
"Osterburken",
"Österreichische",
"Ostkreuz",
"Parchimer",
"Parsberg",
"passend",
"Passlist",
"Paulsternstr",
"Pauschalpreis",
"Pergamonkeller",
"Pergamonweg",
"Perleberg",
"PFLICHT",
"PLAETZE",
"planungs",
"platf",
"Platz",
"platzbedarfe",
"platzprofil",
"pollyjs",
"polyline",
"polylines",
"Positionen",
"Preis",
"preise",
"preisunterdrueckung",
"Prenzlauer",
"prio",
"priorisierte",
"prioritaet",
"produkt",
"produkte",
"produktgattungen",
"Profil",
"PRUEFEN",
"PUBLICTRANSPORT",
"Punkte",
"rabatt",
"Radzio",
"RAILPLUS",
"randomised",
"Ravenstein",
"Referenz",
"referenzen",
"referenziertes",
"Regio",
"Regionalbf",
"regulaere",
"regulaerer",
"reise",
"REISEANGEBOT",
"reiseloesung",
"reisende",
"reisenden",
"REISESTELLENKARTE",
"reisetag",
"Reisezentrum",
"REMENTR",
"REMRESR",
"Reservierbar",
"Reservieren",
"Reservierung",
"reservierungen",
"reservierungs",
"RESERVIERUNGSANGEBOT",
"RESERVIERUNGSENTGELT",
"reservierungspflicht",
"Rhein",
"richtung",
"roehrt",
"Rohrdamm",
"Roigheim",
"Rollstuhl",
"rollstuhlgerechte",
"Römisches",
"Rosenthal",
"Rothenburg",
"Rudow",
"Rueck",
"Rüsselsheim",
"Saale",
"Sammelfaehig",
"satz",
"SBAHN",
"SBAHNEN",
"Scharnweberstr",
"SCHIFF",
"SCHIFFE",
"Schlachthof",
"schnelle",
"Schöneberg",
"Schönefeld",
"Schönhauser",
"Schwedter",
"Seckach",
"Sennfeld",
"servicekategorie",
"SHCARD",
"sich",
"SICT",
"Sieg",
"Siegburg",
"Siemensdamm",
"Siglingen",
"sitzplatz",
"Sitzplatzreservierung",
"slugg",
"Sonnenallee",
"Sören",
"spaeter",
"Sparpreis",
"spezifische",
"Spichernstr",
"Spittelmarkt",
"Stadtgebiet",
"Stadtmitte",
"Stadtpark",
"Steinach",
"Storkower",
"STRASSENBAHN",
"stufe",
"stufenfrei",
"suchbegriff",
"Suche",
"Südkreuz",
"Südstern",
"Sutter",
"tage",
"täglich",
"tapjs",
"tarif",
"Tarifbereiche",
"Tarifgebiet",
"Tarifzone",
"Tauber",
"teilpreis",
"teilstrecken",
"Tempelhof",
"Texte",
"Tiergarten",
"Toel",
"Torfstr",
"Torfstraße",
"Traines",
"traveller",
"Travellers",
"travelling",
"Treptower",
"Tsua",
"tvlr",
"UBAHN",
"ueber",
"uebergreifende",
"ueberschrift",
"Uiffingen",
"Umbuchungen",
"Umstiege",
"umstiegs",
"Umstiegsdauer",
"Umstiegszeit",
"unsere",
"unter",
"Unterwittstadt",
"upsell",
"ursprungs",
"ushrp",
"uuidv",
"Vaihingen",
"vendo",
"verbindung",
"verbindungen",
"verbindungs",
"verbindungsabschnitt",
"verbindungssuche",
"Verfuegbar",
"verfügbar",
"verkehrmittel",
"verkehrsmittel",
"Verkehrstage",
"VERKNUEPFT",
"Verlauf",
"vertraglicher",
"vlexx",
"Voordeelurenabo",
"Vorhanden",
"VORTEILE",
"VORTEILSCARD",
"Vorverkaufszeitraum",
"waehrung",
"wagenreihung",
"wählen",
"Wannsee",
"Wegener",
"Weichselstr",
"wenden",
"wenn",
"Westbahn",
"Westend",
"Westf",
"westhafen",
"Westkreuz",
"wichtig",
"Wilmersdorfer",
"wird",
"Witzleben",
"wochentage",
"Worringen",
"wunsch",
"Wunschplatz",
"Württemberg",
"württembergallee",
"Würzburg",
"Wutzkyallee",
"Yorckstr",
"Yureka",
"Zahlungsarten",
"Zeitpunkt",
"Zeitraum",
"zgck",
"ziel",
"Zimmern",
"Zitadelle",
"Zoologischer",
"Zuege",
"zugart",
"zugattrib",
"zugattribute",
"Zuges",
"zugfahrt",
"zuglaeufe",
"Zuglauf",
"zugnummer",
"zulaessige",
"Zusammenfassung",
"Züttlingen",
"Zwickauer",
"zwischenhalte",
"bestprice",
"intervalle",
"Intervalle",
"tagesbest",
"dbbahnhof",
"Deutschlandticket",
"fahrverguenstigungen",
"cancelation"
],
"ignorePaths": [
"docs/dumps/**",
"test/e2e/fixtures/**",
"test/fixtures/**",
"test/parse/remarks.js",
"test/parse/dbnav-journey.js"
],
"dictionaries": [
"node"
]
}

View file

@ -1,19 +0,0 @@
# `db-vendo-client` API
Also see the [root readme](https://github.com/public-transport/db-vendo-client) for a shortlist of differences of db-vendo-client to hafas-client and of differences between the profiles.
- [`journeys(from, to, [opt])`](journeys.md) get journeys between locations
- [`refreshJourney(refreshToken, [opt])`](refresh-journey.md) fetch up-to-date/more details of a `journey`
- `journeysFromTrip(tripId, previousStopover, to, [opt])` not supported
- [`trip(id, lineName, [opt])`](trip.md) get details for a trip
- `tripsByName(lineNameOrFahrtNr, [opt])` not supported
- [`departures(station, [opt])`](departures.md) query the next departures at a station
- [`arrivals(station, [opt])`](arrivals.md) query the next arrivals at a station
- [`locations(query, [opt])`](locations.md) find stations, POIs and addresses
- [`stop(id, [opt])`](stop.md) get details about a stop/station
- [`nearby(location, [opt])`](nearby.md) show stations & POIs around
- `radar(north, west, south, east, [opt])` not supported
- `reachableFrom(address, [opt])` not supported
- `remarks([opt])` not supported
- `lines(query, [opt])` not supported
- `serverInfo([opt])` not supported

View file

@ -1,3 +1,3 @@
# `arrivals(station, [opt])`
Just like [`departures(station, [opt])`](departures.md), except that it resolves with arrival times instead of departure times.
Just like [`departures(station, [opt])`](departures.md), except that it gives arrival times instead of departure times.

942
docs/changelog.md Normal file
View file

@ -0,0 +1,942 @@
# Changelog
## `5.25.0`
- 0a636981 parse `CHKI` (check-in, check-out) legs ✅
- 7c68f962 `parse{Stopover,JourneyLeg, Trip}`: expose `{arrival,departure}PrognosisType`
- 95af0a01 `parseArrival` & `parseDeparture`: expose `prognosisType`
[🏷 `5.25.0`](https://github.com/public-transport/hafas-client/releases/tag/5.24.0), 2022-07-30
## `5.24.1`
- 492cb7df KVB: provide CA certificate chain via `Agent` 🐛
[🏷 `5.24.1`](https://github.com/public-transport/hafas-client/releases/tag/5.24.1), 2022-06-23
## `5.24.0`
- 2edcd49e `serverInfo()`: add `opt.versionInfo` 📝✅
- f3c2ee6f/1236cf63 DB: support age-based tariffs Thanks @roehrt!
- 7e1f7ed4 BVG/VBB: parse stop DHIDs ✅
- f8ca2d5d BVG: only expand `9*` stop IDs to 12 digits 🐛
- 68ecd7c5 readme: fix `strecken.info` link 📝 Thanks @fhueter!
[🏷 `5.24.0`](https://github.com/public-transport/hafas-client/releases/tag/5.24.0), 2022-04-26
## `5.23.0`
- 57084262 expose `departure.destination` & `arrival.origin`
[🏷 `5.23.0`](https://github.com/public-transport/hafas-client/releases/tag/5.23.0), 2022-02-23
## `5.22.2`
- f6b144f0 BVG: update API endpoint
- e3a02297 `lib/request`: tweak `User-Agent` randomisation logic
- e69d069d/fa9a8d9f update integration tests ✅
- 2ec079ad TPG: add integration test ✅
- 4cd0e9d9 minor tweaks 📝
[🏷 `5.22.2`](https://github.com/public-transport/hafas-client/releases/tag/5.22.2), 2022-01-13
## `5.22.1`
- 2fd06941 use [HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) ⚡️
- 9c10a176 fix request debug-logging 🐛
[🏷 `5.22.1`](https://github.com/public-transport/hafas-client/releases/tag/5.22.1), 2021-12-28
## `5.22.0`
- ed86ad0b add [*KVB* profile](../p/kvb)
- f6733d93 ÖBB: enable `lines()`
[🏷 `5.22.0`](https://github.com/public-transport/hafas-client/releases/tag/5.22.0), 2021-12-08
## `5.21.1`
- f41b8ac4 BVG: update API endpoint 🐛, add new integration test fixtures ✅
[🏷 `5.21.1`](https://github.com/public-transport/hafas-client/releases/tag/5.21.1), 2021-11-18
## `5.21.0`
- 97b6a76e `parseTrip()`: expose `realtimeDataUpdatedAt`
- 3453cbe1 `parseArrival()`/`parseDeparture()`: expose `stbStop.{rem,msg}L` as `remarks[]` as well
- 69ddf5fb/6941e7a4 BVG: parse occupancy ✅
- c270eed9 `nationalExp` -> `nationalExpress` 🐛
- 4492b3a3 use correct `HttpsAgent` option for `LOCAL_ADDRESS` environment variable 🐛
[🏷 `5.21.0`](https://github.com/public-transport/hafas-client/releases/tag/5.21.0), 2021-11-01
## `5.20.2`
- 84c7582a `journeys()`: fix empty `viaLocL[]` 🐛 (#247)
- dd5e4368 fix `departures()`/`arrivals()` without `opt.direction` 🐛
- 3f75e075 BVG: update API endpoint
- 1f6e6810 DB: update `.ext`
- 39d3807c minor tweaks 📝
- 8d4f8a83 E2E/integration tests: update fixtures ✅
[🏷 `5.20.2`](https://github.com/public-transport/hafas-client/releases/tag/5.20.2), 2021-10-26
## `5.20.1`
- 46fb44d0 SNCB: update CA chain 🐛
- f0d33564 `parseTrip()`: handle missing `stopL[]` (on-demand trips) 🐛✅
- fd6a349b `tripsByName()`: more options, add to debug CLI, skipped E2E test ✅📝
- 39ca7ede `tripsByName()`: support some journey filters
- 22a7f16e remove SBB integration/E2E test ✅
- 102c4bf2 BVG/VBB: fix `nearby()` integration/E2E test ✅
- 959e894d E2E/integration tests: un-skip tests, update mocked `when`, update fixtures ✅
[🏷 `5.20.1`](https://github.com/public-transport/hafas-client/releases/tag/5.20.1), 2021-10-24
## `5.20.0`
- 0a096a13 `parseJourneyLeg()`/`parseTrip()`: expose `currentLocation` ✅📝
- b10c1ce6 Rejseplanen `trip()` test: update fixtures ✅
- 6507d5a7 VBB `departures()` test: update fixtures ✅
- c1ee557c `parseArrivalOrDeparture()`: expose `currentTripLocation` ✅📝
[🏷 `5.20.0`](https://github.com/public-transport/hafas-client/releases/tag/5.20.0), 2021-10-18
## `5.19.1`
- 042668ff add [*DART* profile](../p/dart)
- ca75c440 adapt *VBB* profile to server changes
[🏷 `5.19.1`](https://github.com/public-transport/hafas-client/releases/tag/5.19.1), 2021-09-21
## `5.19.0`
- c10f3181 `refreshJourney()`: expose `realtimeDataFrom`
- c3bdcc88 expose `prodCtx.catOut` as `line.productName`
- 21610276 first/last walking leg: handle `dTZOffset`/`aTZOffset` of `0` 🐛
- 6de2dc7b/dd52c4ad DB: fix price parsing 🐛
- 0114f587 adapt E2E tests to latest data ✅
[🏷 `5.19.0`](https://github.com/public-transport/hafas-client/releases/tag/5.19.0), 2021-08-24
## `5.18.0`
- e293223c add [*VVV* profile](../p/vvv)
- f20466c2 add [*IVB* profile](../p/ivb)
- 0ae13b09 add [*STV* profile](../p/stv)
- 649a7ec0 add [*OÖVV* profile](../p/ooevv)
- db2cbfdc add [*VOR* profile](../p/vor)
- 56bd16b5 add [Salzburg profile](../p/salzburg)
- ce828176/aab7babb/0995696c [*DB* profile](../p/db): add [`journeysFromTrip()`](journeys-from-trips.md)
[🏷 `5.18.0`](https://github.com/public-transport/hafas-client/releases/tag/5.18.0), 2021-08-05
## `5.17.0`
- 24c2cc6e add [*BLS* profile](../p/bls)
- 33dab455 add [*TPG* profile](../p/tpg)
[🏷 `5.17.0`](https://github.com/public-transport/hafas-client/releases/tag/5.17.0), 2021-07-28
## `5.16.0`
- 7cb62108 `lib/request`: validate response `content-type` 🐛
- 96b4d55f (re-)upgrade profiles, adapt feature flags
- 62843f79 update profiles' examples 📝
- e9701648 add Rejseplanen profile to the list 📝
- 4557e336 update the "writing a profile" guide 📝
96b4d55f might break your code. Eventually I would have been forced to upgrade the HAFAS protocol version anyways though, so I included this change in the `5.16` minor release.
[🏷 `5.16.0`](https://github.com/public-transport/hafas-client/releases/tag/5.16.0), 2021-05-01
## `5.15.2`
- 7025d3bc ÖBB: fix profile by using `https:` 🐛
- ebe7c595 `lib/request`: fix `DEBUG` env var switch 🐛
- 6f56f152 DB: enable usage of 1st class BahnCard 🐛
- 68d8bf9f/9bfd4566/15be4a0b/b6ad9ba0/d9de0e00 fix readme/docs 📝
[🏷 `5.15.2`](https://github.com/public-transport/hafas-client/releases/tag/5.15.2), 2021-03-26
## `5.15.1`
- 7025d3bc ÖBB: enable `remarks()`
- cb8d92be use `LOCAL_ADDRESS` environment variable to pick network interface address
- 92f1831c `departures()`/`arrivals()`: document `opt.products` 📝
- 7025d3bc/c6fb9661 ÖBB: change `ver` to `1.33` 🐛
- 78bbf9b6 VKG, VVT `departures()`: disable `getPasslist` & `stbFltrEquiv` 🐛
[🏷 `5.15.1`](https://github.com/public-transport/hafas-client/releases/tag/5.15.1), 2021-02-12
## `5.15.0`
- 7106d24a add [*VOS* profile](../p/vos)
- 2ae6a9a4 add [*AVV* profile](../p/avv)
- f47343df add [*BART* profile](../p/bart)
- 2853fb04 add [*VVT* profile](../p/vvt)
- 0690724d add [*VKG* profile](../p/vkg)
- d69d2530 add `profile.remarksGetPolyline` flag
- 51af991e `lib/request`: add `profile.(auth|client|ext|ver)` to request
- 4ee062a1 `lib/request`: allow string `profile.salt`
- c260e34f DB: parse `gridL[].itemL[].remL[]` 🐛
- 33f398bd `parseWarning`: use `fromLocations[0]`
[🏷 `5.15.0`](https://github.com/public-transport/hafas-client/releases/tag/5.15.0), 2021-01-26
## `5.14.0`
- ad6cfd22/3407ad6b/d017e627/02af67e2 add [*mobil.nrw* profile](../p/mobil-nrw)
- 174ed807 `remarks()`: support missing `res.msgL[]` 🐛
- 4efff792 `parseJourney`: use `j.recon.ctx` as `refreshToken` too 🐛
- 86bf3b46 docs: remove "migrating to 4" guide, fix profile examples, minor tweaks 📝
[🏷 `5.14.0`](https://github.com/public-transport/hafas-client/releases/tag/5.14.0), 2021-01-19
## `5.13.0`
- 7444e08/bbf024d/6815c9e add [*SBB* (Switzerland) profile](../p/sbb)
- 17e08ac `parseJourneyLeg``parseAlternative`: handle missing `stopL[]` 🐛, add tests ✅
- 850ec94 *mobiliteit.lu*: fix endpoint, upgrade to version `1.25`
- 54b7d28 *mobiliteit.lu*: fix `national-train` product bitmasks 🐛
[🏷 `5.13.0`](https://github.com/public-transport/hafas-client/releases/tag/5.13.0), 2020-12-27
## `5.12.0`
- 3e6d6d9 add [`serverInfo()` method](server-info.md)
- ed48971/731d9b8 add [`remarks()` method](remarks.md)
- 9d8260b/53e10f7/1a0d97d add [`lines()` method](lines.md)
- e6bc8c6 `departures()`/`arrivals()`: add `line` option
[🏷 `5.12.0`](https://github.com/public-transport/hafas-client/releases/tag/5.12.0), 2020-12-09
## `5.11.0`
- 259fcd7 add [*VRN* (south-west Germany) profile](../p/vrn)
[🏷 `5.11.0`](https://github.com/public-transport/hafas-client/releases/tag/5.11.0), 2020-11-26
## `5.10.1`
- 6d4f29a `nearby()`: support `opt.products`
- 66ff661 `parseJourneyLeg``applyRemarks`: handle legs without `stopovers[]` 🐛
[🏷 `5.10.1`](https://github.com/public-transport/hafas-client/releases/tag/5.10.1), 2020-11-15
## `5.10.0`
- 013ab2d add [*mobiliteit.lu* (Luxembourg) profile](../p/mobiliteit-lu)
- 11ca3b1 add [`tripsByName()` method](trips-by-name.md)
- 92fb29d `parseTrip`: handle `stopL[]` items without `idx` 🐛
[🏷 `5.10.0`](https://github.com/public-transport/hafas-client/releases/tag/5.10.0), 2020-11-01
## `5.9.0`
- 8ed218f add [*Irish Rail* profile](../p/irish-rail)
- de86391 support HTTP proxies via `HTTPS_PROXY` & `HTTP_PROXY` environment variables
[🏷 `5.9.0`](https://github.com/public-transport/hafas-client/releases/tag/5.9.0), 2020-09-24
## `5.8.0`
- 4d06057/82de740/c17bd5a add [*Rejseplanen* profile](../p/rejseplanen)
- 9848dfa RMV: fix product bitmasks 🐛 (by [Adwirawien](https://github.com/Adwirawien))
- 25fb25c `parseLeg`: use remarks without `fIdx`/`tIdx` 🐛
- 68aaad1 *S-Bahn München*: switch to `1.21` protocol
- a621fd6 minor tweaks
- 2d139c8/c9f8cc6/b2a3ce4/e6f25a6/c17bd5a improve/update E2E & integration tests ✅
[🏷 `5.8.0`](https://github.com/public-transport/hafas-client/releases/tag/5.8.0), 2020-09-15
## `5.7.1`
- 2612494 fix platform parsing with some profiles 🐛 (by [em0lar](https://em0lar.de))
[🏷 `5.7.1`](https://github.com/public-transport/hafas-client/releases/tag/5.7.1), 2020-09-09
## `5.7.0`
- b2b1b75/3f4c05d/097557c add [*ZVV* profile](../p/zvv)
- 4fc4c3b fix `H9360` error message 🐛
[🏷 `5.7.0`](https://github.com/public-transport/hafas-client/releases/tag/5.7.0), 2020-08-01
## `5.6.3`
- 71db75d `journeys()`: expose realtime data timestamp
- d2314e0 `journeys()`: don't send `outDate`/`outTime` & `ctxScr`
- f9bfd69 `parseJourneyLeg`: parse `jny.poly` 🐛
- 51f4a66/2c04e2f `journeys()`: remove collection of results
- 6b27517 `parseMovement`: skip invalid `stopL[]` items 🐛
[🏷 `5.6.3`](https://github.com/public-transport/hafas-client/releases/tag/5.6.3), 2020-07-26
## `5.6.2`
- de896b1 `parseCommon`: respect `opt.polyline` 🐛
- fc2e214 ÖBB: add `trip()` test ✅
- dce42bf move trip parsing into `parse/trip`
[🏷 `5.6.2`](https://github.com/public-transport/hafas-client/releases/tag/5.6.2), 2020-06-13
## `5.6.1`
- 542aa8c parse `DEVI` journey legs (#175)
- 3ca4a0c/57fc610 `arrivals()`: add `provenance` field (#180)
- ee94c65 ÖBB: improve `onCall` product name
- a8a9303 `nearby()`: return at most `opt.results` results
[🏷 `5.6.1`](https://github.com/public-transport/hafas-client/releases/tag/5.6.1), 2020-06-10
## `5.6.0`
- 07c77f8/76e3102/1abafb5/d92eb15/0251e31 parse stop/station entrances & sub-stops (#153)
- 9e75f42/0251e31/322004b DB: parse *Reisezentrum* opening hours & station facilities (#153)
[🏷 `5.6.0`](https://github.com/public-transport/hafas-client/releases/tag/5.6.0), 2020-05-21
## `5.5.1`
- 3c888a0 `refreshJourney()`: actually throw the error 🐛, add error code
- e02a20b readme: update links 📝
- b302ba7 minor readme/documentation tweaks 📝
[🏷 `5.5.1`](https://github.com/public-transport/hafas-client/releases/tag/5.5.1), 2020-05-21
## `5.5.0`
- fa3146d/9c4189a add [*SVV* profile](../p/svv)
- e032ec1 "invalid response" error: add `isHafasError: true` flag
- 0699d4d `departures()`/`arrivals()`: let `results` option default to `null`
- 1b01331 use `object-scan@13` ⚡️
[🏷 `5.5.0`](https://github.com/public-transport/hafas-client/releases/tag/5.5.0), 2020-04-09
## `5.4.0`
- 01b3693/17031f3/7d3107e add [*SNCB*/*NMBS* profile](../p/sncb)
- ae74bb4 `departures()`/`arrivals()`: add `results` option
[🏷 `5.4.0`](https://github.com/public-transport/hafas-client/releases/tag/5.4.0), 2020-03-29
## `5.3.1`
- 916ac30 PKP: trim `-` from stop names
- a939090 INSA: `ver` `1.21` -> `1.18` 🐛
- 2cb6a0c `parseIcon()`, `parseHint()`, `parseLocation()`: handle more edge cases 🐛
- 0dceb41 `parseJourneyLeg()`: parse isRchbl correctly 🐛
- 78487d9 `journeys()`: default `earlierRef` & `laterRef` to `null` 🐛
- cda96b6 improve docs 📝
[🏷 `5.3.1`](https://github.com/public-transport/hafas-client/releases/tag/5.3.1), 2020-03-18
## `5.3.0`
- 1c790e1/299b5ac add [*INVG* profile](../p/invg)
- d5116c2/c2b15fa add [*PKP* profile](../p/pkp)
- 682f9f9/8540f5f add [*VBN* profile](../p/vbn)
- 3a9e548/0ea2c01 add [*RMV* profile](../p/rmv)
- 84637b2/522248b add [*RSAG* profile](../p/rsag)
- 86ddf2c add [*VMT* profile](../p/vmt)
[🏷 `5.3.0`](https://github.com/public-transport/hafas-client/releases/tag/5.3.0), 2020-03-12
## `5.2.0`
- 1b03b2e INSA: protocol version `1.21`, enable [`reachableFrom()`](reachable-from.md)
- 2a24137/3ea9380 `parseLocation()`: parse foreign stop IDs
- 3ea9380 `parseLocation()`: parse fare zone, transit authority
- 8c7f164 `parseLine()`: expose admin code
- b9d5c85 add DB & INSA `stop()` tests
[🏷 `5.2.0`](https://github.com/public-transport/hafas-client/releases/tag/5.2.0), 2020-03-08
## `5.1.2`
- e5abe3d DB: fix journey leg loadFactor parsing 🐛
- bc30309 fix undefined variables 🐛
- db94a62/c072a70/df010fc/9874292 add linting
[🏷 `5.1.2`](https://github.com/public-transport/hafas-client/releases/tag/5.1.2), 2020-03-02
## `5.1.1`
- 8cb7d80 improve `findInTree` performance (#152) ⚡️
- 940519b make readme more helpful 📝
- 9522e92 `object-scan@11`
[🏷 `5.1.1`](https://github.com/public-transport/hafas-client/releases/tag/5.1.1), 2020-02-22
## `5.1.0`
- 542a9ee/1c67350/738354d add [*VSN* profile](../p/vsn)
- dfff999 `request()`: add resonse ID to error objects
- c1beb28 `Error` -> `TypeError`
[🏷 `5.1.0`](https://github.com/public-transport/hafas-client/releases/tag/5.1.0), 2020-02-08
## `5.0.4`
- db9287f [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) -> [`google-polyline`](https://www.npmjs.com/package/google-polyline)
- 9b0e55c VBB: accept station IDs with an unknown length 🐛
- ea4912a debug CLI: accept JS objects
[🏷 `5.0.4`](https://github.com/public-transport/hafas-client/releases/tag/5.0.4), 2020-02-03
## `5.0.3`
- 8c6a8d8 `findInTree`: improved performance ⚡️
- c080f32 `vbb-translate-ids@4` 🐛
[🏷 `5.0.3`](https://github.com/public-transport/hafas-client/releases/tag/5.0.3), 2020-01-29
## `5.0.2`
- e049aa3 `parseWarning()`: fix `parseMsgEvent()` 🐛
[🏷 `5.0.2`](https://github.com/public-transport/hafas-client/releases/tag/5.0.2), 2020-01-15
## `5.0.1`
- 51b1e68 `throttle.js`, `retry.js`: use default profile 🐛
[🏷 `5.0.1`](https://github.com/public-transport/hafas-client/releases/tag/5.0.1), 2020-01-15
## `5.0.0`
Note that this version is not backwords-compatible with `4.*`. Check out [the migration guide](migrating-to-5.md).
### breaking changes 💥
- 2f8f82f require Node `>=10`
- 29a2cf3/2b9280e add `plannedArrival`/`plannedDeparture`/`plannedWhen`, `scheduled*` -> `planned*`/`prognosed*`
- 938a6f2/2d1d482 add `plannedArrivalPlatform`/`plannedDeparturePlatform`/`plannedPlatform`, `scheduled*` -> `planned*`/`prognosed*`
- 35e44d4 `parseWarning()`/`parseHint()`: change signature to `(profile, raw, data) => …`
- 4162328 `createClient()`: change signature to `(profile, userAgent, opt = {}) => …`
- fb7a565/252ce5b/9fc6664/2cfee22/e2567ef change parse fns signature to `({profile, opt, res, common}) => (rawData) => …`
- baff692 `journeys()`: don't request nr of results by default
- b8496be DB `journeys()`: let `journey.price` default to `null`
- 6d5c608 call `request()` via `profile`
### features
- f8210c5/9c47a39/0c145d3/9a89cd0 `journeys()`: add `walkingSpeed` option
- a40006f/1afe4ca BVG: support *BerlKönig*, add E2E test
- 352fa2e parse more warning fields
- 8b2a5a8 `parseIcon()`: use `.txt` & `.txtS` as text fallback
- 39a6267 request formatters (e.g. `formatTripReq()`) via `profile`
### bugfixes 🐛
- 5ea22f7 `parseHint()`: parse `.code` & `.text` properly
- 29d7bd4 `parseJourney()`: fix `journey.scheduledDays` year
- 9a6bc2d `parseWarning()`: call `parseDateTime()` via `profile`
- 7b7293e `request()`: use *transformed* `req`
[🏷 `5.0.0`](https://github.com/public-transport/hafas-client/releases/tag/5.0.0), 2020-01-05
## `4.8.0`
- 56dee66/46eadcf/1611635 add [*DB Busradar NRW* profile](../p/db-busradar-nrw)
[🏷 `4.8.0`](https://github.com/public-transport/hafas-client/releases/tag/4.8.0), 2019-12-29
## `4.7.0`
- fceaf86 parse `Q` hints
- c883d96 documentation for `mgate.exe` endpoints
[🏷 `4.7.0`](https://github.com/public-transport/hafas-client/releases/tag/4.7.0), 2019-12-26
## `4.6.2`
- 105c18b DB: always use `rtMode: HYBRID`
[🏷 `4.6.2`](https://github.com/public-transport/hafas-client/releases/tag/4.6.2), 2019-11-18
## `4.6.1`
- 43b4a6e handle `H_UNKNOWN` error
- 1cc453b parseArrOrDep, parseLocation: bugfixes 🐛
[🏷 `4.6.1`](https://github.com/public-transport/hafas-client/releases/tag/4.6.1), 2019-10-28
## `3.10.3`
- c9ceeca put deprecation note
[🏷 `3.10.3`](https://github.com/public-transport/hafas-client/releases/tag/3.10.3), 2019-10-28
## `2.10.4`
- 096f8a0 put deprecation note
[🏷 `2.10.4`](https://github.com/public-transport/hafas-client/releases/tag/2.10.4), 2019-10-28
## `4.6.0`
- 73ca349/19c3ee6 NVV profile
[🏷 `4.6.0`](https://github.com/public-transport/hafas-client/releases/tag/4.6.0), 2019-08-16
## `4.5.2`
- 2e88e96 install-unique client ID via `postinstall` step -> generate process-unique ID
[🏷 `4.5.2`](https://github.com/public-transport/hafas-client/releases/tag/4.5.2), 2019-08-16
## `3.10.2`
- 1babfbf `parseWarning`: handle missing summary/text 🐛
[🏷 `3.10.2`](https://github.com/public-transport/hafas-client/releases/tag/3.10.2), 2019-08-12
## `4.5.1`
- bd7d5bb `parseWarning`: handle missing `summary`/`text` 🐛
- 92c842b DB: enable `radar()`
[🏷 `4.5.1`](https://github.com/public-transport/hafas-client/releases/tag/4.5.1), 2019-07-20
## `4.5.0`
- b144dd5/b57c212 return nice error messages & error codes
[🏷 `4.5.0`](https://github.com/public-transport/hafas-client/releases/tag/4.5.0), 2019-07-08
## `4.4.0`
- e46d6cd `parseLocation`: expose `stop.isMeta`
[🏷 `4.4.0`](https://github.com/public-transport/hafas-client/releases/tag/4.4.0), 2019-06-30
## `4.3.0`
- 1e0182f `parseLint`: use `addName`
- d0f7ca1 follow HTTP redirects, accept `br` encoding
[🏷 `4.3.0`](https://github.com/public-transport/hafas-client/releases/tag/4.3.0), 2019-06-25
## `4.2.2`
- 64f797e `parseProductsBitmask`: fix bitmask handling 🐛
- 707fd29 `p-retry@4`, `p-throttle@3`
[🏷 `4.2.2`](https://github.com/public-transport/hafas-client/releases/tag/4.2.2), 2019-06-25
## `4.2.1`
- 9078d2d fix `leg.reachable`, which was breaking all walking legs 🐛
[🏷 `4.2.1`](https://github.com/public-transport/hafas-client/releases/tag/4.2.1), 2019-06-08
## `4.2.0`
- 6da1e80 add `leg.reachable`
[🏷 `4.2.0`](https://github.com/public-transport/hafas-client/releases/tag/4.2.0), 2019-06-07
## `4.1.1`
- 875ea18 parse scheduled/actual platform information on legs, fixes #116 🐛
- f92e933 [DB](../p/db) departures/arrivals: parse load factor #112
[🏷 `4.1.1`](https://github.com/public-transport/hafas-client/releases/tag/4.1.1), 2019-05-29
## `4.1.0`
- 831bcaf ISO date+time: suppress milliseconds if 0
- 3e01303/75432fc CFG profile
- 820f2ab `parseWarning`: parse products
- 3ab099b/57c7186 HVV profile
[🏷 `4.1.0`](https://github.com/public-transport/hafas-client/releases/tag/4.1.0), 2019-05-27
## `4.0.3`
- 6aa57d4 `parseJourneyLeg`/`parseMovement`/`parseArrival`/`parseDeparture`: handle missing `dirTxt` 🐛
[🏷 `4.0.3`](https://github.com/public-transport/hafas-client/releases/tag/4.0.3), 2019-04-01
## `4.0.2`
- 133cee9 `parseWarning`: expose `warning.id` 🐛
[🏷 `4.0.2`](https://github.com/public-transport/hafas-client/releases/tag/4.0.2), 2019-03-27
## `4.0.1`
- 5d49fd0 `parseDateTime`: fix `tzOffset` & `daysOffset` 🐛
[🏷 `4.0.1`](https://github.com/public-transport/hafas-client/releases/tag/4.0.1), 2019-03-19
## `4.0.0`
This version is not fully backwords-compatible. Check out [the migration guide](migrating-to-4.md).
### breaking changes 💥
- 1e13cf1/b99ceb2 `parseLocation`: strip leading zeros from IDs
- a9fd9ff `parseDateTime`: return ISO string/timestamp
- ca1105f `parseDateTime`: parse timezone offset if given
- bf3c4c5 require Node `>=8.3.0`
- bbff1f4 `movement.nextStops` -> `movement.nextStopovers`
- bad0af8/8b87868/2e12206 rename `station(id)` -> `stop(id)`
- 96ff59d/0daa1c5/88c78c2 `leg.id` -> `leg.tripId`
- 3bc2eff `locations()`: default `opt.results` to `5`
- a1ffad3/cb535cd `parseLine`: remove `line.class` & `line.productCode`
- fcc53b5/b2b2d11/a1c40ad `journeys()`: return object with `journeys`, `earlierRef`, `laterRef`
- 61e7d14 `journeys()`: default `opt.transfers` to `-1`
- d7e439b debugging: `NODE_DEBUG` -> `DEBUG`
- 8f9b22e `locations()`, `nearby()`: `opt.stations` -> `opt.stops`
- a972dad `departures()`/`arrivals()`, `locations()`, `nearby()`, `stop()`: `opt.stationLines` -> `opt.linesOfStops`
- 0e1fcb0/0e1fcb0 `leg.mode: 'walking'` -> `leg.walking: true`
- 567cc98 DB, INSA, Nah.SH, ÖBB: `nationalExp` -> `nationalExpress`
- 9c44995 remove `arrival.trip`/`departure.trip` & `movement.trip`
- eb3ffba/eab850e mark POIs objects with `poi: true`
- 748f8ce `createThrottledClient` -> `withThrottling`
- fbde6a1 `createClientWithRetry` -> `withRetrying`
- 1646173 throw `Error`s -> `TypeError`s
- 7e39a2f/3b0740d `formerScheduled…` -> `scheduled…`
### bugfixes 🐛
- fcc2a23 ÖBB `journeys()`: fix `opt.results`
[🏷 `4.0.0`](https://github.com/public-transport/hafas-client/releases/tag/4.0.0), 2019-02-28
## `3.10.1`
- dafc96a update CMTA credentials
- 46e7729 remove `console.error` call 🐛
[🏷 `3.10.1`](https://github.com/public-transport/hafas-client/releases/tag/3.10.1), 2019-02-28
## `3.10.0`
- d797333/1e16a10 [DB](../p/db): parse additional line names
[🏷 `3.10.0`](https://github.com/public-transport/hafas-client/releases/tag/3.10.0), 2019-02-13
## `3.9.1`
- a145fea extend default retrying options 🐛
[🏷 `3.9.1`](https://github.com/public-transport/hafas-client/releases/tag/3.9.1), 2019-02-08
## `3.9.0`
- b0f786c support for retrying failed requests ✨  [docs](readme.md#retrying-failed-requests)
[🏷 `3.9.0`](https://github.com/public-transport/hafas-client/releases/tag/3.9.0), 2019-02-08
## `3.8.1`
- 3f58d84 handle `stop` objects as input 🐛
[🏷 `3.8.1`](https://github.com/public-transport/hafas-client/releases/tag/3.8.1), 2019-02-06
## `3.8.0`
- 5d0096c `departures()`: profile flag for `getPasslist` & `stbFilterEquiv`
- #99 [Saarfahrplan profile](p/saarfahrplan)  Thanks @ialokim & @juliuste!
[🏷 `3.8.0`](https://github.com/public-transport/hafas-client/releases/tag/3.8.0), 2018-12-31
## `3.7.0`
- e867dac/f097022 `opt.stopovers`, `departure.nextStopovers`/`arrival.previousStopovers`
[🏷 `3.7.0`](https://github.com/public-transport/hafas-client/releases/tag/3.7.0), 2018-12-28
## `3.6.3`
- cb2d298 `stop`s/`station`s: default `id` of `null` 🐛
[🏷 `3.6.3`](https://github.com/public-transport/hafas-client/releases/tag/3.6.3), 2018-12-28
## `3.6.2`
- 5beff47 `radar()`: fix `polylines` option 🐛
- 48424cf `p-throttle` as normal dependency 🐛
[🏷 `3.6.2`](https://github.com/public-transport/hafas-client/releases/tag/3.6.2), 2018-12-16
## `3.6.1`
- b809281 fix error parsing 🐛
- bcbc366/ae2007c/e1f1d0d ÖBB `radar()`: fix filtering of `movement.nextStops` 🐛
[🏷 `3.6.1`](https://github.com/public-transport/hafas-client/releases/tag/3.6.1), 2018-12-10
## `3.6.0`
- 4b56f66 parse `journey.cycle` if returned by HAFAS
- 17b8f14 `journeyLeg.cycle`: parse `nr` field if returned by HAFAS
- 8fac5fc `journeyLeg.alternatives`: parse `direction`, `delay`, `tripId`
[🏷 `3.6.0`](https://github.com/public-transport/hafas-client/releases/tag/3.6.0), 2018-12-03
## `3.5.0`
- 9d96902 `readableFrom()`: make `opt.maxDuration` optional
- 02e0e51 parse scheduled days of a `journey`
[🏷 `3.5.0`](https://github.com/public-transport/hafas-client/releases/tag/3.5.0), 2018-11-13
## `3.4.3`
- 9936466 `p-throttle@2`, `tape-promise@4`
[🏷 `3.4.3`](https://github.com/public-transport/hafas-client/releases/tag/3.4.3), 2018-10-24
## `3.4.2`
- 2a6b0dc speed up date+time formatting ⚡️
[🏷 `3.4.2`](https://github.com/public-transport/hafas-client/releases/tag/3.4.2), 2018-09-24
## `3.4.1`
- 582c9de speed up date+time parsing ⚡️
[🏷 `3.4.1`](https://github.com/public-transport/hafas-client/releases/tag/3.4.1), 2018-09-22
## `3.4.0`
- #81 [S-Bahn München profile](p/sbahn-muenchen)  Thanks @flori-uni!
[🏷 `3.4.0`](https://github.com/public-transport/hafas-client/releases/tag/3.4.0), 2018-09-20
## `3.3.1`
- 035877c `reachableFrom()` retry 🐛
[🏷 `3.3.1`](https://github.com/public-transport/hafas-client/releases/tag/3.3.1), 2018-09-03
## `3.3.0`
- #80/b36ccda `reachableFrom()` method  [docs](reachable-from.md)
[🏷 `3.3.0`](https://github.com/public-transport/hafas-client/releases/tag/3.3.0), 2018-09-03
## `3.2.1`
- 044a5ee `arrivals()`: return a `direction` of `null` :bug:
- b37bedb parse `line.id` if possible
[🏷 `3.2.1`](https://github.com/public-transport/hafas-client/releases/tag/3.2.1), 2018-09-03
## `3.2.0`
- #79 [CapMetro/CMTA profile](p/cmta)  Thanks @nickturskyi!
[🏷 `3.2.0`](https://github.com/public-transport/hafas-client/releases/tag/3.2.0), 2018-08-26
## `3.1.2`
- f796337 handle warnings without schedule `sDate`/`eDate`/`lModDate` 🐛
[🏷 `3.1.2`](https://github.com/public-transport/hafas-client/releases/tag/3.1.2), 2018-08-24
## `3.1.1`
- 39cc2f3 fix install on Windows 🐛
[🏷 `3.1.1`](https://github.com/public-transport/hafas-client/releases/tag/3.1.1), 2018-08-23
## `3.1.0`
- 9257d3a parse `line.fahrtNr`
[🏷 `3.1.0`](https://github.com/public-transport/hafas-client/releases/tag/3.1.0), 2018-08-22
## `3.0.0`
This version is not fully backwords-compatible. Check out [the migration guide](migrating-to-3.md).
### new features ✨
- 2d3796a BVG profile
- 0db84ce #61 parse remarks for stopovers and journey legs
- ac9819b `arrivals()` method  [docs](arrivals.md)
- 5b754aa `refreshJourney()` method  [docs](refresh-journey.md)
- 21c273c `journeys()`/`trip()`: leg stopovers: parse & expose delays
- 021ae45 `journeys()`/`trip()`: leg stopovers: parse & expose platforms
- 84bce0c `arrivals()`/`departures()`: parse & expose platforms
- 85e0bdf `journeys()`: `startWithWalking` option with default `true`
- f6ae29c journey legs with `type: 'walking'` now have a `distance` in meters
- 0d5a8fa departures, arrivals, stopovers: former scheduled platform(s)
- 0199749 `language` option with default `en`
- 1551943 `arrivals()`/`departures()`: `includeRelatedStations` option with default `true`
### breaking changes 💥
- c4935bc new mandatory `User-Agent` parameter
- b7c1ee3 profiles: new products markup ([guide](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/writing-a-profile.md#3-products))
- 40b559f change `radar(n, w, s, e)` signature to `radar({north, west, south, east})`
- 005f3f8 remove `journey.departure`, `journey.arrival`, …
- 0ef0301 validate `opt.when`
- 431574b parse polylines using `profile.parsePolyLine` [docs for the output format](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/journey-leg.md#polyline-option)
- a356a26 throw if 0 products enabled
- c82ad23 `journeys()`: `opt.when``opt.departure`/`opt.arrival`
- 665bed9 rename `location(id)` to `station(id)`
- 6611f26 `journeys()`/`trip()`: `leg.passed``leg.stopovers`
- ebe4fa6 `journeys()`/`trip()`: `opt.passedStations``opt.stopovers`
- 3e672ee `journeys()`/`trip()`: `stopover.station``stopover.stop`
- 2e6aefe journey leg, departure, movement: `journeyId` -> `tripId`
- 8881d8a & b6fbaa5: change parsers signature to `parse…(profile, opt, data)`
- cabe5fa: option to parse & expose `station.lines`, default off
- c8ff217 rename `journeyLeg()` to `trip()`
- 8de4447 rename `profile.journeyLeg` to `profile.trip`
### bugfixes
- dd0a9b2 `parseStopover`: fix first/last canceled stopovers 🐛
[🏷 `3.0.0`](https://github.com/public-transport/hafas-client/releases/tag/3.0.0), 2018-08-17
## `2.10.3`
- 50bd440 better `User-Agent` randomization
[🏷 `2.10.3`](https://github.com/public-transport/hafas-client/releases/tag/2.10.3), 2018-08-08
## `2.10.2`
- d54c26d randomize `User-Agent`
[🏷 `2.10.2`](https://github.com/public-transport/hafas-client/releases/tag/2.10.2), 2018-07-25
## `2.10.1`
- 04d550f parse `TRSF` legs as `walking` 🐛
[🏷 `2.10.1`](https://github.com/public-transport/hafas-client/releases/tag/2.10.1), 2018-07-02
## `2.10.0`
- 4da8689 journey legs with `type: 'walking'` now have a `distance` in meters
- c1bdade `departures()`: parse & expose platforms
- fccd3d0 `journeys()`: `startWithWalking` option
[🏷 `2.10.0`](https://github.com/public-transport/hafas-client/releases/tag/2.10.0), 2018-06-30
## `2.9.1`
- a952b08 notes on how to use `hafas-client` with react-native 📝
- 38a3749 `parseStopover`: fix first/last canceled stopovers 🐛
## `2.9.0`
- 49186ae journey leg passed stations: add `arrivalDelay` & `departureDelay`
- deb8829 [`journeys()`](journeys.md): new `whenRepresents` option
- f3d8304 let the `insa` and `nahsh` profiles use HTTPS
[🏷 `2.9.0`](https://github.com/public-transport/hafas-client/releases/tag/2.9.0), 2018-06-20
## `2.8.1`
- 769f2e3 send `Accept: application/json`
[🏷 `2.8.1`](https://github.com/public-transport/hafas-client/releases/tag/2.8.1), 2018-06-07
## `2.8.0`
- 16c3f01 enable [`journeyLeg()`](journey-leg.md) for [DB](../p/db)
[🏷 `2.8.0`](https://github.com/public-transport/hafas-client/releases/tag/2.8.0), 2018-05-24
## `2.7.5`
- 908d531 [DB](../p/db) [`journeys()`](journeys.md): fix polylines parsing 🐛
[🏷 `2.7.5`](https://github.com/public-transport/hafas-client/releases/tag/2.7.5), 2018-05-24
## `2.7.4`
- 709b7b4 update dependencies
[🏷 `2.7.4`](https://github.com/public-transport/hafas-client/releases/tag/2.7.4), 2018-05-24
## `2.7.3`
- 48f2cef each movement from `radar()` now has a `journeyId` field
[🏷 `2.7.3`](https://github.com/public-transport/hafas-client/releases/tag/2.7.3), 2018-05-21
## `2.7.2`
- a97e0d3 fix polylines parsing 🐛
[🏷 `2.7.2`](https://github.com/public-transport/hafas-client/releases/tag/2.7.2), 2018-05-16
## `2.7.1`
- aa480e0 fix polylines parsing 🐛
[🏷 `2.7.1`](https://github.com/public-transport/hafas-client/releases/tag/2.7.1), 2018-05-16
## `2.7.0`
- `journeys()`: `polylines` option
- `journeyLeg()`: `polyline` option
- `radar()`: `polylines` option
[🏷 `2.7.0`](https://github.com/public-transport/hafas-client/releases/tag/2.7.0), 2018-05-15
## `2.6.0`
- 5d10d76 journey legs: parse cycle
[🏷 `2.6.0`](https://github.com/public-transport/hafas-client/releases/tag/2.6.0), 2018-04-29
## `2.5.3`
- d676b84 fix parsing for journey leg alternatives 🐛
[🏷 `2.5.3`](https://github.com/public-transport/hafas-client/releases/tag/2.5.3), 2018-04-29
## `2.5.2`
- 16e6dd6 departure docs: fix method 📝
- c60213a DB: tram mode should be `train` 🐛
[🏷 `2.5.2`](https://github.com/public-transport/hafas-client/releases/tag/2.5.2), 2018-04-24
## `2.5.1`
- afc0124 fix stopover parsing 🐛
[🏷 `2.5.1`](https://github.com/public-transport/hafas-client/releases/tag/2.5.1), 2018-04-05
## `2.5.0`
- new [Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) [profile](../p/nahsh)
- new [*writing a profile* guide](./writing-a-profile.md)
[🏷 `2.5.0`](https://github.com/public-transport/hafas-client/releases/tag/2.5.0), 2018-03-18
## `2.4.2`
- `parseStopover`: expose canceled arrivals & departures 🐛
[🏷 `2.4.2`](https://github.com/public-transport/hafas-client/releases/tag/2.4.2), 2018-03-17
## `2.4.1`
- new [*writing a profile* guide](./writing-a-profile.md)
- `parseMovement`: use `parseStopover` 🐛
- `parseStopover`: use `parseStationName` 🐛
[🏷 `2.4.1`](https://github.com/public-transport/hafas-client/releases/tag/2.4.1), 2018-03-17
## `2.4.0`
- new [Nahverkehr Sachsen-Anhalt (NASA)](https://de.wikipedia.org/wiki/Nahverkehrsservice_Sachsen-Anhalt)/[INSA](https://insa.de) profile
- new `earlierRef`/`laterRef` feature to query earlier/later journeys (pagination)
- former scheduled date & time for canceled departures & journeys
[🏷 `2.4.0`](https://github.com/public-transport/hafas-client/releases/tag/2.4.0), 2018-03-14

View file

@ -1,90 +0,0 @@
# New DB Board and Route Planning APIs (beyond HAFAS and IRIS)
(Beware that a DB journey is what you usually call a trip (a vehicle travelling at a certain time) and a DB trip is what you usually call a journey (result of a route search from A to B).)
## RIS::Boards
https://apis.deutschebahn.com/db/apis/ris-boards/v1/public/
EPs:
* departures/<evaNo>
* arrivals/<evaNo>
Notes:
* docs (also helpful for other RIS-based APIs below): https://developers.deutschebahn.com/db-api-marketplace/apis/product/ris-boards-transporteure/api/ris-boards-transporteure#/RISBoards_151/overview
* needs an API Key
* provides remarks
* does not provide loadFactor
* no route planning
* uses RIS trip IDs
* boards up to 12 hours
## bahnhof.de RIS
https://www.bahnhof.de/api/boards/departures?evaNumbers=8000105&filterTransports=BUS&duration=60&locale=de
Notes:
* no API Key needed
* provides remarks
* uses RIS trip IDs
* no route planning
* boards up to 6 hours, only from current time (or unknown parameter)
## Regio Guide RIS
https://regio-guide.de/@prd/zupo-travel-information/api/public/ri/
EPs:
* departure/8000105?modeOfTransport=HIGH_SPEED_TRAIN,REGIONAL_TRAIN,CITY_TRAIN,INTER_REGIONAL_TRAIN,UNKNOWN,BUS,TRAM,SUBWAY&timeStart=2024-12-11T15:08:25.678Z&timeEnd=2024-12-12T01:53:25.678&expandTimeFrame=TIME_END&&occupancy=true
* board/arrival/<evaNo>
* routing-search (with POST body, see regio-guide.de)
* trip/<tripId-from-routing-search>
* journey/<journeyId-from-trip>
Notes:
* no API Key needed
* no remarks in boards (or with unknown param), only some in journey
* cancelled trips are completely missing from boards (?)
* uses RIS trip IDs, does not expose them directly in the routing-search response
* loadFactor for some regional services, not for long distance services
* boards up to 12 hours
* routing-search returns polylines (!)
## Vendo/Movas Navigator API
https://app.vendo.noncd.db.de/mob/
EPs:
* bahnhofstafel/abfahrt
* bahnhofstafel/ankunft
* location/search
* angebote/fahrplan (for route planning)
* zuglauf
* zuglaeufe/ICE_947/halte/by-abfahrt/8000207_2024 (coach sequence)
* angebote/recon (tickets)
* trip/recon (polylines)
Notes:
* see [traffic dumps](dumps/)
* no API Key needed
* used by new DB Navigator
* HAFAS trip IDs
* boards only 1 hour (or unknown param)
* does not contain machine-readable cancelled info in the boards (only "Halt entfällt" string), but contains relevant remarks
* loadFactor only on journeys (?)
* polylines only for zuglauf and trip/recon
* limited remarks on boards
## Vendo/Movas bahn.de API
https://int.bahn.de/web/api/
EPs:
* angebote/fahrplan (for route planning)
* reiseloesung/orte
* reiseloesung/orte/nearby
* reiseloesung/verbindung
* reiseloesung/fahrt
* reiseloesung/abfahrten?datum=2024-12-30&zeit=11:55:00&ortExtId=8011160&ortId=A%3D1%40O%3DBerlin+Hbf%40X%3D13369549%40Y%3D52525589%40U%3D80%40L%3D8011160%40i%3DU%C3%97008065969%40&mitVias=true&maxVias=8&verkehrsmittel[]=ICE&verkehrsmittel[]=EC_IC&verkehrsmittel[]=IR&verkehrsmittel[]=REGIONAL
* reiseloesung/ankuenfte
Notes:
* no API Key needed
* uses HAFAS trip IDs
* provides loadFactor
* polylines only for /verbindung and /fahrt

View file

@ -24,50 +24,54 @@ With `opt`, you can override the default options, which look like this:
```js
{
when: new Date(),
direction: null, // only supported in `dbweb` and with `enrichStations=true` (experimental)
line: null, // not supported
direction: null, // only show departures heading to this station
line: null, // filter by line ID
duration: 10, // show departures for the next n minutes
results: null, // max. number of results; `null` means "whatever HAFAS wants"
subStops: true, // not supported
entrances: true, // not supported
linesOfStops: false, // not supported
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
linesOfStops: false, // parse & expose lines at the stop/station?
remarks: true, // parse & expose hints & warnings?
stopovers: false, // fetch & parse previous/next stopovers?, only supported with `dbweb` profile
stopovers: false, // fetch & parse previous/next stopovers?
// departures at related stations
// e.g. those that belong together on the metro map.
includeRelatedStations: true,
moreStops: null // also include departures/arrivals for array of up to nine additional station evaNumbers (not supported with dbnav and dbweb)
language: 'en' // language to get results in
}
```
The maximum supported duration depends on the profile (see main readme), e.g. 720 for `db` and 60 for `dbnav`. In order to use the `dbris` profile, you need to pass the env vars `DB_API_KEY` and `DB_CLIENT_ID`.
If you pass an object `opt.products`, its fields will partially override the default products defined in the profile.
If you pass an object `opt.products`, its fields will partially override the default products defined in the profile. An example with the [BVG profile](../p/bvg):
```js
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
// will query with these products: suburban, subway, bus, express, regional
client.departures('900000024101', {products: {tram: false, ferry: false}})
```
## Response
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the `when` field includes the current delay. The `delay` field, if present, expresses how much the former differs from the schedule.
You may pass a departure's `tripId` into [`trip(id, lineName, [opt])`](trip.md) to get details on the whole trip. For the `dbnav`/`dbweb` profile HAFAS trip ids will be returned, for the `db` profile, RIS trip ids will be returned, then the `trip()` endpoint supports both id types.
You may pass the `tripId` field into [`trip(id, lineName, [opt])`](trip.md) to get details on the vehicle's trip.
For `db` profile, cancelled trips will not be contained in the response! For the `db` and `dbnav` profile, only the most important remarks will be contained in the boards.
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} from 'db-vendo-client'
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbnavProfile, userAgent)
const client = createClient(vbbProfile, 'my-awesome-program')
// S Charlottenburg
const {
departures,
realtimeDataUpdatedAt,
} = await client.departures('8089165', {duration: 3})
client.departures('900000024101', {duration: 3})
.then(console.log)
.catch(console.error)
```
`realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
`departures` may look like this:
The response may look like this:
```js
[ {
@ -77,11 +81,11 @@ const {
// Depending on the HAFAS endpoint, the destination may be present:
destination: {
type: 'stop',
id: '8089165',
id: '900000029101',
name: 'S Spandau',
location: {
type: 'location',
id: '8089165',
id: '900029101',
latitude: 52.534794,
longitude: 13.197477
},

View file

@ -1,18 +0,0 @@
POST /mob/angebote/fahrplan HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
x-feature-reiseketten-enabled: false
X-Correlation-ID: 68f7ceba-70e7-4a88-b9b0-454809655314_3fbcc823-2a69-46f4-9484-6d94b1c0116a
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 4779d837-2aa8-4613-9904-8f950367c3c0
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 1120
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dMünchen Hbf@X\u003d11558339@Y\u003d48140229@U\u003d81@L\u003d8000261@B\u003d1@p\u003d1734722398@i\u003dU×008020347@","verkehrsmittel":["ALL"],"zeitWunsch":{"reiseDatum":"2025-01-02T15:21:31.877957+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dStuttgart Hbf@X\u003d9181636@Y\u003d48784081@U\u003d81@L\u003d8000096@B\u003d1@p\u003d1734722398@i\u003dU×008029034@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["CH-GENERAL-ABONNEMENT KLASSE_2","CH-GENERAL-ABONNEMENT KLASSE_1","CH-HALBTAXABO_OHNE_RAILPLUS KLASSENLOS","A-VORTEILSCARD KLASSENLOS"],"reisendenTyp":"SENIOR"},{"ermaessigungen":["CH-GENERAL-ABONNEMENT KLASSE_2","CH-GENERAL-ABONNEMENT KLASSE_1","CH-HALBTAXABO_OHNE_RAILPLUS KLASSENLOS","A-VORTEILSCARD KLASSENLOS"],"reisendenTyp":"SENIOR"},{"ermaessigungen":["KLIMATICKET_OE KLASSENLOS","NL-40_OHNE_RAILPLUS KLASSENLOS","BAHNCARD100 KLASSE_1","BAHNCARD100 KLASSE_2"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false}

View file

@ -1,17 +0,0 @@
POST /mob/angebote/recon/autonomereservierung HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
X-Correlation-ID: 68f7ceba-70e7-4a88-b9b0-454809655314_3fbcc823-2a69-46f4-9484-6d94b1c0116a
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 7d70a4fc-751f-4893-b2e2-11c925c0538b
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 1872
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reisendenProfil":{"reisende":[{"alter":63,"ermaessigungen":["KLIMATICKET_OE KLASSENLOS","NL-40_OHNE_RAILPLUS KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false,"verbindungHin":{"kontext":"¶HKI¶T$A\u003d1@O\u003dMünchen Hbf@X\u003d11558339@Y\u003d48140229@L\u003d8000261@a\u003d128@$A\u003d1@O\u003dStuttgart Hbf@X\u003d9181636@Y\u003d48784081@L\u003d8000096@a\u003d128@$202501021628$202501021832$ICE 912$$1$$$$$$§T$A\u003d1@O\u003dStuttgart Hbf@X\u003d9181636@Y\u003d48784081@L\u003d8000096@a\u003d128@$A\u003d1@O\u003dParis Est@X\u003d2359120@Y\u003d48876976@L\u003d8700011@a\u003d128@$202501021852$202501022214$TGV 9570$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzIjSElOIzAjRUNLIzU2NDAyOHw1NjQwMjh8NTY0Mzc0fDU2NDM3NHwwfDB8NTY1fDU2Mzk1OHwxfDB8MjZ8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMyMDEyNTE2MjgjClojVk4jMSNTVCMxNzM0NzIyMzk4I1BJIzEjWkkjMTk3Mzc3I1RBIzAjREEjMjAxMjUjMVMjODAwMDI2MSMxVCMxNjI4I0xTIzgwMDAwODAjTFQjMjIwMyNQVSM4MSNSVCMxI0NBI0lDRSNaRSM5MTIjWkIjSUNFICA5MTIjUEMjMCNGUiM4MDAwMjYxI0ZUIzE2MjgjVE8jODAwMDA5NiNUVCMxODMyIwpaI1ZOIzEjU1QjMTczNDcyMjM5OCNQSSMxI1pJIzIzOTQ0OSNUQSMwI0RBIzIwMTI1IzFTIzgwMDAwOTYjMVQjMTg1MiNMUyM4NzAwMDExI0xUIzIyMTQjUFUjODEjUlQjMSNDQSNSSFQjWkUjOTU3MCNaQiNUR1YgOTU3MCNQQyMwI0ZSIzgwMDAwOTYjRlQjMTg1MiNUTyM4NzAwMDExI1RUIzIyMTQj¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32PS07DMBiEr1J5XarfTtM8JEsmDRWgAhGiCIRYhMZtg/IotlMRRTkHl2HXi/EnEWxA7Dzj8XzjhhykIj6hE8clYyLfDYowmNyHEw+1km/Eb0hR5Qvi2+PuEBAfxqSsTBgbiWEGzAYKjPTmXZp3JrUZBUBr0zec0DF5LepFZtSS+E8NMfW+i0W3NyGG8jLp1MX1HMUhzqq+AphF2ud+03y3HYqRnMj9slwPNVmaYPKUU3HDr46fxXoni9H5y0Y8cEpt27UsTzzyqUunwJgnVtylYsldAGAzKgJ8t+fUsaYOY5bnipSvjh8ALjBAT+AYbYZPLvolsVJ/oqNYpXp0pg1ymWV7lEGPdZ2Z58x+sA5y6T9Yh1IL4Dd2K01UZnWWFugZVcneuiwrVcg6KKsi0cTfxJkeLqJY6yzV5jsr1yUOjHMMNW3bfgFUarsn8gEAAA\u003d\u003d"}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,18 +0,0 @@
POST /mob/angebote/recon HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
x-feature-reiseketten-enabled: false
X-Correlation-ID: e1927e98-0d8c-45f2-a161-965622ccd56a_e8cf6ea4-4103-4707-aa44-2287646a87f9
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: dcf2d458-4e11-4e40-a6e3-8922544387e8
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 2749
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reisendenProfil":{"reisende":[{"ermaessigungen":["KEINE_ERMAESSIGUNG KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false,"verbindungHin":{"kontext":"¶HKI¶T$A\u003d1@O\u003dMünchen Hbf@X\u003d11558339@Y\u003d48140229@L\u003d8000261@a\u003d128@$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$202501030000$202501030438$ICE 618$$1$$$$$$§T$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$A\u003d1@O\u003dKassel-Wilhelmshöhe@X\u003d9447114@Y\u003d51312558@L\u003d8003200@a\u003d128@$202501030512$202501030644$ICE 1088$$1$$$$$$§T$A\u003d1@O\u003dKassel-Wilhelmshöhe@X\u003d9447114@Y\u003d51312558@L\u003d8003200@a\u003d128@$A\u003d1@O\u003dHamm(Westf)Hbf@X\u003d7807824@Y\u003d51678077@L\u003d8000149@a\u003d128@$202501030703$202501030850$RE 26708$$1$$$$$$§T$A\u003d1@O\u003dHamm(Westf)Hbf@X\u003d7807824@Y\u003d51678077@L\u003d8000149@a\u003d128@$A\u003d1@O\u003dMünster(Westf)Hbf@X\u003d7635716@Y\u003d51956563@L\u003d8000263@a\u003d128@$202501030859$202501030922$RE 32916$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzQ1MzE2I0hJTiMwI0VDSyM1NjQ0ODB8NTY0NDgwfDU2NDk1NHw1NjUwNDJ8MHwwfDU2NXw1NjQ0MTN8NHwwfDh8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMzMDEyNTAwMDAjClojVk4jMSNTVCMxNzM0NzIyMzk4I1BJIzEjWkkjMTk1OTUyI1RBIzAjREEjMzAxMjUjMVMjODAwMDI2MSMxVCMwI0xTIzgwMDAxOTkjTFQjMTEyMyNQVSM4MSNSVCMxI0NBI0lDRSNaRSM2MTgjWkIjSUNFICA2MTgjUEMjMCNGUiM4MDAwMjYxI0ZUIzAjVE8jODAwMDEwNSNUVCM0MzgjClojVk4jMSNTVCMxNzM0NzIyMzk4I1BJIzEjWkkjMTkyODc1I1RBIzAjREEjMzAxMjUjMVMjODAwMDEwNSMxVCM1MTIjTFMjODAwMDE5OSNMVCMxMDE4I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzEwODgjWkIjSUNFIDEwODgjUEMjMCNGUiM4MDAwMTA1I0ZUIzUxMiNUTyM4MDAzMjAwI1RUIzY0NCMKWiNWTiMxI1NUIzE3MzQ3MjIzOTgjUEkjMSNaSSMyMTg2NjIjVEEjMCNEQSMzMDEyNSMxUyM4MDAzMjAwIzFUIzcwMyNMUyM4MDAwMTQ5I0xUIzg1MCNQVSM4MSNSVCMxI0NBI0RQTiNaRSMyNjcwOCNaQiNSRSAyNjcwOCNQQyMzI0ZSIzgwMDMyMDAjRlQjNzAzI1RPIzgwMDAxNDkjVFQjODUwIwpaI1ZOIzEjU1QjMTczNDcyMjM5OCNQSSMxI1pJIzIxNzk5NiNUQSMwI0RBIzMwMTI1IzFTIzgwMDAxNzEjMVQjODQzI0xTIzgwMDAzMTYjTFQjOTUxI1BVIzgxI1JUIzEjQ0EjRFBOI1pFIzMyOTE2I1pCI1JFIDMyOTE2I1BDIzMjRlIjODAwMDE0OSNGVCM4NTkjVE8jODAwMDI2MyNUVCM5MjIj¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P3UrDMACFX0VypVBHfpr+QSB2ZahMV8T5g3hR13SrdO1M0mEpfQ5fxru9mGmLFyJ6l3Nycr6TFuyFBAFAE9cDFhDv2ogonNxFE99oKd5A0IKy3s5AQK3+EIIAWqCqdZRoYcIYYgoRxGAwb/PtYGKbQGisbGg4RRZ4LZtZoeUcBE8t0M2uj8U3i8iEtlXaq4vrqRH7pKh7ZSoJ6J6HTdPNeiw25FTs5tVqrCny1CTPGOILdnX4LFcbUR6dv2T8gSFEqUeIzx+Z7SEbYuzzJfMQnzMPQogdxEPzbseQS2wXY+J7PGfLwweEHsTQeNyMUXr85GxYkkj5J1ppIY/vhdLZyTjAdQh1kWP4FPnUoQ75ySf/8RFE6Dd/LXRcFU2Rl8bTshaDdVnVshRNWNVlqkCQJYUaL+JEqSJX+jsrVlWcyGRrQm3XdV8RCJMF+wEAAA\u003d\u003d"}}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
POST /mob/location/search HTTP/1.1
Accept: application/x.db.vendo.mob.location.v3+json
X-Correlation-ID: 4f2e274e-6e5c-4711-9125-87229046bba3_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 560187ed-f13a-4654-bc62-b9f69f989563
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 45
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"locationTypes":["ALL"],"searchTerm":"test"}

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Mon, 16 Dec 2024 18:29:33 GMT
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 2994
Connection: keep-alive
server-timing: intid;desc=a0c42ccdea3bbb18
Server-Timing: intid;desc=a0c42ccdea3bbb18
Server-Timing: intid;desc=a0c42ccdea3bbb18
x-correlation-id: 4f2e274e-6e5c-4711-9125-87229046bba3_64466773-556f-4aa8-b128-0948c4d60887
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=a0c42ccdea3bbb18
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd1e15efa531633ee310f49108472c2b1a482b002a4d22b5cf935d3d6970510c0b134616b579c639af340fe261746662484; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
[{"name":"Tessin West","stationId":"7983","locationId":"A=1@O=Tessin West@X=12442572@Y=54034438@U=81@L=8079604@B=1@p=1734031727@i=U×008030295@","evaNr":"8079604","coordinates":{"latitude":54.0344,"longitude":12.442203},"weight":1489,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Testa Grigia","locationId":"A=1@O=Testa Grigia@X=7707540@Y=45934474@U=81@L=8511303@B=1@p=1734031727@i=U×008511303@","evaNr":"8511303","coordinates":{"latitude":45.934475,"longitude":7.70754},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Teschenhagen","stationId":"6172","locationId":"A=1@O=Teschenhagen@X=13374232@Y=54389368@U=81@L=8013104@B=1@p=1734031727@i=U×008028497@","evaNr":"8013104","coordinates":{"latitude":54.38936,"longitude":13.374196},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Testelt","locationId":"A=1@O=Testelt@X=4946863@Y=51009783@U=81@L=8800244@B=1@p=1734031727@i=U×008833266@","evaNr":"8800244","coordinates":{"latitude":51.009785,"longitude":4.946863},"weight":2627,"products":["INTERCITYUNDEUROCITYZUEGE","NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Tessin","stationId":"6174","locationId":"A=1@O=Tessin@X=12462618@Y=54032020@U=81@L=8013106@B=1@p=1734031727@i=U×008027109@","evaNr":"8013106","coordinates":{"latitude":54.032,"longitude":12.461656},"weight":2402,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Chemnitz Zentralhaltestelle","locationId":"A=1@O=Chemnitz Zentralhaltestelle@X=12922263@Y=50831626@U=81@L=8017419@B=1@p=1734031727@i=U×008042918@","evaNr":"8017419","coordinates":{"latitude":50.831627,"longitude":12.922263},"weight":5813,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Teschow","stationId":"6173","locationId":"A=1@O=Teschow@X=11637956@Y=53994355@U=81@L=8013105@B=1@p=1734031727@i=U×008027118@","evaNr":"8013105","coordinates":{"latitude":53.994175,"longitude":11.637687},"weight":1524,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Tesperhude Hudehof, Geesthacht","locationId":"A=1@O=Tesperhude Hudehof, Geesthacht@X=10431253@Y=53404393@U=81@L=694334@B=1@p=1734031727@","evaNr":"694334","coordinates":{"latitude":53.404392,"longitude":10.431253},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Tesperhude Strandweg, Geesthacht","locationId":"A=1@O=Tesperhude Strandweg, Geesthacht@X=10427424@Y=53402316@U=81@L=694333@B=1@p=1734031727@","evaNr":"694333","coordinates":{"latitude":53.402317,"longitude":10.427424},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Testorf Umspannwerk, Wangels","locationId":"A=1@O=Testorf Umspannwerk, Wangels@X=10778516@Y=54250161@U=81@L=700240@B=1@p=1734031727@","evaNr":"700240","coordinates":{"latitude":54.25016,"longitude":10.778516},"weight":220,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"}]

View file

@ -1,18 +0,0 @@
POST /mob/angebote/fahrplan HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
x-feature-reiseketten-enabled: false
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 0d0c6c50-3bea-4cb7-b008-09bc92a0d96a
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 679
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@","verkehrsmittel":["ALL"],"zeitWunsch":{"reiseDatum":"2024-12-16T19:28:48.659812+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@B\u003d1@p\u003d1734031727@i\u003dU×008015458@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["KEINE_ERMAESSIGUNG KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
POST /mob/trip/recon HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 757b9806-028b-4b0e-bdde-2441c2e1e1ee
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 1162
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"reconCtx":"¶HKI¶T$A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@L\u003d8011160@a\u003d128@$A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@L\u003d8000207@a\u003d128@$202412161946$202412170057$ICE 842$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzEjSElOIzAjRUNLIzUzOTc0Nnw1Mzk3NDZ8NTQwMDU3fDU0MDA1N3wwfDB8NTY1fDUzOTcyNXwxfDB8MTA1MHwwfDB8LTIxNDc0ODM2NDgjR0FNIzE2MTIyNDE5NDYjClojVk4jMSNTVCMxNzM0MDMxNzI3I1BJIzEjWkkjMTc3NjUxI1RBIzAjREEjMTYxMjI0IzFTIzgwMTAyNTUjMVQjMTkzNCNMUyM4MDAwMjA3I0xUIzEwMDU3I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzg0MiNaQiNJQ0UgIDg0MiNQQyMwI0ZSIzgwMTExNjAjRlQjMTk0NiNUTyM4MDAwMjA3I1RUIzEwMDU3Iw\u003d\u003d¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P7UrDMBiFb0Xyu4436WcKgdiV4cfQIk4U8Udds1lJ25mmw1J6Hd6JN7AbM20ZCIrkT87JyXnet0N7oVCI8MwPkIXEhzYijmb38YwarcQ7CjtUNsUCha41XCIUgoWqRsepFiZMgDiYYA+N5l1eDCamJAAw1mZsOMUWeivbhdRqicKnDul2N8SS25vYhIoqG9TF9dyIfSqbsQKIjfrncab563YqNuRM7JbVeqqReWaSZwzzGxYJJfPy5Pxlwx8Ytm2Pug7lj8wl5rgB5SsWYL5kAWCMPeCR+bVj2LcdsLFPfJ6z1eETIADPpR7lZpRaTysuxjlSpf4EXx2+5JFroIFvw4AF6thAfmABCPj/YLHruMFv7FbopJKt2c14WjVitC6rRpWijaqmzGoUblJZTw9JWtcyr/UxK9ZVkqq0MKGu7/tvvH4lCvABAAA\u003d"}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
POST /mob/trip/recon HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: f30d7ef5-d9a2-428b-acc2-e8bb68c7d705
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 2507
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"reconCtx":"¶HKI¶T$A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@L\u003d8098160@a\u003d128@$A\u003d1@O\u003dHannover Hbf@X\u003d9741017@Y\u003d52376764@L\u003d8000152@a\u003d128@$202412162128$202412162322$ICE 840$$1$$$$$$§T$A\u003d1@O\u003dHannover Hbf@X\u003d9741017@Y\u003d52376764@L\u003d8000152@a\u003d128@$A\u003d1@O\u003dHanau Hbf@X\u003d8929003@Y\u003d50120957@L\u003d8000150@a\u003d128@$202412170030$202412170414$IC 60471$$1$$$$$$§T$A\u003d1@O\u003dHanau Hbf@X\u003d8929003@Y\u003d50120957@L\u003d8000150@a\u003d128@$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$202412170455$202412170515$RB 15501$$1$$$$$$§T$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@L\u003d8000207@a\u003d128@$202412170526$202412170633$ICE 222$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzI2MCNISU4jNDU1I0VDSyM1Mzk4NDh8NTM5ODQ4fDU0MDM5M3w1NDAzOTN8MHwwfDU2NXw1Mzk4NDh8NHwwfDE4fDB8MHwtMjE0NzQ4MzY0OCNHQU0jMTYxMjI0MjEyOCMKWiNWTiMxI1NUIzE3MzQwMzE3MjcjUEkjMSNaSSMxNzc2MTkjVEEjMCNEQSMxNjEyMjQjMVMjODA5ODE2MCMxVCMyMTI4I0xTIzgwMDAxNTIjTFQjMjMyMiNQVSM4MSNSVCMxI0NBI0lDRSNaRSM4NDAjWkIjSUNFICA4NDAjUEMjMCNGUiM4MDk4MTYwI0ZUIzIxMjgjVE8jODAwMDE1MiNUVCMyMzIyIwpaI1ZOIzEjU1QjMTczNDAzMTcyNyNQSSMxI1pJIzE3MzY1MSNUQSMwI0RBIzE2MTIyNCMxUyM4MDAyNTUzIzFUIzIxNTIjTFMjODUwMzAwMCNMVCMxMTAwNSNQVSM4MSNSVCMxI0NBI0lDI1pFIzYwNDcxI1pCI0lDIDYwNDcxI1BDIzEjRlIjODAwMDE1MiNGVCMxMDAzMCNUTyM4MDAwMTUwI1RUIzEwNDE0IwpaI1ZOIzEjU1QjMTczNDAzMTcyNyNQSSMxI1pJIzE4NzYxOCNUQSMwI0RBIzE3MTIyNCMxUyM4MDA2MTMyIzFUIzQxOCNMUyM4MDAwMTA1I0xUIzUxNSNQVSM4MSNSVCMxI0NBI1JCI1pFIzE1NTAxI1pCI1JCIDE1NTAxI1BDIzMjRlIjODAwMDE1MCNGVCM0NTUjVE8jODAwMDEwNSNUVCM1MTUjClojVk4jMSNTVCMxNzM0MDMxNzI3I1BJIzEjWkkjMjExNDMzI1RBIzAjREEjMTcxMjI0IzFTIzgwMDAxMDUjMVQjNTI2I0xTIzg0MDAwNTgjTFQjOTI5I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzIyMiNaQiNJQ0UgIDIyMiNQQyMwI0ZSIzgwMDAxMDUjRlQjNTI2I1RPIzgwMDAyMDcjVFQjNjMzIw\u003d\u003d¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P7UrDMBiFb0Xyu4436WcKgdiV4cfQIk4U8Udds1lJ25mmw1J6Hd6JN7AbM20ZCIrkT87JyXnet0N7oVCI8MwPkIXEhzYijmb38YwarcQ7CjtUNsUCha41XCIUgoWqRsepFiZMgDiYYA+N5l1eDCamJAAw1mZsOMUWeivbhdRqicKnDul2N8SS25vYhIoqG9TF9dyIfSqbsQKIjfrncab563YqNuRM7JbVeqqReWaSZwzzGxYJJfPy5Pxlwx8Ytm2Pug7lj8wl5rgB5SsWYL5kAWCMPeCR+bVj2LcdsLFPfJ6z1eETIADPpR7lZpRaTysuxjlSpf4EXx2+5JFroIFvw4AF6thAfmABCPj/YLHruMFv7FbopJKt2c14WjVitC6rRpWijaqmzGoUblJZTw9JWtcyr/UxK9ZVkqq0MKGu7/tvvH4lCvABAAA\u003d"}

File diff suppressed because one or more lines are too long

View file

@ -1,15 +0,0 @@
GET /mob/location/details/8011160 HTTP/1.1
Accept: application/x.db.vendo.mob.location.v3+json
Content-Type: application/x.db.vendo.mob.location.v3+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: b9bd5e93-e6d2-4998-bc5a-17b72ca65ca3
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Mon, 16 Dec 2024 18:36:50 GMT
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 1158
Connection: keep-alive
server-timing: intid;desc=5ae5ce547c4a8b38
Server-Timing: intid;desc=5ae5ce547c4a8b38
Server-Timing: intid;desc=5ae5ce547c4a8b38
x-correlation-id: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=5ae5ce547c4a8b38
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd125629ac814505605565f3e87a8081db30d56beae22efb148df982534183fd830148fa0901fa5a36116fab13d52c5d86a; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
{"haltName":"Berlin Hbf","produktGattungen":[{"produktGattung":"HOCHGESCHWINDIGKEITSZUEGE","produkte":[{"name":"ICE"},{"name":"RJ"}]},{"produktGattung":"INTERCITYUNDEUROCITYZUEGE","produkte":[{"name":"EC"},{"name":"IC"},{"name":"NJ"}]},{"produktGattung":"INTERREGIOUNDSCHNELLZUEGE","produkte":[{"name":"BUS"},{"name":"Bus"},{"name":"D"},{"name":"EN"},{"name":"ES"},{"name":"FLX"},{"name":"UEX"}]},{"produktGattung":"NAHVERKEHRSONSTIGEZUEGE","produkte":[{"name":"FEX"},{"name":"HBX"},{"name":"R"},{"name":"RB"},{"name":"RE"},{"name":"RSM"},{"name":"Bus RE3"},{"name":"Bus RE5"},{"name":"Bus RE7"},{"name":"Bus RE8"},{"name":"Bus S7"}]},{"produktGattung":"SBAHNEN","produkte":[{"name":"S 3"},{"name":"S 5"},{"name":"S 7"},{"name":"S 9"},{"name":"S 45"}]},{"produktGattung":"BUSSE","produkte":[{"name":"Bus 120"},{"name":"Bus 123"},{"name":"Bus 142"},{"name":"Bus 147"},{"name":"Bus 245"},{"name":"Bus M41"},{"name":"Bus M85"},{"name":"Bus N5"},{"name":"Bus N20"},{"name":"Bus N40"}]},{"produktGattung":"UBAHN","produkte":[{"name":"U 5"}]},{"produktGattung":"STRASSENBAHN","produkte":[{"name":"STR 12"},{"name":"STR M5"},{"name":"STR M8"},{"name":"STR M10"}]}]}

View file

@ -1,17 +0,0 @@
POST /mob/bahnhofstafel/abfahrt HTTP/1.1
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: b2d3fe71-a8d6-4675-bbe5-a5081368a6ab
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 197
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"anfragezeit":"21:28","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8098160@i\u003dU×008031922@","verkehrsmittel":["ALL"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
POST /mob/bahnhofstafel/abfahrt HTTP/1.1
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 8c20d0c0-3c0b-40ef-bc95-849c65e375df
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 378
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"anfragezeit":"21:28","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8098160@i\u003dU×008031922@","verkehrsmittel":["HOCHGESCHWINDIGKEITSZUEGE","INTERCITYUNDEUROCITYZUEGE","INTERREGIOUNDSCHNELLZUEGE","NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}

View file

@ -1,17 +0,0 @@
POST /mob/bahnhofstafel/ankunft HTTP/1.1
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: d7e7526d-1d69-41d1-bf48-9c2ed5f4a302
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 378
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"anfragezeit":"21:28","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8098160@i\u003dU×008031922@","verkehrsmittel":["HOCHGESCHWINDIGKEITSZUEGE","INTERCITYUNDEUROCITYZUEGE","INTERREGIOUNDSCHNELLZUEGE","NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
POST /mob/location/search HTTP/1.1
Accept: application/x.db.vendo.mob.location.v3+json
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 91946e30-da87-4813-9a13-48dce2cd2cdd
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 44
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"locationTypes":["ST"],"searchTerm":"test"}

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Mon, 16 Dec 2024 18:39:38 GMT
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 2994
Connection: keep-alive
server-timing: intid;desc=6e7cf7863b2dbf7b
Server-Timing: intid;desc=6e7cf7863b2dbf7b
Server-Timing: intid;desc=6e7cf7863b2dbf7b
x-correlation-id: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=6e7cf7863b2dbf7b
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd109bdf5d864ee9ff6d510d0c0e6688e510fd271d9dba08fe92a99ec7d68b43cb8eddd472ec4074727d7ed3137ea220214; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
[{"name":"Tessin West","stationId":"7983","locationId":"A=1@O=Tessin West@X=12442572@Y=54034438@U=81@L=8079604@B=1@p=1734031727@i=U×008030295@","evaNr":"8079604","coordinates":{"latitude":54.0344,"longitude":12.442203},"weight":1489,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Testa Grigia","locationId":"A=1@O=Testa Grigia@X=7707540@Y=45934474@U=81@L=8511303@B=1@p=1734031727@i=U×008511303@","evaNr":"8511303","coordinates":{"latitude":45.934475,"longitude":7.70754},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Teschenhagen","stationId":"6172","locationId":"A=1@O=Teschenhagen@X=13374232@Y=54389368@U=81@L=8013104@B=1@p=1734031727@i=U×008028497@","evaNr":"8013104","coordinates":{"latitude":54.38936,"longitude":13.374196},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Testelt","locationId":"A=1@O=Testelt@X=4946863@Y=51009783@U=81@L=8800244@B=1@p=1734031727@i=U×008833266@","evaNr":"8800244","coordinates":{"latitude":51.009785,"longitude":4.946863},"weight":2627,"products":["INTERCITYUNDEUROCITYZUEGE","NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Tessin","stationId":"6174","locationId":"A=1@O=Tessin@X=12462618@Y=54032020@U=81@L=8013106@B=1@p=1734031727@i=U×008027109@","evaNr":"8013106","coordinates":{"latitude":54.032,"longitude":12.461656},"weight":2402,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Chemnitz Zentralhaltestelle","locationId":"A=1@O=Chemnitz Zentralhaltestelle@X=12922263@Y=50831626@U=81@L=8017419@B=1@p=1734031727@i=U×008042918@","evaNr":"8017419","coordinates":{"latitude":50.831627,"longitude":12.922263},"weight":5813,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Teschow","stationId":"6173","locationId":"A=1@O=Teschow@X=11637956@Y=53994355@U=81@L=8013105@B=1@p=1734031727@i=U×008027118@","evaNr":"8013105","coordinates":{"latitude":53.994175,"longitude":11.637687},"weight":1524,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Tesperhude Hudehof, Geesthacht","locationId":"A=1@O=Tesperhude Hudehof, Geesthacht@X=10431253@Y=53404393@U=81@L=694334@B=1@p=1734031727@","evaNr":"694334","coordinates":{"latitude":53.404392,"longitude":10.431253},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Tesperhude Strandweg, Geesthacht","locationId":"A=1@O=Tesperhude Strandweg, Geesthacht@X=10427424@Y=53402316@U=81@L=694333@B=1@p=1734031727@","evaNr":"694333","coordinates":{"latitude":53.402317,"longitude":10.427424},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Testorf Umspannwerk, Wangels","locationId":"A=1@O=Testorf Umspannwerk, Wangels@X=10778516@Y=54250161@U=81@L=700240@B=1@p=1734031727@","evaNr":"700240","coordinates":{"latitude":54.25016,"longitude":10.778516},"weight":220,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"}]

View file

@ -1,18 +0,0 @@
POST /mob/angebote/fahrplan HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
x-feature-reiseketten-enabled: false
X-Correlation-ID: 67b8a500-1983-49f5-a4ff-177d58b395ed_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: be862f65-99be-46af-a622-8bc332dc70df
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 1122
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@","economic":true,"minUmstiegsdauer":20,"verkehrsmittel":["NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"viaLocations":[{"locationId":"A\u003d1@O\u003dHannover Hbf@X\u003d9741017@Y\u003d52376764@U\u003d81@L\u003d8000152@B\u003d1@p\u003d1734031727@i\u003dU×008013552@","minUmstiegsdauer":60,"verkehrsmittel":["NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}],"zeitWunsch":{"reiseDatum":"2024-12-16T19:28:48.659+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@B\u003d1@p\u003d1734031727@i\u003dU×008015458@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["KEINE_ERMAESSIGUNG KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false}

File diff suppressed because one or more lines are too long

View file

@ -1,18 +0,0 @@
POST /mob/angebote/fahrplan HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
x-feature-reiseketten-enabled: false
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: c752e01a-e03e-42a5-bcd3-26b1cc2574b3
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 783
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@B\u003d1@p\u003d1734031727@i\u003dU×008015458@","minUmstiegsdauer":20,"verkehrsmittel":["NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"zeitWunsch":{"reiseDatum":"2024-12-16T19:45:47.459239+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["BAHNCARD25 KLASSE_2"],"reisendenTyp":"SENIOR"}]},"reservierungsKontingenteVorhanden":false}

File diff suppressed because one or more lines are too long

View file

@ -1,15 +0,0 @@
GET /mob/zuglauf/2%7C%23VN%231%23ST%231734031727%23PI%231%23ZI%23178229%23TA%230%23DA%23161224%231S%238000207%231T%231926%23LS%238098160%23LT%2310012%23PU%2381%23RT%231%23CA%23ICE%23ZE%23947%23ZB%23ICE%20%20947%23PC%230%23FR%238000207%23FT%231926%23TO%238098160%23TT%2310012%23 HTTP/1.1
Accept: application/x.db.vendo.mob.zuglauf.v2+json
Content-Type: application/x.db.vendo.mob.zuglauf.v2+json
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 57c6969b-e937-4591-accf-0850e7b72278
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0

File diff suppressed because one or more lines are too long

View file

@ -1,15 +0,0 @@
GET /mob/zuglaeufe/ICE_947/halte/by-abfahrt/8000207_2024-12-16T19:26:00+01:00/wagenreihung HTTP/1.1
Accept: application/x.db.vendo.mob.wagenreihung.v3+json
Content-Type: application/x.db.vendo.mob.wagenreihung.v3+json
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 4ce19a5d-9da2-480d-a278-543fade2f485
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0

View file

@ -1,15 +0,0 @@
GET /mob/zuglaeufe/ICE_947/halte/by-abfahrt/8000207_2024-12-16T19:26:00+01:00/wagenreihung HTTP/1.1
Accept: application/x.db.vendo.mob.wagenreihung.v3+json
Content-Type: application/x.db.vendo.mob.wagenreihung.v3+json
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 4ce19a5d-9da2-480d-a278-543fade2f485
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Mon, 16 Dec 2024 18:48:06 GMT
Content-Type: application/x.db.vendo.mob.wagenreihung.v3+json
Content-Length: 4538
Connection: keep-alive
server-timing: intid;desc=8f64b8dc41cc78fe
Server-Timing: intid;desc=8f64b8dc41cc78fe
Server-Timing: intid;desc=8f64b8dc41cc78fe
x-correlation-id: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=8f64b8dc41cc78fe
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd1b784b8e477d277c1a5b018395e42eab8d1fee3cb97bf2b9ec80a7dd3763f495c187c41e167ab4fcf87709c96c07cb192; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
{"status":"MATCHES_SCHEDULE","gleis":{"start":{"position":0},"ende":{"position":487},"sektoren":[{"bezeichnung":"G","start":{"position":0},"ende":{"position":96.75},"gleisabschnittswuerfelPosition":58,"ersteKlasse":false},{"bezeichnung":"F","start":{"position":96.75},"ende":{"position":161.75},"gleisabschnittswuerfelPosition":135.5,"ersteKlasse":false},{"bezeichnung":"E","start":{"position":161.75},"ende":{"position":216.4},"gleisabschnittswuerfelPosition":188,"ersteKlasse":false},{"bezeichnung":"D","start":{"position":216.4},"ende":{"position":275.525},"gleisabschnittswuerfelPosition":244.8,"ersteKlasse":false},{"bezeichnung":"C","start":{"position":275.525},"ende":{"position":327.4},"gleisabschnittswuerfelPosition":306.25,"ersteKlasse":false},{"bezeichnung":"B","start":{"position":327.4},"ende":{"position":371.5},"gleisabschnittswuerfelPosition":348.55,"ersteKlasse":false},{"bezeichnung":"A","start":{"position":371.5},"ende":{"position":487},"gleisabschnittswuerfelPosition":394.45,"ersteKlasse":true}],"bezeichnung":"5"},"fahrzeuggruppen":[{"fahrzeuge":[{"fahrzeugtyp":{"fahrzeugkategorie":"POWERCAR","baureihe":"I4020","ersteKlasse":false,"zweiteKlasse":false},"status":"OPEN","orientierung":"FORWARDS","positionAmGleis":{"start":{"position":427.44},"ende":{"position":448},"sektor":"A"},"ausstattungsmerkmale":[]},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_FIRST_CLASS","baureihe":"Apmz","ersteKlasse":true,"zweiteKlasse":false},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":401.04},"ende":{"position":427.44},"sektor":"A"},"ausstattungsmerkmale":[{"art":"SEATS_SEVERELY_DISABLED","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"},{"art":"ZONE_QUIET","status":"UNDEFINED"}],"ordnungsnummer":27},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_FIRST_CLASS","baureihe":"Apmz","ersteKlasse":true,"zweiteKlasse":false},"status":"OPEN","orientierung":"FORWARDS","positionAmGleis":{"start":{"position":374.64},"ende":{"position":401.04},"sektor":"A"},"ausstattungsmerkmale":[{"art":"SEATS_BAHN_COMFORT","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"}],"ordnungsnummer":26},{"fahrzeugtyp":{"fahrzeugkategorie":"DININGCAR","baureihe":"WRmbsz","ersteKlasse":false,"zweiteKlasse":false},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":348.24},"ende":{"position":374.64},"sektor":"B"},"ausstattungsmerkmale":[{"art":"INFO","status":"UNDEFINED"},{"art":"TOILET_WHEELCHAIR","status":"UNDEFINED"},{"art":"WHEELCHAIR_SPACE","status":"AVAILABLE"}],"ordnungsnummer":25},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_ECONOMY_CLASS","baureihe":"Bpmbz","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":321.84},"ende":{"position":348.24},"sektor":"B"},"ausstattungsmerkmale":[{"art":"SEATS_SEVERELY_DISABLED","status":"UNDEFINED"},{"art":"ZONE_FAMILY","status":"UNDEFINED"},{"art":"CABIN_INFANT","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"},{"art":"TOILET_WHEELCHAIR","status":"UNDEFINED"},{"art":"WHEELCHAIR_SPACE","status":"AVAILABLE"}],"ordnungsnummer":24},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_ECONOMY_CLASS","baureihe":"Bpmz","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":295.44},"ende":{"position":321.84},"sektor":"C"},"ausstattungsmerkmale":[{"art":"SEATS_BAHN_COMFORT","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"}],"ordnungsnummer":23},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_ECONOMY_CLASS","baureihe":"Bpmz","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":269.04},"ende":{"position":295.44},"sektor":"C"},"ausstattungsmerkmale":[{"art":"AIR_CONDITION","status":"UNDEFINED"},{"art":"ZONE_QUIET","status":"UNDEFINED"}],"ordnungsnummer":22},{"fahrzeugtyp":{"fahrzeugkategorie":"CONTROLCAR_ECONOMY_CLASS","baureihe":"Bpmzf","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"FORWARDS","positionAmGleis":{"start":{"position":242.64},"ende":{"position":269.04},"sektor":"D"},"ausstattungsmerkmale":[{"art":"AIR_CONDITION","status":"UNDEFINED"}],"ordnungsnummer":21}],"fahrtreferenz":{"typ":"HIGH_SPEED_TRAIN","linie":"","ziel":{"bezeichnung":"Berlin Hbf"},"gattung":"ICE","fahrtnummer":947},"bezeichnung":"ICE0225"}],"fahrtrichtung":"RECHTS","gleisSoll":"4","gleisVorschau":"5"}

View file

@ -1,17 +0,0 @@
POST /mob/angebote/recon/autonomereservierung HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: c88c72b9-3515-4acf-b052-3e71578c9461
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 2276
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reisendenProfil":{"reisende":[{"ermaessigungen":["BAHNCARD25 KLASSE_2"],"reisendenTyp":"SENIOR"}]},"reservierungsKontingenteVorhanden":false,"verbindungHin":{"kontext":"¶HKI¶T$A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@L\u003d8000207@a\u003d128@$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$202412162342$202412170220$IC 60403$$1$$$$$$§T$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$A\u003d1@O\u003dErfurt Hbf@X\u003d11037989@Y\u003d50972352@L\u003d8010101@a\u003d128@$202412170249$202412170518$ICE 698$$1$$$$$$§T$A\u003d1@O\u003dErfurt Hbf@X\u003d11037989@Y\u003d50972352@L\u003d8010101@a\u003d128@$A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@L\u003d8098160@a\u003d128@$202412170527$202412170729$ICE 1606$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzQ1MzE1I0hJTiMwI0VDSyM1Mzk5OTV8NTM5OTgyfDU0MDQ0OXw1NDA0NDl8MHwwfDU2NXw1Mzk5NTN8M3wwfDh8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMxNjEyMjQyMzQyIwpaI1ZOIzEjU1QjMTczNDAzMTcyNyNQSSMxI1pJIzE3MzU2MyNUQSMwI0RBIzE2MTIyNCMxUyM4NDAwMDU4IzFUIzIwMTUjTFMjODUwMzAwMCNMVCMxMDgwNSNQVSM4MSNSVCMxI0NBI0lDI1pFIzYwNDAzI1pCI0lDIDYwNDAzI1BDIzEjRlIjODAwMDIwNyNGVCMyMzQyI1RPIzgwMDAxMDUjVFQjMTAyMjAjClojVk4jMSNTVCMxNzM0MDMxNzI3I1BJIzEjWkkjMTc3MTQ3I1RBIzAjREEjMTYxMjI0IzFTIzgwMDAyNjEjMVQjMjE1MSNMUyM4MDk4MTYwI0xUIzEwNzU1I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzY5OCNaQiNJQ0UgIDY5OCNQQyMwI0ZSIzgwMDAxMDUjRlQjMTAyNDkjVE8jODAxMDEwMSNUVCMxMDUxOCMKWiNWTiMxI1NUIzE3MzQwMzE3MjcjUEkjMSNaSSMxNzQ1MzgjVEEjMCNEQSMxNzEyMjQjMVMjODAxMDEwMSMxVCM1MjcjTFMjODAwMjU1MyNMVCM5MzkjUFUjODEjUlQjMSNDQSNJQ0UjWkUjMTYwNiNaQiNJQ0UgMTYwNiNQQyMwI0ZSIzgwMTAxMDEjRlQjNTI3I1RPIzgwOTgxNjAjVFQjNzI5Iw\u003d\u003d¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P7UrDMBiFb0Xyu443adM2hUDsyvBjaBEnivijrtms9GOm6bCUXod34g3sxkxbBoIi+ZNz3pPzvOnQXioUIDzzfGQh+aGNiMLZfTRjRiv5joIOlU2xQAG1hkuIArBQ1ego0dKECRAHE+yi0bzLisHEzKEAxtqMDafYQm9lu8i1WqLgqUO63Q2x+PYmMqGiSgd1cT03Yp/kzVgBxEb987jT/HU7FRtyKnfLaj3V5Flqkmccixt+dfjKy5Pzl4144C6jvmeDeOQUmGMDYWLFfSyW3AcAAp4IzZsdx57tgI094omMrw6fAD5g6lBfmEVqPX1wMW6RKPUnNpQqz45cbNsG7bABTMyh/g8wxtiFf8AuZS77Dd5KHVd5ayDG06qRo3VZNaqUbVg1ZVqjYJPk9TSIk7rOs1ofs3JdxYlKChPq+r7/Bvf6c1bwAQAA"}}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
POST /mob/bahnhofstafel/abfahrt HTTP/1.1
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 2b250673-6250-4bcd-8dc3-8cf83c0a7686
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 376
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"anfragezeit":"20:35","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@i\u003dU×008015458@","verkehrsmittel":["HOCHGESCHWINDIGKEITSZUEGE","INTERCITYUNDEUROCITYZUEGE","INTERREGIOUNDSCHNELLZUEGE","NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Wed, 18 Dec 2024 21:38:58 GMT
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 590
Connection: keep-alive
server-timing: intid;desc=bf069d97495f8dad
Server-Timing: intid;desc=bf069d97495f8dad
Server-Timing: intid;desc=bf069d97495f8dad
x-correlation-id: faddeefb-53f2-41a9-9879-e1c4edd7845f_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=bf069d97495f8dad
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd1e76575d2a4e5c74d05675d588d2a35d6e7b8b0e5cad991641ebd4a05f4c1b051774f52d1a60eac0d3365250488a0be85; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
{"bahnhofstafelAbfahrtPositionen":[{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#514236#TA#0#DA#181224#1S#541958#1T#1944#LS#548569#LT#2108#PU#81#RT#1#CA#rfb#ZE#8175#ZB#RUF 8175#PC#9#FR#541958#FT#1944#TO#548569#TT#2108#","kurztext":"RUF","mitteltext":"RUF 8175","abfrageOrt":{"name":"Penzing Ortsmitte, Aidenbach","locationId":"A=1@O=Penzing Ortsmitte, Aidenbach@X=13063430@Y=48562182@U=81@L=548572@","evaNr":"548572"},"richtung":"Köching Abzw. Haidenburg Wartehäuschen, Aidenbach","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T21:03:00+01:00","produktGattung":"ANRUFPFLICHTIGEVERKEHRE"}]}

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Wed, 18 Dec 2024 21:42:04 GMT
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 2498
Connection: keep-alive
server-timing: intid;desc=4e6f4ce0b26b14cb
Server-Timing: intid;desc=4e6f4ce0b26b14cb
Server-Timing: intid;desc=4e6f4ce0b26b14cb
x-correlation-id: faddeefb-53f2-41a9-9879-e1c4edd7845f_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=4e6f4ce0b26b14cb
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd1b5c4411e233c8956b40fa37e9050070c341e1782b80282d570eb1ed0cb63a2fab4db66435d0bad351471a04f949cac0d; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
{"bahnhofstafelAbfahrtPositionen":[{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#989203#TA#17#DA#181224#1S#368702#1T#930#LS#936350#LT#935#PU#81#RT#1#CA#FAE#ZE#FÄ1#ZB#Fähre #PC#6#FR#368702#FT#930#TO#936350#TT#935#","kurztext":"Fähre","mitteltext":"Fähre","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12097404@Y=54176710@U=81@L=368702@","evaNr":"368702"},"richtung":"Warnemünde","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T09:30:00+01:00","produktGattung":"SCHIFF"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#417851#TA#1#DA#181224#1S#936250#1T#947#LS#939255#LT#1021#PU#81#RT#1#CA#Bus#ZE#18#ZB#Bus 18#PC#5#FR#936250#FT#947#TO#939255#TT#1021#","kurztext":"Bus","mitteltext":"Bus 18","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12098627@Y=54176485@U=81@L=936250@","evaNr":"936250"},"richtung":"Dierkower Kreuz","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T09:47:00+01:00","produktGattung":"BUS"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#989203#TA#18#DA#181224#1S#368702#1T#950#LS#936350#LT#955#PU#81#RT#1#CA#FAE#ZE#FÄ1#ZB#Fähre #PC#6#FR#368702#FT#950#TO#936350#TT#955#","kurztext":"Fähre","mitteltext":"Fähre","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12097404@Y=54176710@U=81@L=368702@","evaNr":"368702"},"richtung":"Warnemünde","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T09:50:00+01:00","produktGattung":"SCHIFF"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#420342#TA#0#DA#181224#1S#936250#1T#1007#LS#842059#LT#1018#PU#81#RT#1#CA#Bus#ZE#17#ZB#Bus 17#PC#5#FR#936250#FT#1007#TO#842059#TT#1018#","kurztext":"Bus","mitteltext":"Bus 17","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12098627@Y=54176485@U=81@L=936250@","evaNr":"936250"},"richtung":"Markgrafenheide Ost","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T10:07:00+01:00","produktGattung":"BUS"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#989203#TA#19#DA#181224#1S#368702#1T#1010#LS#936350#LT#1015#PU#81#RT#1#CA#FAE#ZE#FÄ1#ZB#Fähre #PC#6#FR#368702#FT#1010#TO#936350#TT#1015#","kurztext":"Fähre","mitteltext":"Fähre","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12097404@Y=54176710@U=81@L=368702@","evaNr":"368702"},"richtung":"Warnemünde","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T10:10:00+01:00","produktGattung":"SCHIFF"}]}

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Wed, 18 Dec 2024 21:43:02 GMT
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
Content-Length: 1030
Connection: keep-alive
server-timing: intid;desc=3007cc29ce998300
Server-Timing: intid;desc=3007cc29ce998300
Server-Timing: intid;desc=3007cc29ce998300
x-correlation-id: faddeefb-53f2-41a9-9879-e1c4edd7845f_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=3007cc29ce998300
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd1778d8a6954cb4376d998b52e8b4b9edfa2de0eed859c823593d6a880ffc23b5ab8f107642d7ccfb36b5be2d8c9d6632f; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
{"bahnhofstafelAbfahrtPositionen":[{"gleis":"2","zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#338816#TA#0#DA#181224#1S#8000096#1T#712#LS#8100003#LT#1352#PU#81#RT#1#CA#DPF#ZE#963#ZB#WB 963#PC#2#FR#8000096#FT#712#TO#8100003#TT#1352#","kurztext":"WB","mitteltext":"WB 963","abfrageOrt":{"name":"Stuttgart Hbf","locationId":"A=1@O=Stuttgart Hbf@X=9181636@Y=48784081@U=81@L=8000096@i=U×008029034@","evaNr":"8000096","stationId":"6071"},"echtzeitNotizen":[],"abgangsDatum":"2024-12-18T07:12:00+01:00","produktGattung":"IR"},{"gleis":"8","zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#203510#TA#0#DA#181224#1S#8000096#1T#717#LS#8098160#LT#1315#PU#81#RT#1#CA#DPF#ZE#1240#ZB#FLX 1240#PC#2#FR#8000096#FT#717#TO#8098160#TT#1315#","kurztext":"FLX","mitteltext":"FLX 1240","abfrageOrt":{"name":"Stuttgart Hbf","locationId":"A=1@O=Stuttgart Hbf@X=9181636@Y=48784081@U=81@L=8000096@i=U×008029034@","evaNr":"8000096","stationId":"6071"},"richtung":"Berlin Hbf","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T07:17:00+01:00","produktGattung":"IR"}]}

View file

@ -1,17 +0,0 @@
POST /mob/location/search HTTP/1.1
Accept: application/x.db.vendo.mob.location.v3+json
X-Correlation-ID: 0079a47f-6208-4507-9d9a-0e02802ef52c_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: 205f5fd7-f96a-4e07-a47d-bb3edb8c443e
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 52
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"locationTypes":["ALL"],"searchTerm":"schillerstr"}

View file

@ -1,17 +0,0 @@
HTTP/1.1 200 OK
Date: Wed, 18 Dec 2024 22:19:20 GMT
Content-Type: application/x.db.vendo.mob.location.v3+json
Content-Length: 2877
Connection: keep-alive
server-timing: intid;desc=8a52db25dd785bfe
Server-Timing: intid;desc=8a52db25dd785bfe
Server-Timing: intid;desc=8a52db25dd785bfe
x-correlation-id: 0079a47f-6208-4507-9d9a-0e02802ef52c_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
Strict-Transport-Security: max-age=16070400; includeSubDomains
X-XSS-Protection: 0
server-timing: intid;desc=8a52db25dd785bfe
Content-Security-Policy: frame-ancestors 'none';
X-Content-Type-Options: nosniff
Set-Cookie: TS01be2125=01d513bcd1de3bcb4b755a3859cad31611f802372756db9780547d536b115fb805b4cb7fe4f2aefad293244318a0660f18cf234872; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
[{"name":"Schillerstr., Ofterdingen","locationId":"A=1@O=Schillerstr., Ofterdingen@X=9030635@Y=48420917@U=81@L=750308@B=1@p=1734378327@","evaNr":"750308","coordinates":{"latitude":48.420918,"longitude":9.030635},"weight":995,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Lutherstadt Wittenberg","locationId":"A=1@O=Schillerstr., Lutherstadt Wittenberg@X=12660057@Y=51875984@U=81@L=963037@B=1@p=1734378327@","evaNr":"963037","coordinates":{"latitude":51.875984,"longitude":12.660057},"weight":943,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Geithain","locationId":"A=1@O=Schillerstr., Geithain@X=12691420@Y=51058568@U=81@L=200268@B=1@p=1734378327@","evaNr":"200268","coordinates":{"latitude":51.058567,"longitude":12.69142},"weight":941,"products":["BUSSE"],"locationType":"ST"},{"name":"Schillerstr., Zwickau","locationId":"A=1@O=Schillerstr., Zwickau@X=12493001@Y=50715018@U=81@L=983776@B=1@p=1734378327@","evaNr":"983776","coordinates":{"latitude":50.71502,"longitude":12.493001},"weight":941,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Überherrn","locationId":"A=1@O=Schillerstr., Überherrn@X=6705494@Y=49239322@U=81@L=835123@B=1@p=1734378327@","evaNr":"835123","coordinates":{"latitude":49.239323,"longitude":6.705494},"weight":941,"products":["BUSSE"],"locationType":"ST"},{"name":"Schillerstr./Forstweg, Brieselang","locationId":"A=1@O=Schillerstr./Forstweg, Brieselang@X=12988073@Y=52583830@U=81@L=734788@B=1@p=1734378327@","evaNr":"734788","coordinates":{"latitude":52.58383,"longitude":12.988073},"weight":941,"products":["BUSSE"],"locationType":"ST"},{"name":"Schillerstr., Berlin","locationId":"A=1@O=Schillerstr., Berlin@X=13410584@Y=52591948@U=81@L=732759@B=1@p=1734378327@","evaNr":"732759","coordinates":{"latitude":52.59195,"longitude":13.410584},"weight":412,"products":["STRASSENBAHN"],"locationType":"ST"},{"name":"Schillerstr., Schöneiche b. Berlin","locationId":"A=1@O=Schillerstr., Schöneiche b. Berlin@X=13709386@Y=52476958@U=81@L=738128@B=1@p=1734378327@","evaNr":"738128","coordinates":{"latitude":52.47696,"longitude":13.709386},"weight":412,"products":["STRASSENBAHN"],"locationType":"ST"},{"name":"Schillerstr., Markdorf","locationId":"A=1@O=Schillerstr., Markdorf@X=9386033@Y=47727336@U=81@L=801101@B=1@p=1734378327@","evaNr":"801101","coordinates":{"latitude":47.727337,"longitude":9.386033},"weight":272,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Salzwedel","locationId":"A=1@O=Schillerstr., Salzwedel@X=11160072@Y=52850918@U=81@L=957938@B=1@p=1734378327@","evaNr":"957938","coordinates":{"latitude":52.850918,"longitude":11.160072},"weight":272,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"}]

View file

@ -1,18 +0,0 @@
POST /mob/angebote/fahrplan HTTP/1.1
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
x-feature-reiseketten-enabled: false
X-Correlation-ID: 84f2ab9b-d4a7-4e98-9a29-113783f0d431_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
X-Device-Os-Name: Android
X-Device-Os-Version: 32
X-Device-Model: Google Pixel 3a
X-App-Version: 24.32.2
Accept-Language: en,de
X-INSTANA-ANDROID: a33cf0fb-5fd8-4c2f-befa-d4c1963cf3b7
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
Content-Length: 686
Host: app.vendo.noncd.db.de
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d2@O\u003dBerlin - Bohnsdorf, Schillerstraße 10@H\u003d10@X\u003d13585128@Y\u003d52398257@U\u003d92@L\u003d980126874@B\u003d1@p\u003d1706613073@","verkehrsmittel":["ALL"],"zeitWunsch":{"reiseDatum":"2024-12-18T22:38:09.928078+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["BAHNCARD25 KLASSE_2"],"reisendenTyp":"SENIOR"}]},"reservierungsKontingenteVorhanden":false}

File diff suppressed because one or more lines are too long

View file

@ -1,15 +0,0 @@
# Dumps from DB Navigator API app.vendo.noncd.db.de
In this directory, some intercepted traffic from DB Navigator. (Repo license does not apply to this directory.)
You can browse some responses of the bahn.de API and others in the [fixtures](https://github.com/public-transport/db-vendo-client/tree/main/test/fixtures) and [e2e fixtures](https://github.com/public-transport/db-vendo-client/tree/main/test/e2e/fixtures).
How to intercept DB Navigator traffic:
1. Download/extract Split APK
2. Merge APK (e.g. using [APKEditor](https://github.com/REAndroid/APKEditor))
3. decompile using apktool
4. edit [res/xml/network_security_config.xml](https://developer.android.com/privacy-and-security/security-config) to allow user CAs not just in debug
5. recompile using apktool, sign
6. install on an Android
7. intercept with a mitm decryption tool of your choice by installing CA cert into Android store (e.g. [PCAPdroid](https://github.com/emanuele-f/PCAPdroid) with [mitm addon](https://github.com/emanuele-f/PCAPdroid-mitm), no root needed)

160
docs/hafas-mgate-api.md Normal file
View file

@ -0,0 +1,160 @@
# HAFAS `mgate.exe` protocol
The protocol of `mgate.exe` HAFAS endpoints is not openly (and freely) documented. The following documentation is based on general observations and reverse-engineering.
*Note:* There are also `rest.exe` (a.k.a. "open API", a.k.a. "REST API") endpoints. This documentation is *not* about them.
## date & time format
Dates are encoded as `YYYYMMDD`, time strings as `HHMMSS`. These are in the timezone configured on the HAFAS/server side, *per endpoint*.
Whenever HAFAS returns a time string that exceeds the day the response describes, it will add a "day offset". As an example, when you query departures at `2019-12-12T23:50+01:00` for the next 30 minutes, it will encode the departure at `2019-12-13T00:13+01:00` as `20191212` & `01001300`.
For working code, check out [`parseDateTime()`](../parse/date-time.js).
## coordinate format
All endpoints I've seen so far use [WGS84](http://wiki.gis.com/wiki/index.php/WGS84). Values are multiplied by `10^6` though, so you would encode `{latitude: 1.23, longitude: -2.34}` as `{Y: 1230000: X: -2340000}`. There's an optional parameter `z` with the elevation.
For working code, check out [`formatAddress()`](../format/address.js).
## querying the API
In many aspects, the API looks and feels like [RPCs](https://en.wikipedia.org/wiki/Remote_procedure_call). You must send queries via HTTP `POST`, with the minimal JSON body looking like this:
```js
{
"auth": {
"type": "AID",
"aid": "…" // endpoint-specific authentication token, e.g. `1Rxs112shyHLatUX4fofnmdxK`
},
"ver": "…", // endpoint-specific string, e.g. `1.15`
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
"client": {
"type": "IPA", // might also be `IPH` for "iPhone" or `WEB` for "web client"
"id": "…", // endpoint-specific string, e.g. `BVG`
"name": "…", // endpoint-specific string, e.g. `FahrInfo`
"v": "…" // endpoint-specific string, e.g. `4070700`
},
"lang": "…", // language, sometimes 2-digit (e.g. `de`), sometimes 3-digit (e.g. `deu`)
"svcReqL": [
{
"meth": "…", // name of the API call, supported values depend on the endpoint
"req": {
// actual request parameters…
}
// some endpoints also require this:
"cfg": {
"cfgGrpL": [],
"cfgHash": "…" // endpoint-specific string
}
}
]
}
```
- The data in `client` must be correct, otherwise HAFAS will reject your request.
- HAFAS will return slightly different response formats (and slightly different levels of detail) for different `ver`, `ext` and `client.v` values.
- All endpoints known support JSON & UTF-8, so make sure to send `Accept: application/json` & `Accept-Charset: utf-8` headers.
- Most endpoints support at least GZIP compression, so make sure to send a `Accept-Encoding: gzip` header.
For working code, check out [`request()`](lib/request.js).
## Authentication
There are three known types of authentication used among `mgate.exe` endpoints.
For working code, check out [`hafas-client`'s `request()`](lib/request.js), [`public-transport-enabler`'s Java implementation](https://github.com/schildbach/public-transport-enabler/blob/69614c87af627e2feafc576882f2ccccdbf4b7e6/src/de/schildbach/pte/AbstractHafasClientInterfaceProvider.java#L845-L860), [`TripKit`'s Swift implementation](https://github.com/alexander-albers/tripkit/blob/724b6cd8c258c9c61e7443c81e914618b79393cb/TripKit/AbstractHafasClientInterfaceProvider.swift#L1473-L1495) or [`marudor.de`'s TypeScript implementation](https://github.com/marudor/BahnhofsAbfahrten/blob/cf64d53c6902981ec529d3952253b2c83bff9221/src/server/HAFAS/profiles.ts#L30-L54).
### unprotected endpoints
You can just query these, as long as you send a formally correct request.
### endpoints using the `checksum` query parameter
`checksum` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code): You can compute it by [hashing](https://en.wikipedia.org/wiki/Hash_function) the request body and a secret *salt*.
This secret can be read from the config file inside the accompanying client app. There is no guide for this yet, so please [open an issue](https://github.com/public-transport/hafas-client/issues/new).
### endpoints using the `mic` & `mac` query parameters
`mic` is a [message integrity code](https://en.wikipedia.org/wiki/Message_authentication_code), the [hash](https://en.wikipedia.org/wiki/Hash_function) of the request body.
`mac` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code), the hash of `mic` and a secret *salt*.
This secret can be read from the config file inside the accompanying client app. There is no guide for this yet, so please [open an issue](https://github.com/public-transport/hafas-client/issues/new).
## API responses
A minimal valid response looks like this:
```js
{
"ver": "…", // endpoint-specific string, e.g. `1.15`
"lang": "…", // language
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
"id": "…", // unique ID for each response?
"svcResL": [
{
"meth": "StationBoard",
"err": "OK",
"res": {
// result of the API call
}
}
]
}
```
For working code, check out [`request()`](lib/request.js).
### parse error
todo: generic server error
```js
{
"ver": "…", // endpoint-specific string, e.g. `1.15`
"lang": "…", // language, sometimes 2-digit (e.g. `de`), sometimes 3-digit (e.g. `deu`)
"err": "PARSE", // error code
"errTxt": "…", // error message, not always present
"svcResL": []
}
```
### authentication error
```js
{
"ver": "…", // endpoint-specific string, e.g. `1.15`
"lang": "…", // language
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
"err": "AUTH", // error code
"errTxt": "…", // error message, not always present
"svcResL": []
}
```
### API-call-specific error
```js
{
"ver": "…", // endpoint-specific string, e.g. `1.15`
"lang": "…", // language
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
"svcResL": [
{
"meth": "StationBoard",
"err": "…", // error code, e.g. `H9300`
"errTxt": "…", // error message, e.g. `Unknown arrival station`
"res": {}
}
]
}
```
## more ressources
- [@Nakaner's `strecken.info` API docs](https://github.com/Nakaner/bahnstoerungen/tree/62a72b1e0f0255668500b438187ff65aef39242a/api-doc/db-strecken-info)
- [unfinished HAFAS glossary](https://gist.github.com/derhuerst/74b703e2a0fc64e4a0fa8fbb1f3a61b4)
- [various `mgate.exe` HTTP traffic recordings](https://gist.github.com/search?q=post+mgate.exe&ref=searchresults)

View file

@ -0,0 +1,45 @@
# `journeysFromTrip(tripId, previousStopover, to, [opt])`
`to` must be an [*Friendly Public Transport Format* (FPTF) `stop`](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#stop) or [`station`](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#station). See [`journeys()`](journeys.md) for details.
With `opt`, you can override the default options, which look like this:
```js
{
accessibility: 'none', // 'none', 'partial' or 'complete'
stopovers: false, // return stations on the way?
polylines: false, // return leg shapes?
transferTime: 0, // minimum time for a single transfer in minutes
tickets: false, // return tickets?
remarks: true // parse & expose hints & warnings?
}
```
## Response
*Note:* The returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from `plannedDeparture`/`plannedArrival`, respectively.
As an example, we're going to use the [*Deutsche Bahn* profile](../p/db):
```js
const createClient = require('hafas-client')
const dbProfile = require('hafas-client/p/db')
const berlinSüdkreuz = '8011113'
const münchenHbf = '8000261'
const kölnHbf = '8000207'
const client = createClient(dbProfile, 'my-awesome-program')
// find any journey from Berlin Südkreuz to München Hbf
const [journey] = await client.journeys(berlinSüdkreuz, münchenHbf, {results: 1, stopovers: true})
// find the ICE leg
const leg = journey.legs.find(l => l.line.product === 'nationalExpress')
// find the stopover at the stop you've just passed
const previousStopover = leg.stopovers.find(st => st.departure && new Date(st.departure) < Date.now())
// find journeys from the ICE train to Köln Hbf
const journeys = await client.journeysFromTrip(leg.id, previousStopover, kölnHbf)
```
`journeys` will be an array of [FPTF `journey`s](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#journey), as documented in [`journeys()`](journeys.md).

View file

@ -28,7 +28,7 @@
longitude: 3.21
}
// an address, which is an FPTF `location` object
// an address, which is an FTPF `location` object
{
type: 'location',
address: 'foo street 1',
@ -53,11 +53,11 @@ With `opt`, you can override the default options, which look like this:
stopovers: false, // return stations on the way?
transfers: -1, // Maximum nr of transfers. Default: Let HAFAS decide.
transferTime: 0, // minimum time for a single transfer in minutes
accessibility: 'none', // not supported
accessibility: 'none', // 'none', 'partial' or 'complete'
bike: false, // only bike-friendly journeys
walkingSpeed: 'normal', // not supported
walkingSpeed: 'normal', // 'slow', 'normal', 'fast'
// Consider walking to nearby stations at the beginning of a journey?
startWithWalking: true, // always true (?)
startWithWalking: true,
products: {
// these entries may vary from profile to profile
suburban: true,
@ -65,21 +65,15 @@ With `opt`, you can override the default options, which look like this:
tram: true,
bus: true,
ferry: true,
nationalExpress: true,
national: true,
express: true,
regional: true
regionalExpress: true // this is actually FlixTrain and co.
},
tickets: false, // return tickets? only available for [refreshJourney](refresh-journey.md)
polylines: false, // return a shape for each leg? only available for [refreshJourney](refresh-journey.md)
subStops: true, // not supported
entrances: true, // not supported
tickets: false, // return tickets? only available with some profiles
polylines: false, // return a shape for each leg?
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
remarks: true, // parse & expose hints & warnings?
scheduledDays: false, // returns a field `serviceDays` (instead of `scheduledDays` in hafas-client!) with a different, human-readable structure
notOnlyFastRoutes: false, // if true, also show journeys that are mathematically non-optimal
bestprice: false, // search for lowest prices across the entire day, returns list of journeys sorted by price
firstClass: false, // first or second class for tickets
loyaltyCard: null, // BahnCards etc., see below
scheduledDays: false, // parse which days each journey is valid on
language: 'en', // language to get results in
}
```
@ -88,27 +82,24 @@ With `opt`, you can override the default options, which look like this:
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule.
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} 'db-vendo-client'
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbProfile, userAgent)
const client = createClient(vbbProfile, 'my-awesome-program')
// Frankfurt to Stuttgart
await client.journeys('8000105', '8000096', {
// Hauptbahnhof to Heinrich-Heine-Str.
client.journeys('900000003201', '900000100008', {
results: 1,
stopovers: true
})
.then(console.log)
.catch(console.error)
```
`journeys()` will resolve with an object with the following fields:
- `journeys`
- `earlierRef`/`laterRef` pass them as `opt.earlierThan`/`opt.laterThan` into another `journeys()` call to retrieve the next "page" of journeys
- `realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
This object might look like this:
The `Promise` returned by `journeys()` will resolve with an object with the `journeys` & `earlierRef`/`laterRef` fields. It might look like this:
```js
{
@ -278,9 +269,46 @@ This object might look like this:
} ],
earlierRef: '…', // use with the `earlierThan` option
laterRef: '…' // use with the `laterThan` option
realtimeDataUpdatedAt: 1531259400, // 2018-07-10T23:50:00+02
}
```
Some [profiles](../p) are able to parse the ticket information, if returned by the API. For example, if you pass `tickets: true` with the [VBB profile](../p/vbb), each `journey` will have a tickets array that looks like this:
```js
[ {
name: 'Berlin Tarifgebiet A-B: Einzelfahrausweis Regeltarif',
price: 2.8,
tariff: 'Berlin',
coverage: 'AB',
variant: 'adult',
amount: 1
}, {
name: 'Berlin Tarifgebiet A-B: Einzelfahrausweis Ermäßigungstarif',
price: 1.7,
tariff: 'Berlin',
coverage: 'AB',
variant: 'reduced',
amount: 1,
reduced: true
}, /* … */ {
name: 'Berlin Tarifgebiet A-B: Tageskarte Ermäßigungstarif',
price: 4.7,
tariff: 'Berlin',
coverage: 'AB',
variant: '1 day, reduced',
amount: 1,
reduced: true,
fullDay: true
}, /* … */ {
name: 'Berlin Tarifgebiet A-B: 4-Fahrten-Karte Regeltarif',
price: 9,
tariff: 'Berlin',
coverage: 'AB',
variant: '4x adult',
amount: 4
} ]
```
If a journey leg has been cancelled, a `cancelled: true` will be added. Also, `departure`/`departureDelay`/`departurePlatform` and `arrival`/`arrivalDelay`/`arrivalPlatform` will be `null`.
To get more journeys earlier/later than the current set of results, pass `earlierRef`/`laterRef` into `opt.earlierThan`/`opt.laterThan`. For example, query *later* journeys as follows:
@ -289,16 +317,21 @@ To get more journeys earlier/later than the current set of results, pass `earlie
const hbf = '900000003201'
const heinrichHeineStr = '900000100008'
const res1 = await client.journeys(hbf, heinrichHeineStr)
const lastJourney = res1.journeys[res1.journeys.length - 1]
console.log('departure of last journey', lastJourney.legs[0].departure)
client.journeys(hbf, heinrichHeineStr)
.then((res) => {
const lastJourney = res.journeys[res.journeys.length - 1]
console.log('departure of last journey', lastJourney.legs[0].departure)
// get later journeys
const res2 = await client.journeys(hbf, heinrichHeineStr, {
laterThan: res1.laterRef
// get later journeys
return client.journeys(hbf, heinrichHeineStr, {
laterThan: res.laterRef
})
})
const firstLaterJourney = res2.journeys[res2.journeys.length - 1]
console.log('departure of first (later) journey', firstLaterJourney.legs[0].departure)
.then((laterRes) => {
const firstLaterJourney = laterRes.journeys[laterRes.journeys.length - 1]
console.log('departure of first (later) journey', firstLaterJourney.legs[0].departure)
})
.catch(console.error)
```
```
@ -306,16 +339,19 @@ departure of last journey 2017-12-17T19:07:00+01:00
departure of first (later) journey 2017-12-17T19:19:00+01:00
```
## Using the `loyaltyCard` option
If you pass `polylines: true`, each journey leg will have a `polyline` field. Refer to [the section in the `trip()` docs](trip.md#polyline-option) for details.
If you pass `scheduledDays: true`, each journey will have a `scheduledDays` object looking like this:
```js
import {data as loyaltyCards} from 'db-vendo-client/format/loyalty-cards.js' // see there for a list
hafas.journeys(from, to, {
loyaltyCard: {type: data.BAHNCARD, discount: 25}
})
{
'2018-01-01': true,
'2018-01-02': false,
// …
'2018-10-12': true,
'2018-10-13': true,
// …
'2019-01-02': false,
'2019-01-03': false
}
```
## The `routingMode` option
The `routingMode` option is not supported by db-vendo-client. The behavior will be the same as the [`HYBRID` mode of hafas-client](https://github.com/public-transport/hafas-client/blob/main/p/db/readme.md#using-the-routingmode-option), i.e. cancelled trains/infeasible journeys will also be contained for informational purpose.

62
docs/lines.md Normal file
View file

@ -0,0 +1,62 @@
# `lines([opt])`
**Fetches all lines known to the HAFAS endpoint**, e.g. warnings about disruptions, planned construction work, and general notices about the operating situation.
## Example
As an example, we're going to use the [SVV profile](../p/svv):
```js
const createClient = require('hafas-client')
const svvProfile = require('hafas-client/p/svv')
const client = createClient(svvProfile, 'my-awesome-program')
console.log(await client.lines('S1'))
```
```js
[
{
"id": "obb-1-S1-V-j20-1",
"type": "line",
"name": "S1",
"public": true,
"mode": "train",
"product": "bahn-s-bahn",
"operator": {
"type": "operator",
"id": "montafonerbahn-ag",
"name": "Montafonerbahn AG"
},
"directions": [
"Bludenz Bahnhof",
"Bregenz Hafen Bahnhof",
"Lindau Hbf",
"Bregenz Bahnhof",
"Schruns Bahnhof",
"Lochau Bahnhof"
],
},
// …
{
"id": "svv-42-50-j20-2",
"type": "line",
"name": "S1",
"public": true,
"mode": "train",
"product": "bahn-s-bahn",
"operator": {
"type": "operator",
"id": "salzburg-ag-salzburger-lokalbahn",
"name": "Salzburg AG - Salzburger Lokalbahn"
},
"directions": [
"Lamprechtshausen Bahnhof",
"Salzburg Hauptbahnhof",
"Acharting S-Bahn",
"Weitwörth-Nussdorf Bahnhof"
],
},
]
```

View file

@ -6,32 +6,34 @@ With `opt`, you can override the default options, which look like this:
```js
{
fuzzy: true // not supported
fuzzy: true // find only exact matches?
, results: 5 // how many search results?
, stops: true // return stops/stations?
, addresses: true
, poi: true // points of interest
, subStops: true // not supported
, entrances: true // not supported
, linesOfStops: false // not supported
, subStops: true // parse & expose sub-stops of stations?
, entrances: true // parse & expose entrances of stops/stations?
, linesOfStops: false // parse & expose lines at each stop/station?
, language: 'en' // language to get results in
}
```
## Response
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} from 'db-vendo-client'
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbnavProfile, userAgent)
const client = createClient(vbbProfile, 'my-awesome-program')
await client.locations('Alexanderplatz', {results: 3})
client.locations('Alexanderplatz', {results: 3})
.then(console.log)
.catch(console.error)
```
The result may look like this:
The response may look like this:
```js
[ {

108
docs/migrating-to-5.md Normal file
View file

@ -0,0 +1,108 @@
# Migrating to `hafas-client@5`
## If you use Node `8` ("Carbon")…
…migrate to Node `10` ("Dubnium"), sorry. [Node `8` is out of maintenance now](https://nodejs.org/en/about/releases/). 83f43c6
## new fields for departure/arrival time & delays
An arrival/departure now looks like this:
```js
{
when: null, // realtime/prognosed
plannedWhen: '2019-10-10T10:10+10:00',
platform: '3', // realtime/prognosed
plannedPlatform: '4'
}
```
A stopover/journey leg now will look like this:
```js
{
arrival: null, // realtime/prognosed
plannedArrival: '2019-10-10T10:10+10:00',
arrivalDelay: null,
arrivalPlatform: '3', // realtime/prognosed
plannedArrivalPlatform: '4',
departure: '2019-10-10T10:12+10:00', // realtime/prognosed
plannedDeparture: '2019-10-10T10:10+10:00',
departureDelay: 120, // seconds
departurePlatform: null, // realtime/prognosed
plannedDeparturePlatform: null
}
```
If the same stopover/journey leg is `cancelled: true`, it will look like this:
```js
{
arrival: null,
prognosedArrival: null,
plannedArrival: '2019-10-10T10:10+10:00',
arrivalDelay: null,
arrivalPlatform: null,
prognosedArrivalPlatform: '3',
plannedArrivalPlatform: '4',
departure: null,
prognosedDeparture: '2019-10-10T10:12+10:00',
plannedDeparture: '2019-10-10T10:10+10:00',
departureDelay: 120, // seconds
departurePlatform: null,
prognosedDeparturePlatform: null,
plannedDeparturePlatform: null
}
```
## If you use `journeys()`
…with the `walkingSpeed` option and a custom profile, add `journeysWalkingSpeed` to your profile. 937583e
…without the `results` option, but *expect* a certain number of results, you must pass `results` now. 0045587
## If you use `departures()`/`arrivals()` with the [BVG profile](../p/bvg)…
With the latest protocol version, the BVG endpoint doesn't support these options anymore:
- `stopovers`  Fetch & parse previous/next stopovers? Default: `false`
- `includeRelatedStations` Fepartures at related stations, e.g. those that belong together on the metro map? Default: `true`
2d72391
## If you use a custom profile…
Let's assume you have parse function looking like this:
```js
const createParseLine = (profile, opt, data) => (rawLine) => {
const operator = data.operators[rawLine.oprX] || null
if (operator && operator.name === 'foo') {
return {
type: 'line',
name: 'really special tram line',
mode: 'tram',
product: 'special-tram',
operator
}
}
return defaultParseLine(rawLine)
}
```
Adapt your parse function like this:
```diff
const createParseLine = (profile, opt, _) => (rawLine) => {
- const operator = data.operators[rawLine.oprX] || null
+ const operator = rawLine.operator || null
```
See also [`#127`](https://github.com/public-transport/hafas-client/pull/127).
If you use `icons` in `parseWarning`/`parseHint`, adapt the function(s) to take an object `data` as the first argument. You can access the list of *parsed* icons via `data.icons`, *parsed* warnings via `data.warnings`, etc. a229205 b36f0e3
## Other breaking changes
- `journey.price` will be `null` if there's no pricing data returned by the endpoint, instead of `{amount: null}`. 8fe277d

View file

@ -1,6 +1,6 @@
# `nearby(location, [opt])`
This method can be used to find stops/stations & POIs close to a location. Note that it is not supported by every profile/endpoint.
This method can be used to find stations close to a location. Note that it is not supported by every profile/endpoint.
`location` must be an [*FPTF* `location` object](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#location-objects).
@ -10,32 +10,35 @@ With `opt`, you can override the default options, which look like this:
{
results: 8, // maximum number of results
distance: null, // maximum walking distance in meters
poi: false, // not supported
poi: false, // return points of interest?
stops: true, // return stops/stations?
subStops: true, // not supported
entrances: true, // not supported
linesOfStops: false, // not supported
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
linesOfStops: false, // parse & expose lines at each stop/station?
language: 'en' // language to get results in
}
```
## Response
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} from 'db-vendo-client'
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbProfile, userAgent)
const client = createClient(vbbProfile, 'my-awesome-program')
await client.nearby({
client.nearby({
type: 'location',
latitude: 52.5137344,
longitude: 13.4744798
}, {distance: 400})
.then(console.log)
.catch(console.error)
```
The result may look like this:
The response may look like this:
```js
[ {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
'use strict'
// Refer to the the ./writing-a-profile.md guide.
const products = [
{
id: 'commuterTrain',
mode: 'train',
bitmasks: [16],
name: 'ACME Commuter Rail',
short: 'CR',
default: true
},
{
id: 'metro',
mode: 'train',
bitmasks: [8],
name: 'Foo Bar Metro',
short: 'M',
default: true
}
]
const transformReqBody = (body) => {
// get these from the recorded app requests
// body.client = { … }
// body.ver = …
// body.auth = { … }
// body.lang = …
return body
}
const insaProfile = {
// locale: …,
// timezone: …,
// endpoint: …,
transformReqBody,
products: products,
trip: false,
radar: false
}
module.exports = insaProfile

187
docs/radar.md Normal file
View file

@ -0,0 +1,187 @@
# `radar({north, west, south, east}, [opt])`
Use this method to find all vehicles currently in an area. Note that it is not supported by every profile/endpoint.
`north`, `west`, `south` and `eath` must be numbers (e.g. `52.52411`). Together, they form a [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box).
With `opt`, you can override the default options, which look like this:
```js
{
results: 256, // maximum number of vehicles
duration: 30, // compute frames for the next n seconds
frames: 3, // nr of frames to compute
polylines: true, // return a track shape for each vehicle?
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
language: 'en' // language to get results in
}
```
## Response
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule.
As an example, we're going to use the [VBB profile](../p/vbb):
```js
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
client.radar({
north: 52.52411,
west: 13.41002,
south: 52.51942,
east: 13.41709
}, {results: 5})
.then(console.log)
.catch(console.error)
```
The response may look like this:
```js
[ {
location: {
type: 'location',
latitude: 52.521508,
longitude: 13.411267
},
line: {
type: 'line',
id: 's9',
fahrtNr: '12345',
name: 'S9',
public: true,
mode: 'train',
product: 'suburban',
symbol: 'S',
nr: 9,
metro: false,
express: false,
night: false,
operator: {
type: 'operator',
id: 's-bahn-berlin-gmbh',
name: 'S-Bahn Berlin GmbH'
}
},
direction: 'S Flughafen Berlin-Schönefeld',
trip: 31463, // todo: outdated, should be tripId!
nextStopovers: [ {
stop: {
type: 'stop',
id: '900000029101',
name: 'S Spandau',
location: {
type: 'location',
latitude: 52.534794,
longitude: 13.197477
},
products: {
suburban: true,
subway: false,
tram: false,
bus: true,
ferry: false,
express: true,
regional: true
}
},
arrival: null,
plannedArrival: null,
arrivalDelay: null,
arrivalPlatform: null,
plannedArrivalPlatform: null,
departure: null,
plannedDeparture: '2017-12-17T19:16:00+01:00',
departureDelay: null,
departurePlatform: null,
plannedDeparturePlatform: '1'
} /* … */ ],
frames: [ {
origin: {
type: 'stop',
id: '900000100003',
name: 'S+U Alexanderplatz',
location: { /* … */ },
products: { /* … */ }
},
destination: {
type: 'stop',
id: '900000100004',
name: 'S+U Jannowitzbrücke',
location: { /* … */ },
products: { /* … */ }
},
t: 0
}, /* … */ {
origin: { /* Alexanderplatz */ },
destination: { /* Jannowitzbrücke */ },
t: 30000
} ]
}, {
location: {
type: 'location',
latitude: 52.523297,
longitude: 13.411151
},
line: {
type: 'line',
id: 'm2',
fahrtNr: '54321',
name: 'M2',
public: true,
mode: 'train',
product: 'tram',
symbol: 'M',
nr: 2,
metro: true,
express: false,
night: false,
operator: {
type: 'operator',
id: 'berliner-verkehrsbetriebe',
name: 'Berliner Verkehrsbetriebe'
}
},
direction: 'Heinersdorf',
trip: 26321,
nextStopovers: [ {
stop: { /* S+U Alexanderplatz/Dircksenstr. */ },
arrival: null,
plannedArrival: null,
arrivalDelay: null,
departure: null,
plannedAeparture: '2017-12-17T19:52:00+01:00',
departureDelay: null
}, {
stop: { /* Memhardstr. */ },
arrival: null,
plannedArrival: '2017-12-17T19:54:00+01:00',
arrivalDelay: null,
arrivalPlatform: null,
plannedArrivalPlatform: null,
departure: null,
plannedDeparture: '2017-12-17T19:54:00+01:00',
departureDelay: null,
departurePlatform: null,
plannedDeparturePlatform: '1'
}, /* … */ ],
frames: [ {
origin: { /* S+U Alexanderplatz/Dircksenstr. */ },
destination: { /* Memhardstr. */ },
t: 0
}, /* … */ {
origin: { /* Memhardstr. */ },
destination: { /* Mollstr./Prenzlauer Allee */ },
t: 30000
} ]
}, /* … */ ]
```
If you pass `polylines: true`, each movement will have a `polyline` field, as documented in [the corresponding section in the `trip()` docs](trip.md#polyline-option), with the exception that station info is missing.

94
docs/reachable-from.md Normal file
View file

@ -0,0 +1,94 @@
# `reachableFrom(address, [opt])`
This method can be used to get stations reachable within a certain time from an address. This concept is called [isochrone diagram](https://en.wikipedia.org/wiki/Isochrone_map#Transportation_planning).
*Note*: It appears that HAFAS cannot generate actual isochrones, but only the list of reachable stations, which you can estimate the isochrone(s) from.
`address` must be an [*FPTF* `location` object](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#location-objects).
With `opt`, you can override the default options, which look like this:
```js
{
when: new Date(),
maxTransfers: 5, // maximum of 5 transfers
maxDuration: 20, // maximum travel duration in minutes, pass `null` for infinite
products: {
// These entries may vary from profile to profile!
suburban: true,
subway: true
// …
},
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
}
```
## Response
`reachableFrom(address, [opt])` returns an array, in which each item has a `duration` and an array of [*Friendly Public Transport Format* `station`s](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#station).
As an example, we're going to use the [VBB profile](../p/vbb):
```js
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
client.reachableFrom({
type: 'location',
address: '13353 Berlin-Wedding, Torfstr. 17',
latitude: 52.541797,
longitude: 13.350042
}, {
maxDuration: 10 // minutes
})
.then(console.log)
.catch(console.error)
```
The response may look like this:
```js
[
{
duration: 2,
stations: [
{
type: 'stop',
id: '900000009101',
name: 'U Amrumer Str.',
location: {type: 'location', latitude: 52.542201, longitude: 13.34953},
products: { /* … */ }
}
]
}, {
duration: 3,
stations: [
{
type: 'stop',
id: '900000001201',
name: 'S+U Westhafen',
location: {type: 'location', latitude: 52.536179, longitude: 13.343839},
products: { /* … */ }
}
// …
]
},
// …
{
duration: 10,
stations: [
{
type: 'stop',
id: '900000001203',
name: 'Döberitzer Str.',
location: {type: 'location', latitude: 52.530668, longitude: 13.36811},
products: { /* … */ }
}
// …
]
}
]
```

View file

@ -1,37 +1,49 @@
# `db-vendo-client` documentation
# API documentation
**[JS API documentation](api.md)**
- [`journeys(from, to, [opt])`](journeys.md) get journeys between locations
- [`refreshJourney(refreshToken, [opt])`](refresh-journey.md) fetch up-to-date/more details of a `journey`
- [`journeysFromTrip(tripId, previousStopover, to, [opt])`](journeys-from-trip.md) get journeys from a trip to a location
- [`trip(id, lineName, [opt])`](trip.md) get details for a trip
- [`tripsByName(lineNameOrFahrtNr, [opt])`](trips-by-name.md) get all trips matching a name
- [`departures(station, [opt])`](departures.md) query the next departures at a station
- [`arrivals(station, [opt])`](arrivals.md) query the next arrivals at a station
- [`locations(query, [opt])`](locations.md) find stations, POIs and addresses
- [`stop(id, [opt])`](stop.md) get details about a stop/station
- [`nearby(location, [opt])`](nearby.md) show stations & POIs around
- [`radar(north, west, south, east, [opt])`](radar.md) find all vehicles currently in a certain area
- [`reachableFrom(address, [opt])`](reachable-from.md)  get all stations reachable from an address within `n` minutes
- [`remarks([opt])`](remarks.md) get all remarks
- [`lines(query, [opt])`](lines.md) get all lines matching a name
- [`serverInfo([opt])`](server-info.md) fetch meta information from HAFAS
[REST API OpenAPI schema](openapi.yaml) ([open in Swagger Editor](https://editor.swagger.io/?url=https://raw.githubusercontent.com/public-transport/db-vendo-client/refs/heads/main/docs/openapi.yaml))
## Migrating from an old `hafas-client` version
## Migrating from an old (v5) `hafas-client` version
`db-vendo-client` tries to be as compatible as possible with `hafas-client` v6. If you were still on v5 or earlier, see the [`5` → `6` migration guide](https://github.com/public-transport/hafas-client/blob/main/docs/migrating-to-6.md) of `hafas-client`.
- [`4` → `5` migration guide](migrating-to-5.md)
## Throttling requests
There's opt-in support for throttling requests to the endpoint.
```js
import {createClient} from 'db-vendo-client'
import {withThrottling} from 'db-vendo-client/throttle.js'
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const createClient = require('hafas-client')
const withThrottling = require('hafas-client/throttle')
const dbProfile = require('hafas-client/p/db')
// create a throttled HAFAS client with Deutsche Bahn profile
const client = createClient(withThrottling(dbProfile), userAgent)
const client = createClient(withThrottling(dbProfile), 'my-awesome-program')
// Berlin Jungfernheide to München Hbf
await client.journeys('8011167', '8000261', {results: 1})
client.journeys('8011167', '8000261', {results: 1})
.then(console.log)
.catch(console.error)
```
You can also pass custom values for the nr of requests (`limit`) per interval into `withThrottling`:
You can pass custom values for the nr of requests (`limit`) per interval into `withThrottling`:
```js
// 2 requests per second
const throttledDbProfile = withThrottling(dbProfile, 2, 1000)
const client = createClient(throttledDbProfile, userAgent)
const client = createClient(throttledDbProfile, 'my-awesome-program')
```
## Retrying failed requests
@ -39,14 +51,17 @@ const client = createClient(throttledDbProfile, userAgent)
There's opt-in support for retrying failed requests to the endpoint.
```js
import {createClient} from 'db-vendo-client'
import {withRetrying} from 'db-vendo-client/retry.js'
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const createClient = require('hafas-client')
const withRetrying = require('hafas-client/retry')
const dbProfile = require('hafas-client/p/db')
// create a client with Deutsche Bahn profile that will retry on HAFAS errors
const client = createClient(withRetrying(dbProfile), userAgent)
const client = createClient(withRetrying(dbProfile), 'my-awesome-program')
// Berlin Jungfernheide to München Hbf
client.journeys('8011167', '8000261', {results: 1})
.then(console.log)
.catch(console.error)
```
You can pass custom options into `withRetrying`. They will be passed into [`retry`](https://github.com/tim-kos/node-retry#tutorial).
@ -58,97 +73,13 @@ const retryingDbProfile = withRetrying(dbProfile, {
minTimeout: 10 * 1000,
factor: 3
})
const client = createClient(retryingDbProfile, userAgent)
const client = createClient(retryingDbProfile, 'my-awesome-program')
```
## User agent randomization
## Writing a profile
By default, `db-vendo-client` does not randomize the client name that you pass into `createClient`, and sends it as [`User-Agent`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) as it is. At least DB Navigator always sends the same user agent as well (cf. `dbnav` profile). You can turn on randomization by setting `profile.randomizeUserAgent` to `false`:
Check [the guide](writing-a-profile.md).
```js
const client = createClient({
...someProfile,
randomizeUserAgent: true,
}, userAgent)
```
## General documentation for `mgate.exe` APIs
## Logging requests
You can use `profile.logRequest` and `profile.logResponse` to process the raw [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), respectively.
As an example, we can implement a custom logger:
```js
import {createClient} from 'db-vendo-client'
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const logRequest = (ctx, fetchRequest, requestId) => {
// ctx looks just like with the other profile.* hooks:
const {dbProfile, opt} = ctx
console.debug(requestId, fetchRequest.headers, fetchRequest.body + '')
}
const logResponse = (ctx, fetchResponse, body, requestId) => {
console.debug(requestId, fetchResponse.headers, body + '')
}
// create a client with Deutsche Bahn profile that debug-logs
const client = createClient({
...dbProfile,
logRequest,
logResponse,
}, userAgent)
```
```js
// logRequest output:
'29d0e3' {
accept: 'application/json',
'accept-encoding': 'gzip, br, deflate',
'content-type': 'application/json',
connection: 'keep-alive',
'user-agent': 'hafas842c51-clie842c51nt debug C842c51LI'
} {"lang":"de","svcReqL":[{"cfg":{"polyEnc":"GPA"},"meth":"LocMatch",…
// logResponse output:
'29d0e3' {
'content-encoding': 'gzip',
'content-length': '1010',
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 06 Oct 2022 12:31:09 GMT',
server: 'Apache',
vary: 'User-Agent'
} {"ver":"1.45","lang":"deu","id":"sb42zgck4mxtxm4s","err":"OK","graph"…
```
The default `profile.logRequest` [`console.error`](https://nodejs.org/docs/latest-v10.x/api/console.html#console_console_error_data_args)s the request body, if you have set `$DEBUG` to `hafas-client`. Likewise, `profile.logResponse` `console.error`s the response body.
## Error handling
Unexpected errors e.g. due to bugs in `db-vendo-client` itself aside, its methods may reject with the following errors:
- `Error` A generic error, e.g. if the DB backend returned a HTTP error.
- `HafasError` A generic error to signal that something HAFAS-related went wrong, either in the client, or in the HAFAS endpoint.
Each `HafasError` error has the following properties:
- `isHafasError` Always `true`. Allows you to distinguish HAFAS-related errors from e.g. network errors.
- `code` A string representing the error type for all other error classes, e.g. `INVALID_REQUEST` for `HafasInvalidRequestError`. `null` for plain `HafasError`s.
- `isCausedByServer` Boolean, telling you if the HAFAS endpoint says that it couldn't process your request because *it* is unavailable/broken.
- `hafasCode` A HAFAS-specific error code, if the HAFAS endpoint returned one; e.g. `H890` when no journeys could be found. `null` otherwise.
- `request` The [Fetch API `Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) of the request.
- `url` The URL of the request.
- `response` The [Fetch API `Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
## Using `db-vendo-client` from another language
If you want to use `db-vendo-client` to access DB APIs but work with non-Node.js environments, you can use it together with [hafas-rest-api](https://github.com/public-transport/hafas-rest-api) to create a REST API (see the [root readme](https://github.com/public-transport/db-vendo-client/tree/main#usage) and the Docker image).
Or use [`hafas-client-rpc`](https://github.com/derhuerst/hafas-client-rpc) to create a [JSON-RPC](https://www.jsonrpc.org) interface that you can send commands to.
## General documentation and notes for DB APIs
[`db-apis.md`](db-apis.md)
[`hafas-mgate-api.md`](hafas-mgate-api.md)

View file

@ -7,10 +7,10 @@ With `opt`, you can override the default options, which look like this:
```js
{
stopovers: false, // return stations on the way?
polylines: false, // return a shape for each leg? mutually exclusive with tickets
tickets: false, // return tickets? mutually exclusive with polylines
subStops: true, // not supported
entrances: true, // not supported
polylines: false, // return a shape for each leg?
tickets: false, // return tickets? only available with some profiles
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
remarks: true, // parse & expose hints & warnings?
language: 'en' // language to get results in
}
@ -18,22 +18,23 @@ With `opt`, you can override the default options, which look like this:
## Response
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} from 'db-vendo-client'
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbProfile, userAgent)
const client = createClient(vbbProfile)
const {journeys} = await client.journeys('8000105', '8000096', {results: 1})
// later, fetch up-to-date info on the journey
const {
journey,
realtimeDataUpdatedAt,
} = await client.refreshJourney(journeys[0].refreshToken, {stopovers: true, remarks: true})
// Hauptbahnhof to Heinrich-Heine-Str.
client.journeys('900000003201', '900000100008', {results: 1})
.then(([journey]) => {
// later, fetch up-to-date info on the journey
client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
.then(console.log)
.catch(console.error)
})
.catch(console.error)
```
`journey` is a *single* [*Friendly Public Transport Format* v2 draft](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md) `journey`, in the same format as returned by [`journeys()`](journeys.md).
`realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
`refreshJourney()` will return a *single* [*Friendly Public Transport Format* v2 draft](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md) `journey`, in the same format as with `journeys()`.

142
docs/remarks.md Normal file
View file

@ -0,0 +1,142 @@
# `remarks([opt])`
**Fetches all remarks known to the HAFAS endpoint**, e.g. warnings about disruptions, planned construction work, and general notices about the operating situation.
With `opt`, you can override the default options, which look like this:
```js
{
results: 100, // maximum number of remarks
// filter by time
from: Date.now(),
to: null,
products: null, // filter by affected products
language: 'en', // depends on the profile
}
```
## Example
As an example, we're going to use the [SVV profile](../p/svv):
```js
const createClient = require('hafas-client')
const svvProfile = require('hafas-client/p/svv')
const client = createClient(svvProfile, 'my-awesome-program')
console.log(await client.remarks())
```
```js
[
{
id: 'HIM_FREETEXT_110342',
type: 'warning',
summary: 'Bus will be running at different times',
text: 'Due to operational changes, this bus will be running at different times. We apologise for any inconvenience this may cause.',
priority: 50,
company: 'KGÖVV',
validFrom: '2020-07-04T00:00:00+02:00',
validUntil: '2020-08-09T23:59:00+02:00',
modified: '2020-07-01T14:39:12+02:00',
products: {
'bahn-s-bahn': true,
'u-bahn': true,
strassenbahn: true,
fernbus: true,
regionalbus: true,
stadtbus: true,
'seilbahn-zahnradbahn': true,
schiff: true,
},
categories: [1],
icon: {type: 'HIM1', title: null},
},
// …
{
id: 'HIM_FREETEXT_110235',
type: 'warning',
summary: 'Linie 7 - Umleitungen',
text: 'Aufgrund einer Baustelle gibt es bei der Linie 7 umfangreiche Umleitungen.',
priority: 100,
company: 'VOR',
validFrom: '2020-07-13T00:00:00+02:00',
validUntil: '2020-08-31T23:59:00+02:00',
modified: '2020-06-30T12:37:38+02:00',
affectedLines: [{
type: 'line',
id: '7',
name: '7',
public: true,
}],
products: {
'bahn-s-bahn': true,
'u-bahn': true,
strassenbahn: true,
fernbus: true,
regionalbus: true,
stadtbus: true,
'seilbahn-zahnradbahn': false,
schiff: true,
},
categories: [1],
icon: {type: 'HIM1', title: null},
},
// …
{
id: 'HIM_FREETEXT_106619',
type: 'warning',
summary: 'Stop Bad Hall Bahnhofstraße can not be approached',
text: 'The stop at Bad Hall Bahnhofstraße can not be approached during 21.04.-24.07.2020. Please use alternatively the stop at Bad Hall Busterminal (Abzw Bahnhofstraße).',
priority: 100,
company: 'OÖVG',
validFrom: '2020-04-21T00:00:00+02:00',
validUntil: '2020-07-24T23:59:00+02:00',
modified: '2020-07-08T12:52:13+02:00',
affectedLines: [{
type: 'line',
id: '452',
name: '452',
public: true,
}],
products: {
'bahn-s-bahn': false,
'u-bahn': false,
strassenbahn: false,
fernbus: false,
regionalbus: true,
stadtbus: false,
'seilbahn-zahnradbahn': false,
schiff: false
},
categories: [1],
icon: {type: 'HIM1', title: null},
},
// …
{
id: 'HIM_FREETEXT_106671',
type: 'warning',
summary: 'Neue Haltestellennamen',
text: 'Im Zuge der Neuordnung der Regionalbusverkehre werden mit 6.7.2020 neue Fahrpläne und Liniennummern wirksam und dadurch können sich mitunter die Haltestellennamen verändern.',
priority: 100,
company: 'VOR',
validFrom: '2020-04-21T00:00:00+02:00',
validUntil: '2020-09-30T23:59:00+02:00',
modified: '2020-04-21T13:20:41+02:00',
products: {
'bahn-s-bahn': true,
'u-bahn': true,
strassenbahn: true,
fernbus: true,
regionalbus: true,
stadtbus: true,
'seilbahn-zahnradbahn': false,
schiff: true,
},
categories: [4],
icon: {type: 'HIM4', title: null},
},
// …
]
```

37
docs/server-info.md Normal file
View file

@ -0,0 +1,37 @@
# `serverInfo([opt])`
**Fetches meta information from the HAFAS endpoint.**
With `opt`, you can override the default options, which look like this:
```js
{
versionInfo: true, // query HAFAS versions?
language: 'en', // depends on the profile
}
```
## Example
As an example, we're going to use the [SVV profile](../p/svv):
```js
const createClient = require('hafas-client')
const svvProfile = require('hafas-client/p/svv')
const client = createClient(svvProfile, 'my-awesome-program')
console.log(await client.serverInfo())
```
```js
{
// version of the HAFAS Connection Interface (HCI), the API that hafas-client uses
hciVersion: '1.23',
timetableStart: '20200517',
timetableEnd: '20201212',
serverTime: '2020-07-19T21:32:12+02:00',
realtimeDataUpdatedAt: 1595187102,
}
```

View file

@ -1,7 +1,5 @@
# `stop(id, [opt])`
This endpoint is not available with `dbweb` profile.
`id` must be in one of these formats:
```js
@ -25,8 +23,8 @@ With `opt`, you can override the default options, which look like this:
```js
{
subStops: true, // not supported
entrances: true, // not supported
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
linesOfStops: false, // parse & expose lines at the stop/station?
language: 'en' // language to get results in
}
@ -34,18 +32,20 @@ With `opt`, you can override the default options, which look like this:
## Response
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} from 'hafas-client'
import {profile as dbProfile} from 'hafas-client/p/db/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbProfile, userAgent)
const client = createClient(vbbProfile, 'my-awesome-program')
await client.stop('900000042101') // U Spichernstr.
client.stop('900000042101') // U Spichernstr.
.then(console.log)
.catch(console.error)
```
The result may look like this:
The response may look like this:
```js
{
@ -73,6 +73,11 @@ The result may look like this:
product: 'subway',
public: true,
name: 'U1',
symbol: 'U',
nr: 1,
metro: false,
express: false,
night: false
},
// …
{
@ -82,6 +87,21 @@ The result may look like this:
product: 'bus',
public: true,
name: 'N9',
symbol: 'N',
nr: 9,
metro: false,
express: false,
night: true
} ]
}
```
If the endpoint returns a list of entrances for a station, the resulting station object will have an `entrances` array looking similar to this:
```js
[
{type: 'location', latitude: 47.411069, longitude: 10.277412},
{type: 'location', latitude: 47.410493, longitude: 10.277223},
{type: 'location', latitude: 47.410754, longitude: 10.278023}
]
```

View file

@ -1,26 +0,0 @@
# automated tests in `db-vendo-client`
Because transit data is inherently dynamic (e.g. a different set of departures being returned for a stop now than in 10 minutes), and because it is of paramount importance that `db-vendo-client` actually works with HAFAS endpoints *as they currently work*, its testing setup is a bit unusual.
`db-vendo-client` has three kinds of automated tests:
- unit tests, which test individual aspects of the case base in isolation (e.g. the parsing of HAFAS-formatted dates & times) run via `npm run test-unit`
- end-to-end (E2E) tests, which run actual HTTP requests against their respective profile's HAFAS endpoint run via `npm run test-e2e`
- integration tests, which are the E2E tests running against pre-recorded (and checked-in) HTTP request fixtures run via `npm run test-integration`
Because the E2E & integration tests are based on the same code, when changing this code, you should also update the integration test fixtures accordingly.
*Note:* In order to be as reproducible as possible, the tests query transit data for a certain *fixed* point in time on the future, hard-coded in each profile's test suite (a.k.a. each file `test/e2e/*.js`). In combination with the recording & mocking of HTTP requests, this effectively makes the integration tests deterministic.
## adding integration test fixtures
As an example, let's assume that we have added an entirely new test to [the *DB* profile's E2E tests](../test/e2e/db.js).
The behaviour of the HTTP request recording (into fixtures) and mocking (using the recorded fixtures) is controlled via an environment variable `$VCR_MODE`:
- By running the test(s) with `VCR_MODE=record`, we can record the HTTP requests being made. The tests will run just like without `$VCR_MODE`, except that they will query data for date+time specified in `T_MOCK` (e.g. [here](https://github.com/public-transport/db-vendo-client/blob/8ff945c07515155380de0acb33584e474d6d547c/test/e2e/db.js#L33)).
- Then, by running the test(s) with `VCR_MODE=playback`, because their HTTP requests match the pre-recorded fixtures, they work on the corresponding mocked HTTP responses.
Usually, you would not want to update all *already existing* recorded HTTP request fixtures of the test suite you have made changes in, as they are unrelated to the test you have added. To only record your *added* test, temporarily change `tap.test(…)` to read `tap.only(…)`, and run with `TAP_ONLY=1 VCR_MODE=record`; This will skip all unrelated tests entirely.
Then, check the augmented fixtures (in `test/e2e/fixtures`) into Git, and revert the `tap.only(…)` change. To make sure that everything works, run the entire test suite/file (*without `TAP_ONLY=1`*) with `VCR_MODE=playback`.
*Note:* It might be that the test suite/file you want to augment hasn't been updated in a while, so that `T_MOCK` is in the past. In this case, recording additional fixtures from actual HTTP requests of your added test is usually not possible because the HAFAS API is unable to serve old transit data. In this case, you will first have to change `T_MOCK` to a future date+time (a weekday as "normal" as possible) and re-record all tests' HTTP requests; Don't hesitate to get in touch with me if you have trouble with this.

View file

@ -1,20 +1,25 @@
# `trip(id, [opt])`
# `trip(id, lineName, [opt])`
This method can be used to refetch information about a trip  a vehicle stopping at a set of stops at specific times.
*Note*: This method is not supported by every profile/endpoint.
Let's say you used [`journeys`](journeys.md) and now want to get more up-to-date data about the arrival/departure of a leg. You'd pass in the trip ID from `leg.tripId`, e.g. `'1|24983|22|86|18062017'`, and the name of the line from `leg.line.name` like this:
```js
import {createClient} from 'db-vendo-client'
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
const client = createClient(dbnavProfile, userAgent)
const client = createClient(vbbProfile, 'my-awesome-program')
const {journeys} = client.journeys('8000096', '8000105', {results: 1})
const leg = journeys[0].legs[0]
await client.trip(leg.tripId)
// Hauptbahnhof to Heinrich-Heine-Str.
client.journeys('900000003201', '900000100008', {results: 1})
.then(([journey]) => {
const leg = journey.legs[0]
return client.trip(leg.tripId, leg.line.name)
})
.then(console.log)
.catch(console.error)
```
With `opt`, you can override the default options, which look like this:
@ -22,9 +27,9 @@ With `opt`, you can override the default options, which look like this:
```js
{
stopovers: true, // return stations on the way?
polyline: false, // return a shape for the trip? only supported with HAFAS trip id (i.e. not with a trip id from a departure/arrival board of the `db` profile)
subStops: true, // not supported
entrances: true, // not supported
polyline: false, // return a shape for the trip?
subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations?
remarks: true, // parse & expose hints & warnings?
language: 'en' // language to get results in
}
@ -34,24 +39,20 @@ With `opt`, you can override the default options, which look like this:
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule.
As an example, we're going to use the [VBB profile](../p/vbb):
```js
import {createClient} from 'db-vendo-client'
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(dbnavProfile)
const client = createClient(vbbProfile)
const {
trip,
realtimeDataUpdatedAt,
} = await client.trip('1|31431|28|86|17122017', 'S9', {
when: 1513534689273,
})
client.trip('1|31431|28|86|17122017', 'S9', {when: 1513534689273})
.then(console.log)
.catch(console.error)
```
`realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
When running the code above, `trip` looked like this:
The response looked like this:
```js
{
@ -138,9 +139,9 @@ When running the code above, `trip` looked like this:
### `polyline` option
Only supported with HAFAS trip id (i.e. not with a trip id from a departure/arrival board of the `db` profile).
If you pass `polyline: true`, the trip will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1). Every `Point` next to a station will have `properties` containing the station's metadata.
If you pass `polyline: true`, the trip will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1).
We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken from the [VBB profile](../p/vbb):
```js
{
@ -148,6 +149,12 @@ If you pass `polyline: true`, the trip will have a `polyline` field, containing
features: [
{
type: 'Feature',
properties: {
type: 'station',
id: '900000070301',
name: 'U Alt-Mariendorf',
/* … */
},
geometry: {
type: 'Point',
coordinates: [13.3875, 52.43993] // longitude, latitude
@ -156,6 +163,12 @@ If you pass `polyline: true`, the trip will have a `polyline` field, containing
/* … */
{
type: 'Feature',
properties: {
type: 'station',
id: '900000017101',
name: 'U Mehringdamm',
/* … */
},
geometry: {
type: 'Point',
coordinates: [13.38892, 52.49448] // longitude, latitude
@ -173,6 +186,12 @@ If you pass `polyline: true`, the trip will have a `polyline` field, containing
},
{
type: 'Feature',
properties: {
type: 'station',
id: '900000089301',
name: 'U Alt-Tegel',
/* … */
},
geometry: {
type: 'Point',
coordinates: [13.28406, 52.58915] // longitude, latitude

108
docs/trips-by-name.md Normal file
View file

@ -0,0 +1,108 @@
# `tripsByName([lineNameOrFahrtNr], [opt])`
Get all trips matching one or more criteria, e.g. a specific name.
## Response
As an example, we're going to use the [VBB profile](../p/vbb):
```js
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
console.log(await client.tripsByName('S1'))
```
With `opt`, you can override the default options, which look like this:
```js
{
// use either this
when: null,
// or these
fromWhen: null, untilWhen: null,
onlyCurrentlyRunning: true,
products: {
// these entries may vary from profile to profile
suburban: true,
subway: true,
tram: true,
bus: true,
ferry: true,
express: true,
regional: true,
},
currentlyStoppingAt: null, // only show trips currently stopping at a stop/station, string
lineName: null, // only show trips with this line name, string
operatorNames: null, // only show trips with these operator names, array of strings
}
```
The response may look like this:
```js
[
{
id: '1|1214|0|86|16092020'
direction: null,
line: {
type: 'line',
id: 's1',
fahrtNr: '325',
name: 'S1',
mode: 'train',
product: 'suburban',
// …
},
origin: {
type: 'stop',
id: '900000550239',
name: 'Warnemünde, Bhf',
location: { /* … */ },
products: { /* … */ },
},
departure: '2020-09-16T04:03:00+02:00',
plannedDeparture: '2020-09-16T04:03:00+02:00',
departureDelay: null,
departurePlatform: null,
plannedDeparturePlatform: null,
destination: {
type: 'stop',
id: '900000550002',
name: 'Rostock, Hbf',
location: { /* … */ },
products: { /* … */ },
},
arrival: '2020-09-16T04:24:00+02:00',
plannedArrival: '2020-09-16T04:24:00+02:00',
arrivalDelay: null,
arrivalPlatform: null,
plannedArrivalPlatform: null,
},
// …
{
id: '1|62554|0|86|16092020'
direction: null,
line: {
type: 'line',
id: 's1',
fahrtNr: '2001',
name: 'S1',
public: true,
mode: 'train',
product: 'suburban',
// …
},
origin: { /* … */ },
destination: { /* … */ },
// …
}
]
```

163
docs/writing-a-profile.md Normal file
View file

@ -0,0 +1,163 @@
# Writing a profile
**Per HAFAS endpoint, `hafas-client` has an endpoint-specific customisation called *profile*.** A profile may, for example, do the following:
- handle the additional requirements of the endpoint (e.g. authentication),
- extract additional information from the data provided by the endpoint,
- guard against triggering bugs of certain endpoints (e.g. time limits).
This guide is about writing such a profile. If you just want to use an already supported endpoint, refer to the [main readme](../readme.md) instead.
*Note*: **If you get stuck, ask for help by [creating an issue](https://github.com/public-transport/hafas-client/issues/new)**; We're happy to help you expand the scope of this library!
## 0. How do the profiles work?
A profile may consist of three things:
- **mandatory details about the HAFAS endpoint**
- `endpoint`: The protocol, host and path of the endpoint.
- `locale`: The [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) [locale](https://en.wikipedia.org/wiki/Locale_(computer_software)) of your endpoint (or the area that your endpoint covers).
- `timezone`: An [IANA-time-zone](https://www.iana.org/time-zones)-compatible [timezone](https://en.wikipedia.org/wiki/Time_zone) of your endpoint.
- **flags indicating which features are supported by the endpoint** e.g. `trip`
- **methods overriding the [default profile](../lib/default-profile.js)**
Let's use a fictional endpoint for [Austria](https://en.wikipedia.org/wiki/Austria) as an example:
```js
const myProfile = {
endpoint: 'https://example.org/bin/mgate.exe',
locale: 'de-AT',
timezone: 'Europe/Vienna'
}
```
Assuming their HAFAS endpoint returns all line names prefixed with `foo `, we can adapt our profile to clean them:
```js
// get the default line parser
const parseLine = require('hafas-client/parse/line')
// wrapper function with additional logic
const parseLineWithoutFoo = (ctx, rawLine) => {
const line = parseLine(ctx, rawLine)
line.name = line.name.replace(/foo /g, '')
return line
}
myProfile.parseLine = parseLineWithoutFoo
```
If you pass this profile into `hafas-client`, the `parseLine` method will override [the default one](../parse/line.js).
You can also use the `parseHook` helper to reduce boilerplate:
```js
const {parseHook} = require('hafas-client/lib/profile-hooks')
const removeFoo = (ctx, rawLine) => ({
...ctx.parsed,
name: line.name.replace(/foo /g, '')
})
myProfile.parseLine = parseHook(parseLine, removeFoo)
```
## 1. Setup
*Note*: There are many ways to find the required values. This way is rather easy and works with most endpoints by now.
1. **Find the journey planning webapp** corresponding to the API endpoint; Usually, you can find it on the public transport provider's website.
2. **Open your [browser's devtools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools)**, switch to the "Network" tab, and **inspect the requests to the HAFAS API**.
If you can't find the webapp or your public transport provider doesn't have one, you can inspect their mobile app's traffic instead:
1. Get an iOS or Android device and **download the "official" app.**
2. **Configure a [man-in-the-middle HTTP proxy](https://docs.mitmproxy.org/stable/concepts-howmitmproxyworks/)** like [mitmproxy](https://mitmproxy.org).
- Configure your device to trust the self-signed SSL certificate, [as outlined in the mitmproxy docs](https://docs.mitmproxy.org/stable/concepts-certificates/).
- *Note*: This method does not work if the app uses [public key pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning). In this case (the app won't be able to query data), please [create an issue](https://github.com/public-transport/hafas-client/issues/new), so we can discuss other techniques.
3. **Record requests of the app.**
- [There's a video showing this step](https://stuff.jannisr.de/how-to-record-hafas-requests.mp4).
- Make sure to cover all relevant sections of the app, e.g. "journeys", "departures", "live map". Better record more than less!
- To help others in the future, post the requests (in their entirety!) on GitHub, e.g. in as format like [this](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886). This will also let us help you if you have any questions.
## 2. Basic profile
*Note:* You should have read the [general documentation on `mgate.exe` APIs](hafas-mgate-api.md) to make sense of the terminology used below.
You may want to start with the [profile boilerplate](profile-boilerplate.js).
- **Identify the `endpoint`.** The protocol, host and path of the endpoint, *but not* the query string.
- *Note*: **`hafas-client` for now only supports the interface providing JSON** (generated from XML), which is being used by the corresponding iOS/Android apps. It supports neither the JSONP, nor the XML, nor the HTML interface. If the endpoint does not end in `mgate.exe`, it mostly likely won't work.
- **Identify the `locale`.** Basically guess work; Use the date & time formats as an indicator.
- **Identify the `timezone`.** This may be tricky, a for example [Deutsche Bahn](https://en.wikipedia.org/wiki/Deutsche_Bahn) returns departures for Moscow as `+01:00` instead of `+03:00`.
- **Copy the authentication** and other meta fields, namely `ver`, `ext`, `client` and `lang`.
- You can find these fields in the root of each request JSON. Check [a HVV request](https://gist.github.com/derhuerst/5a9d29a556b54182f9d30202f7244bfd#file-journeys-http-L11-L54) and [the corresponding HVV profile](https://github.com/public-transport/hafas-client/blob/99142acf8b156599daa69f2e1470901088827982/p/hvv/index.js#L5-L23) for an example.
- Add a function `transformReqBody(ctx, body)` to your profile, which adds the fields to `body`. todo: adapt this
- Some profiles have a `checksum` parameter (like [here](https://gist.github.com/derhuerst/2a735268bd82a0a6779633f15dceba33#file-journey-details-1-http-L1)) or two `mic` & `mac` parameters (like [here](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886#file-1-http-L1)). If you see one of them in your requests, jump to the [*Authentication* section of the `mgate.exe` docs](hafas-mgate-api.md#authentication). Unfortunately, this is necessary to get the profile working.
## 3. Products
In `hafas-client`, there's a distinction between the `mode` and the `product` fields:
- The `mode` field describes the mode of transport in general. [Standardised by the *Friendly Public Transport Format*](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#modes), it is on purpose limited to a very small number of possible values, e.g. `train` or `bus`.
- The value for `product` relates to how a means of transport "works" *in local context*. Example: Even though [*S-Bahn*](https://en.wikipedia.org/wiki/Berlin_S-Bahn) and [*U-Bahn*](https://en.wikipedia.org/wiki/Berlin_U-Bahn) in Berlin are both `train`s, they have different operators, service patterns, stations and look different. Therefore, they are two distinct `product`s `subway` and `suburban`.
**Specify `product`s that appear in the app** you recorded requests of. For a fictional transit network, this may look like this:
```js
const products = [
{
id: 'commuterTrain',
mode: 'train',
bitmasks: [16],
name: 'ACME Commuter Rail',
short: 'CR',
default: true
},
{
id: 'metro',
mode: 'train',
bitmasks: [8],
name: 'Foo Bar Metro',
short: 'M',
default: true
}
]
```
Let's break this down:
- `id`: A sensible, [camelCased](https://en.wikipedia.org/wiki/Camel_case#Variations_and_synonyms), alphanumeric identifier. Use it for the key in the `products` array as well.
- `mode`: A [valid *Friendly Public Transport Format* mode](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#modes).
- `bitmasks`: HAFAS endpoints work with a [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)#Arguments_to_functions) that toggles the individual products. It should be an array of values that toggle the appropriate bit(s) in the bitmask (see below).
- `name`: A short, but distinct name for the means of transport, *just precise enough in local context*, and in the local language. In Berlin, `S-Bahn-Schnellzug` would be too much, because everyone knows what `S-Bahn` means.
- `short`: The shortest possible symbol that identifies the product.
- `default`: Should the product be used for queries (e.g. journeys) by default?
If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.2.1) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements.
### Finding the right values for the `bitmasks` field
As shown in [the video](https://stuff.jannisr.de/how-to-record-hafas-requests.mp4), search for a journey and toggle off one product at a time, recording the requests. After extracting the products bitmask ([example](https://gist.github.com/derhuerst/193ef489f8aa50c2343f8bf1f2a22069#file-via-http-L34)) you will end up with values looking like these:
```
toggles value binary subtraction bit(s)
all products 31 11111 31 - 0
all but ACME Commuter Rail 15 01111 31 - 2^4 2^4
all but Foo Bar Metro 23 10111 31 - 2^3 2^3
all but product E 25 11001 31 - 2^2 - 2^1 2^2, 2^1
all but product F 30 11110 31 - 2^0 2^0
```
## 4. Additional info
We consider these improvements to be *optional*:
- **Check if the endpoint supports the `trip()` call.**
- In the app, check if you can re-fetch details for the status of a single journey leg. It should load realtime delays and the current progress.
- If this feature is supported, add `trip: true` to the profile.
- **Check if the endpoint supports the live map call.** Does the app have a "live map" showing all vehicles within an area? If so, add `radar: true` to the profile.
- **Consider transforming station & line names** into the formats that's most suitable for *local users*. This is just an optimal optimisation that makes it easier for users of the profile to use the data. Some examples:
- `M13 (Tram)` -> `M13`. With Berlin context, it is obvious that `M13` is a tram.
- `Berlin Jungfernheide Bhf` -> `Berlin Jungfernheide`. With local context, it's obvious that *Jungfernheide* is a train station.
- **Check if the endpoint has non-obvious limitations** and let use know about these. Examples:
- Some endpoints have a time limit, after which they won't return more departures, but silently discard them.

View file

@ -1,62 +0,0 @@
import eslintPluginJs from '@eslint/js';
import eslintPluginStylistic from '@stylistic/eslint-plugin';
import globals from 'globals';
const config = [
eslintPluginJs.configs.recommended,
eslintPluginStylistic.configs['all-flat'],
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 'latest',
globals: {
...globals.node,
},
sourceType: 'module',
},
rules: {
'@stylistic/array-bracket-newline': ['error', 'consistent'],
'@stylistic/array-element-newline': ['error', 'consistent'],
'@stylistic/arrow-parens': 'off',
'@stylistic/comma-dangle': ['error', 'always-multiline'],
'@stylistic/dot-location': ['error', 'property'],
'@stylistic/function-call-argument-newline': ['error', 'consistent'],
'@stylistic/function-paren-newline': 'off',
'@stylistic/indent': ['error', 'tab'],
'@stylistic/indent-binary-ops': ['error', 'tab'],
'@stylistic/max-len': 'off',
'@stylistic/multiline-comment-style': 'off',
'@stylistic/multiline-ternary': ['error', 'always-multiline'],
'@stylistic/newline-per-chained-call': ['error', {ignoreChainWithDepth: 1}],
'@stylistic/no-extra-parens': 'off',
'@stylistic/no-mixed-operators': 'off',
'@stylistic/no-tabs': 'off',
'@stylistic/object-property-newline': 'off',
'@stylistic/one-var-declaration-per-line': 'off',
'@stylistic/operator-linebreak': ['error', 'before'],
'@stylistic/padded-blocks': 'off',
'@stylistic/quote-props': ['error', 'consistent-as-needed'],
'@stylistic/quotes': ['error', 'single'],
'curly': 'error',
'no-implicit-coercion': 'error',
'no-unused-vars': [
'error',
{
vars: 'all',
args: 'none',
ignoreRestSiblings: false,
},
],
},
},
{
files: ['test/**', '**/example.js'],
rules: {
'no-unused-vars': 'off',
'@stylistic/semi': 'off',
},
},
];
export default config;

View file

@ -1,27 +1,25 @@
import {formatLocationIdentifier} from './location-identifier.js';
import {formatCoord} from './coord.js';
'use strict'
const formatLocationIdentifier = require('./location-identifier')
const formatCoord = require('./coord')
const formatAddress = (a) => {
if (a.type !== 'location' || !a.latitude || !a.longitude || !a.address) {
throw new TypeError('invalid address');
throw new TypeError('invalid address')
}
const data = {
A: '2', // address?
O: a.address,
X: formatCoord(a.longitude),
Y: formatCoord(a.latitude),
};
if (a.id) {
data.L = a.id;
Y: formatCoord(a.latitude)
}
if (a.id) data.L = a.id
return {
type: 'A', // address
name: a.address,
lid: formatLocationIdentifier(data),
};
};
lid: formatLocationIdentifier(data)
}
}
export {
formatAddress,
};
module.exports = formatAddress

View file

@ -1,5 +1,5 @@
const formatCoord = x => Math.round(x * 1000000);
'use strict'
export {
formatCoord,
};
const formatCoord = x => Math.round(x * 1000000)
module.exports = formatCoord

View file

@ -1,24 +1,22 @@
import {DateTime, IANAZone} from 'luxon';
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js';
'use strict'
const {DateTime, IANAZone} = require('luxon')
const timezones = new WeakMap()
// todo: change to `(profile) => (when) => {}`
const formatDate = (profile, when) => {
let timezone;
if (timezones.has(profile)) {
timezone = timezones.get(profile);
} else {
timezone = new IANAZone(profile.timezone);
timezones.set(profile, timezone);
let timezone
if (timezones.has(profile)) timezone = timezones.get(profile)
else {
timezone = new IANAZone(profile.timezone)
timezones.set(profile, timezone)
}
return DateTime
.fromMillis(Number(when), {
return DateTime.fromMillis(+when, {
locale: profile.locale,
zone: timezone,
})
.toFormat('yyyy-MM-dd');
};
zone: timezone
}).toFormat('yyyyMMdd')
}
export {
formatDate,
};
module.exports = formatDate

11
format/filters.js Normal file
View file

@ -0,0 +1,11 @@
'use strict'
const bike = {type: 'BC', mode: 'INC'}
const accessibility = {
none: {type: 'META', mode: 'INC', meta: 'notBarrierfree'},
partial: {type: 'META', mode: 'INC', meta: 'limitedBarrierfree'},
complete: {type: 'META', mode: 'INC', meta: 'completeBarrierfree'}
}
module.exports = {bike, accessibility}

13
format/index.js Normal file
View file

@ -0,0 +1,13 @@
'use strict'
module.exports = {
date: require('./date'),
time: require('./time'),
filters: require('./filters'),
station: require('./station'),
address: require('./address'),
poi: require('./poi'),
location: require('./location'),
locationFilter: require('./location-filter'),
rectangle: require('./rectangle')
}

12
format/lines-req.js Normal file
View file

@ -0,0 +1,12 @@
'use strict'
const formatLinesReq = (ctx, query) => {
return {
meth: 'LineMatch',
req: {
input: query,
}
}
}
module.exports = formatLinesReq

View file

@ -0,0 +1,8 @@
'use strict'
const formatLocationFilter = (stops, addresses, poi) => {
if (stops && addresses && poi) return 'ALL'
return (stops ? 'S' : '') + (addresses ? 'A' : '') + (poi ? 'P' : '')
}
module.exports = formatLocationFilter

View file

@ -1,18 +1,16 @@
const sep = '@';
'use strict'
const sep = '@'
const formatLocationIdentifier = (data) => {
let str = '';
let str = ''
for (let key in data) {
if (!Object.prototype.hasOwnProperty.call(data, key)) {
continue;
if (!Object.prototype.hasOwnProperty.call(data, key)) continue
str += key + '=' + data[key] + sep // todo: escape, but how?
}
str += key + '=' + data[key] + sep; // todo: escape, but how?
}
return str
}
return str;
};
export {
formatLocationIdentifier,
};
module.exports = formatLocationIdentifier

View file

@ -1,25 +1,17 @@
'use strict'
const formatLocation = (profile, l, name = 'location') => {
if ('string' === typeof l) {
return profile.formatStation(l);
}
if ('string' === typeof l) return profile.formatStation(l)
if ('object' === typeof l && !Array.isArray(l)) {
if (l.type === 'station' || l.type === 'stop') {
return profile.formatStation(l.id);
return profile.formatStation(l.id)
}
if (l.poi) {
return profile.formatPoi(l);
if (l.poi) return profile.formatPoi(l)
if ('string' === typeof l.address) return profile.formatAddress(l)
if (!l.type) throw new TypeError(`missing ${name}.type`)
throw new TypeError(`invalid ${name}.type: ${l.type}`)
}
if ('string' === typeof l.address) {
return profile.formatAddress(l);
}
if (!l.type) {
throw new TypeError(`missing ${name}.type`);
}
throw new TypeError(`invalid ${name}.type: ${l.type}`);
}
throw new TypeError(name + ': valid station, address or poi required.');
};
throw new TypeError(name + ': valid station, address or poi required.')
}
export {
formatLocation,
};
module.exports = formatLocation

20
format/locations-req.js Normal file
View file

@ -0,0 +1,20 @@
'use strict'
const formatLocationsReq = (ctx, query) => {
const {profile, opt} = ctx
return {
cfg: {polyEnc: 'GPA'},
meth: 'LocMatch',
req: {input: {
loc: {
type: profile.formatLocationFilter(opt.stops, opt.addresses, opt.poi),
name: opt.fuzzy ? query + '?' : query
},
maxLoc: opt.results,
field: 'S' // todo: what is this?
}}
}
}
module.exports = formatLocationsReq

View file

@ -1,65 +0,0 @@
const c = {
NONE: Symbol('no loyalty card'),
BAHNCARD: Symbol('Bahncard'),
VORTEILSCARD: Symbol('VorteilsCard'),
HALBTAXABO: Symbol('HalbtaxAbo'),
VOORDEELURENABO: Symbol('Voordeelurenabo'),
SHCARD: Symbol('SH-Card'),
GENERALABONNEMENT: Symbol('General-Abonnement'),
NL_40: Symbol('NL-40%'),
AT_KLIMATICKET: Symbol('AT-KlimaTicket'),
};
const formatLoyaltyCard = (data) => {
if (!data) {
return {
art: 'KEINE_ERMAESSIGUNG',
klasse: 'KLASSENLOS',
};
}
const cls = data.class === 1 ? 'KLASSE_1' : 'KLASSE_2';
if (data.type.toString() === c.BAHNCARD.toString()) {
return {
art: 'BAHNCARD' + (data.business ? 'BUSINESS' : '') + data.discount,
klasse: cls,
};
}
if (data.type.toString() === c.VORTEILSCARD.toString()) {
return {
art: 'A-VORTEILSCARD',
klasse: 'KLASSENLOS',
};
}
if (data.type.toString() === c.HALBTAXABO.toString()) {
return {
art: 'CH-HALBTAXABO_OHNE_RAILPLUS',
klasse: 'KLASSENLOS',
};
}
if (data.type.toString() === c.GENERALABONNEMENT.toString()) {
return {
art: 'CH-GENERAL-ABONNEMENT',
klasse: cls,
};
}
if (data.type.toString() === c.NL_40.toString()) {
return {
art: 'NL-40_OHNE_RAILPLUS',
klasse: 'KLASSENLOS',
};
}
if (data.type.toString() === c.AT_KLIMATICKET.toString()) {
return {
art: 'KLIMATICKET_OE',
klasse: 'KLASSENLOS',
};
}
return {
art: 'KEINE_ERMAESSIGUNG',
klasse: 'KLASSENLOS',
};
};
export {
c as data,
formatLoyaltyCard,
};

28
format/nearby-req.js Normal file
View file

@ -0,0 +1,28 @@
'use strict'
const nearbyReq = (ctx, location) => {
const {profile, opt} = ctx
return {
cfg: {polyEnc: 'GPA'},
meth: 'LocGeoPos',
req: {
ring: {
cCrd: {
x: profile.formatCoord(location.longitude),
y: profile.formatCoord(location.latitude)
},
maxDist: opt.distance || -1,
minDist: 0
},
locFltrL: [
profile.formatProductsFilter(ctx, opt.products || {}),
],
getPOIs: !!opt.poi,
getStops: !!opt.stops,
maxLoc: opt.results
}
}
}
module.exports = nearbyReq

View file

@ -1,10 +1,11 @@
import {formatLocationIdentifier} from './location-identifier.js';
import {formatCoord} from './coord.js';
'use strict'
const formatLocationIdentifier = require('./location-identifier')
const formatCoord = require('./coord')
const formatPoi = (p) => {
// todo: use Number.isFinite()!
if (p.type !== 'location' || !p.latitude || !p.longitude || !p.id || !p.name) {
throw new TypeError('invalid POI');
throw new TypeError('invalid POI')
}
return {
@ -15,11 +16,9 @@ const formatPoi = (p) => {
O: p.name,
L: p.id,
X: formatCoord(p.longitude),
Y: formatCoord(p.latitude),
}),
};
};
Y: formatCoord(p.latitude)
})
}
}
export {
formatPoi,
};
module.exports = formatPoi

View file

@ -1,46 +1,35 @@
const isObj = element => element !== null && 'object' === typeof element && !Array.isArray(element);
'use strict'
const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
const isObj = require('lodash/isObject')
const formatProductsFilter = (ctx, filter, key = 'vendo') => {
if (!isObj(filter)) {
throw new TypeError('products filter must be an object');
}
const {profile} = ctx;
const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k)
const byProduct = {};
const defaultProducts = {};
const formatProductsFilter = (ctx, filter) => {
if (!isObj(filter)) throw new TypeError('products filter must be an object')
const {profile} = ctx
const byProduct = {}
const defaultProducts = {}
for (let product of profile.products) {
byProduct[product.id] = product;
defaultProducts[product.id] = product.default;
byProduct[product.id] = product
defaultProducts[product.id] = product.default
}
filter = Object.assign({}, defaultProducts, filter);
filter = Object.assign({}, defaultProducts, filter)
let products = [];
let foundDeselected = false;
let res = 0, products = 0
for (let product in filter) {
if (!hasProp(filter, product) || filter[product] !== true) {
foundDeselected = true;
continue;
}
if (!byProduct[product]) {
throw new TypeError('unknown product ' + product);
}
products.push(byProduct[product][key]);
}
if (products.length === 0) {
throw new Error('no products used');
}
if (!foundDeselected && key == 'ris') {
return undefined;
}
if (!foundDeselected && key == 'dbnav') {
return ['ALL'];
if (!hasProp(filter, product) || filter[product] !== true) continue
if (!byProduct[product]) throw new TypeError('unknown product ' + product)
products++
for (let bitmask of byProduct[product].bitmasks) res = res | bitmask
}
if (products === 0) throw new Error('no products used')
return products;
};
return {
type: 'PROD',
mode: 'INC',
value: res + ''
}
}
export {
formatProductsFilter,
};
module.exports = formatProductsFilter

29
format/radar-req.js Normal file
View file

@ -0,0 +1,29 @@
'use strict'
const formatRadarReq = (ctx, north, west, south, east) => {
const {profile, opt} = ctx
return {
meth: 'JourneyGeoPos',
req: {
maxJny: opt.results,
onlyRT: false, // todo: does this mean "only realtime"?
date: profile.formatDate(profile, opt.when),
time: profile.formatTime(profile, opt.when),
// todo: would a ring work here as well?
rect: profile.formatRectangle(profile, north, west, south, east),
perSize: opt.duration * 1000,
perStep: Math.round(opt.duration / Math.max(opt.frames, 1) * 1000),
ageOfReport: true, // todo: what is this?
jnyFltrL: [
profile.formatProductsFilter(ctx, opt.products || {})
],
// todo: what is this? what about realtime?
// - CALC
// - CALC_REPORT (as seen in the INSA Young app)
trainPosMode: 'CALC',
}
}
}
module.exports = formatRadarReq

View file

@ -0,0 +1,22 @@
'use strict'
const formatReachableFromReq = (ctx, address) => {
const {profile, opt} = ctx
return {
meth: 'LocGeoReach',
req: {
loc: profile.formatLocation(profile, address, 'address'),
maxDur: opt.maxDuration === null ? -1 : opt.maxDuration,
maxChg: opt.maxTransfers,
date: profile.formatDate(profile, opt.when),
time: profile.formatTime(profile, opt.when),
period: 120, // todo: what is this?
jnyFltrL: [
profile.formatProductsFilter(ctx, opt.products || {})
]
}
}
}
module.exports = formatReachableFromReq

16
format/rectangle.js Normal file
View file

@ -0,0 +1,16 @@
'use strict'
const formatRectangle = (profile, north, west, south, east) => {
return {
llCrd: {
x: profile.formatCoord(west),
y: profile.formatCoord(south)
},
urCrd: {
x: profile.formatCoord(east),
y: profile.formatCoord(north)
}
}
}
module.exports = formatRectangle

View file

@ -0,0 +1,25 @@
'use strict'
const formatRefreshJourneyReq = (ctx, refreshToken) => {
// eslint-disable-next-line no-unused-vars
const {profile, opt} = ctx
const req = {
getIST: true, // todo: make an option
getPasslist: !!opt.stopovers,
getPolyline: !!opt.polylines,
getTariff: !!opt.tickets
}
if (profile.refreshJourneyUseOutReconL) {
req.outReconL = [{ctx: refreshToken}]
} else {
req.ctxRecon = refreshToken
}
return {
meth: 'Reconstruction',
req,
}
}
module.exports = formatRefreshJourneyReq

Some files were not shown because too many files have changed in this diff Show more