added all files to project

This commit is contained in:
2022-03-10 10:36:59 +01:00
parent 09dd957b33
commit 46a936d7de
25351 changed files with 3883356 additions and 0 deletions
@@ -0,0 +1,3 @@
# `arrivals(station, [opt])`
Just like [`departures(station, [opt])`](departures.md), except that it gives arrival times instead of departure times.
@@ -0,0 +1,882 @@
# Changelog
## `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
@@ -0,0 +1,198 @@
# `departures(station, [opt])`
`station` must be in one of these formats:
```js
// a station ID, in a format compatible to the profile you use
'900000013102'
// an FPTF `station` object
{
type: 'station',
id: '900000013102',
name: 'foo station',
location: {
type: 'location',
latitude: 1.23,
longitude: 3.21
}
}
```
With `opt`, you can override the default options, which look like this:
```js
{
when: new Date(),
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, // 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?
// departures at related stations
// e.g. those that belong together on the metro map.
includeRelatedStations: true,
language: 'en' // language to get results in
}
```
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 the `tripId` field into [`trip(id, lineName, [opt])`](trip.md) to get details on the vehicle's trip.
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')
// S Charlottenburg
client.departures('900000024101', {duration: 3})
.then(console.log)
.catch(console.error)
```
The response may look like this:
```js
[ {
tripId: '1|31431|28|86|17122017',
trip: 31431,
direction: 'S Spandau',
line: {
type: 'line',
id: '18299',
fahrtNr: '12345',
mode: 'train',
product: 'suburban',
public: true,
name: 'S9',
symbol: 'S',
nr: 9,
metro: false,
express: false,
night: false,
operator: {
type: 'operator',
id: 's-bahn-berlin-gmbh',
name: 'S-Bahn Berlin GmbH'
}
},
currentTripPosition: {
type: 'location',
latitude: 52.500851,
longitude: 13.283755,
},
stop: {
type: 'station',
id: '900000024101',
name: 'S Charlottenburg',
location: {
type: 'location',
latitude: 52.504806,
longitude: 13.303846
},
products: {
suburban: true,
subway: false,
tram: false,
bus: true,
ferry: false,
express: false,
regional: true
}
},
when: '2017-12-17T19:32:00+01:00',
plannedWhen: '2017-12-17T19:32:00+01:00',
delay: null,
platform: '2',
plannedPlatform: '2'
}, {
cancelled: true,
tripId: '1|30977|8|86|17122017',
trip: 30977,
direction: 'S Westkreuz',
line: {
type: 'line',
id: '16441',
fahrtNr: '54321',
mode: 'train',
product: 'suburban',
public: true,
name: 'S5',
symbol: 'S',
nr: 5,
metro: false,
express: false,
night: false,
operator: { /* … */ }
},
currentTripPosition: {
type: 'location',
latitude: 52.505004,
longitude: 13.322391,
},
stop: { /* … */ },
when: null,
plannedWhen: '2017-12-17T19:33:00+01:00'
delay: null,
platform: null,
plannedPlatform: '2',
prognosedPlatform: '2'
}, {
tripId: '1|28671|4|86|17122017',
trip: 28671,
direction: 'U Rudow',
line: {
type: 'line',
id: '19494',
fahrtNr: '11111',
mode: 'train',
product: 'subway',
public: true,
name: 'U7',
symbol: 'U',
nr: 7,
metro: false,
express: false,
night: false,
operator: { /* … */ }
},
currentTripPosition: {
type: 'location',
latitude: 52.49864,
longitude: 13.307622,
},
stop: { /* … */ },
when: '2017-12-17T19:35:00+01:00',
plannedWhen: '2017-12-17T19:35:00+01:00',
delay: 0,
platform: null,
plannedPlatform: null
} ]
```
@@ -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)
@@ -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).
@@ -0,0 +1,357 @@
# `journeys(from, to, [opt])`
`from` and `to` each must be in one of these formats:
```js
// a station ID, in a format compatible to the profile you use
'900000013102'
// an FPTF `station` object
{
type: 'station',
id: '900000013102',
name: 'foo station',
location: {
type: 'location',
latitude: 1.23,
longitude: 3.21
}
}
// a point of interest, which is an FPTF `location` object
{
type: 'location',
id: '123',
poi: true,
name: 'foo restaurant',
latitude: 1.23,
longitude: 3.21
}
// an address, which is an FTPF `location` object
{
type: 'location',
address: 'foo street 1',
latitude: 1.23,
longitude: 3.21
}
```
With `opt`, you can override the default options, which look like this:
```js
{
// Use either `departure` or `arrival` to specify a date/time.
departure: new Date(),
arrival: null,
earlierThan: null, // ref to get journeys earlier than the last query
laterThan: null, // ref to get journeys later than the last query
results: null, // number of journeys – `null` means "whatever HAFAS returns"
via: null, // let journeys pass this station
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', // 'none', 'partial' or 'complete'
bike: false, // only bike-friendly journeys
walkingSpeed: 'normal', // 'slow', 'normal', 'fast'
// Consider walking to nearby stations at the beginning of a journey?
startWithWalking: true,
products: {
// these entries may vary from profile to profile
suburban: true,
subway: true,
tram: true,
bus: true,
ferry: true,
express: true,
regional: true
},
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, // parse which days each journey is valid on
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')
// Hauptbahnhof to Heinrich-Heine-Str.
client.journeys('900000003201', '900000100008', {
results: 1,
stopovers: true
})
.then(console.log)
.catch(console.error)
```
The `Promise` returned by `journeys()` will resolve with an object with the `journeys` & `earlierRef`/`laterRef` fields. It might look like this:
```js
{
journeys: [ {
legs: [ {
tripId: '1|32615|6|86|10072018',
direction: 'S Ahrensfelde',
line: {
type: 'line',
id: '16845',
fahrtNr: '12345',
name: 'S7',
public: true,
mode: 'train',
product: 'suburban',
operator: {
type: 'operator',
id: 's-bahn-berlin-gmbh',
name: 'S-Bahn Berlin GmbH'
},
symbol: 'S',
nr: 7,
metro: false,
express: false,
night: false
},
currentLocation: {
type: 'location',
latitude: 52.51384,
longitude: 13.526806,
},
origin: {
type: 'station',
id: '900000003201',
name: 'S+U Berlin Hauptbahnhof',
location: {
type: 'location',
latitude: 52.52585,
longitude: 13.368928
},
products: {
suburban: true,
subway: true,
tram: true,
bus: true,
ferry: false,
express: true,
regional: true
}
},
departure: '2018-07-10T23:54:00+02:00',
plannedDeparture: '2018-07-10T23:53:00+02:00',
departureDelay: 60,
departurePlatform: '15',
plannedDeparturePlatform: '14',
destination: {
type: 'station',
id: '900000100004',
name: 'S+U Jannowitzbrücke',
location: {
type: 'location',
latitude: 52.504806,
longitude: 13.303846
},
products: { /* … */ }
},
arrival: '2018-07-11T00:02:00+02:00',
plannedArrival: '2018-07-11T00:01:00+02:00',
arrivalDelay: 60,
arrivalPlatform: '3',
plannedArrivalPlatform: '3',
stopovers: [ {
stop: {
type: 'station',
id: '900000003201',
name: 'S+U Berlin Hauptbahnhof',
/* … */
},
arrival: null,
plannedArrival: null,
arrivalPlatform: null,
plannedArrivalPlatform: null,
departure: null,
plannedDeparture: null,
departurePlatform: null,
plannedDeparturePlatform: null,
remarks: [
{type: 'hint', code: 'bf', text: 'barrier-free'},
{type: 'hint', code: 'FB', text: 'Bicycle conveyance'}
]
}, {
stop: {
type: 'station',
id: '900000100001',
name: 'S+U Friedrichstr.',
/* … */
},
cancelled: true,
arrival: null,
plannedArrival: '2018-07-10T23:55:00+02:00',
prognosedArrival: '2018-07-10T23:56:00+02:00',
arrivalDelay: 60,
arrivalPlatform: null,
plannedArrivalPlatform: null,
departure: null,
plannedDeparture: '2018-07-10T23:56:00+02:00',
prognosedDeparture: '2018-07-10T23:57:00+02:00',
departureDelay: 60,
departurePlatform: null,
plannedDeparturePlatform: null,
remarks: [ /* … */ ]
},
/* … */
{
stop: {
type: 'station',
id: '900000100004',
name: 'S+U Jannowitzbrücke',
/* … */
},
arrival: '2018-07-11T00:02:00+02:00',
plannedArrival: '2018-07-11T00:01:00+02:00',
arrivalDelay: 60,
arrivalPlatform: null,
plannedArrivalPlatform: null,
departure: '2018-07-11T00:02:00+02:00',
plannedDeparture: '2018-07-11T00:02:00+02:00',
departureDelay: null,
departurePlatform: null,
plannedDeparturePlatform: null,
remarks: [ /* … */ ]
} ]
}, {
public: true,
walking: true,
distance: 558,
origin: {
type: 'station',
id: '900000100004',
name: 'S+U Jannowitzbrücke',
location: { /* … */ },
products: { /* … */ }
},
departure: '2018-07-11T00:01:00+02:00',
destination: {
type: 'station',
id: '900000100008',
name: 'U Heinrich-Heine-Str.',
location: { /* … */ },
products: { /* … */ }
},
arrival: '2018-07-11T00:10:00+02:00'
} ]
} ],
earlierRef: '…', // use with the `earlierThan` option
laterRef: '…' // use with the `laterThan` option
}
```
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:
```js
const hbf = '900000003201'
const heinrichHeineStr = '900000100008'
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
return client.journeys(hbf, heinrichHeineStr, {
laterThan: res.laterRef
})
})
.then((laterRes) => {
const firstLaterJourney = laterRes.journeys[laterRes.journeys.length - 1]
console.log('departure of first (later) journey', firstLaterJourney.legs[0].departure)
})
.catch(console.error)
```
```
departure of last journey 2017-12-17T19:07:00+01:00
departure of first (later) journey 2017-12-17T19:19:00+01:00
```
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
{
'2018-01-01': true,
'2018-01-02': false,
// …
'2018-10-12': true,
'2018-10-13': true,
// …
'2019-01-02': false,
'2019-01-03': false
}
```
@@ -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"
],
},
]
```
@@ -0,0 +1,72 @@
# `locations(query, [opt])`
`query` must be an string (e.g. `'Alexanderplatz'`).
With `opt`, you can override the default options, which look like this:
```js
{
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 // 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
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
client.locations('Alexanderplatz', {results: 3})
.then(console.log)
.catch(console.error)
```
The response may look like this:
```js
[ {
type: 'stop',
id: '900000100003',
name: 'S+U Alexanderplatz',
location: {
type: 'location',
latitude: 52.521508,
longitude: 13.411267
},
products: {
suburban: true,
subway: true,
tram: true,
bus: true,
ferry: false,
express: false,
regional: true
}
}, { // point of interest
type: 'location',
id: '900980709',
poi: true,
name: 'Berlin, Holiday Inn Centre Alexanderplatz****',
latitude: 52.523549,
longitude: 13.418441
}, { // point of interest
type: 'location',
id: '900980176',
poi: true,
name: 'Berlin, Hotel Agon am Alexanderplatz',
latitude: 52.524556,
longitude: 13.420266
} ]
```
@@ -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
@@ -0,0 +1,86 @@
# `nearby(location, [opt])`
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).
With `opt`, you can override the default options, which look like this:
```js
{
results: 8, // maximum number of results
distance: null, // maximum walking distance in meters
poi: false, // return points of interest?
stops: true, // return stops/stations?
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
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
client.nearby({
type: 'location',
latitude: 52.5137344,
longitude: 13.4744798
}, {distance: 400})
.then(console.log)
.catch(console.error)
```
The response may look like this:
```js
[ {
type: 'stop',
id: '900000120001',
name: 'S+U Frankfurter Allee',
location: {
type: 'location',
latitude: 52.513616,
longitude: 13.475298
},
products: {
suburban: true,
subway: true,
tram: true,
bus: true,
ferry: false,
express: false,
regional: false
},
distance: 56
}, {
type: 'stop',
id: '900000120540',
name: 'Scharnweberstr./Weichselstr.',
location: {
type: 'location',
latitude: 52.512339,
longitude: 13.470174
},
products: { /* … */ },
distance: 330
}, {
type: 'stop',
id: '900000160544',
name: 'Rathaus Lichtenberg',
location: {
type: 'location',
latitude: 52.515908,
longitude: 13.479073
},
products: { /* … */ },
distance: 394
} ]
```
@@ -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
@@ -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.
@@ -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: { /* … */ }
}
// …
]
}
]
```
@@ -0,0 +1,85 @@
# API documentation
- [`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
## Migrating from an old `hafas-client` version
- [`4` → `5` migration guide](migrating-to-5.md)
## Throttling requests
There's opt-in support for throttling requests to the endpoint.
```js
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), '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 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, 'my-awesome-program')
```
## Retrying failed requests
There's opt-in support for retrying failed requests to the endpoint.
```js
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), '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).
```js
// retry 2 times, after 10 seconds & 30 seconds
const retryingDbProfile = withRetrying(dbProfile, {
retries: 2,
minTimeout: 10 * 1000,
factor: 3
})
const client = createClient(retryingDbProfile, 'my-awesome-program')
```
## Writing a profile
Check [the guide](writing-a-profile.md).
## General documentation for `mgate.exe` APIs
[`hafas-mgate-api.md`](hafas-mgate-api.md)
@@ -0,0 +1,40 @@
# `refreshJourney(refreshToken, [opt])`
`refreshToken` must be a string, taken from `journey.refreshToken`.
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?
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
}
```
## 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)
// 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)
```
`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()`.
@@ -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},
},
// …
]
```
@@ -0,0 +1,33 @@
# `serverInfo([opt])`
**Fetches meta information from the HAFAS endpoint.**
With `opt`, you can override the default options, which look like this:
```js
{
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
{
timetableStart: '20200517',
timetableEnd: '20201212',
serverTime: '2020-07-19T21:32:12+02:00',
realtimeDataUpdatedAt: 1595187102,
}
```
+107
View File
@@ -0,0 +1,107 @@
# `stop(id, [opt])`
`id` must be in one of these formats:
```js
// a stop/station ID, in a format compatible with the profile you use
'900000123456'
// an FPTF `stop`/`station` object
{
type: 'station',
id: '900000123456',
name: 'foo station',
location: {
type: 'location',
latitude: 1.23,
longitude: 3.21
}
}
```
With `opt`, you can override the default options, which look like this:
```js
{
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
}
```
## 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')
client.stop('900000042101') // U Spichernstr.
.then(console.log)
.catch(console.error)
```
The response may look like this:
```js
{
type: 'stop',
id: '900000042101',
name: 'U Spichernstr.',
location: {
type: 'location',
latitude: 52.496581,
longitude: 13.330616
},
products: {
suburban: false,
subway: true,
tram: false,
bus: true,
ferry: false,
express: false,
regional: false
},
lines: [ {
type: 'line',
id: 'u1',
mode: 'train',
product: 'subway',
public: true,
name: 'U1',
symbol: 'U',
nr: 1,
metro: false,
express: false,
night: false
},
// …
{
type: 'line',
id: 'n9',
mode: 'bus',
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}
]
```
+202
View File
@@ -0,0 +1,202 @@
# `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
const createClient = require('hafas-client')
const vbbProfile = require('hafas-client/p/vbb')
const client = createClient(vbbProfile, 'my-awesome-program')
// 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:
```js
{
stopovers: true, // return stations on the way?
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
}
```
## 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)
client.trip('1|31431|28|86|17122017', 'S9', {when: 1513534689273})
.then(console.log)
.catch(console.error)
```
The response looked like this:
```js
{
id: '1|31431|28|86|17122017',
direction: 'S Spandau',
line: {
type: 'line',
id: '18299',
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'
}
},
currentLocation: {
type: 'location',
latitude: 52.447455,
longitude: 13.522464,
},
origin: {
type: 'station',
id: '900000260005',
name: 'S Flughafen Berlin-Schönefeld',
location: {
type: 'location',
latitude: 52.390796,
longitude: 13.51352
},
products: {
suburban: true,
subway: false,
tram: false,
bus: true,
ferry: false,
express: false,
regional: true
}
},
departure: '2017-12-17T18:37:00+01:00',
plannedDeparture: '2017-12-17T18:37:00+01:00',
departureDelay: null,
departurePlatform: '13',
plannedDeparturePlatform: '13',
destination: {
type: 'station',
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: '2017-12-17T19:50:30+01:00',
plannedArrival: '2017-12-17T19:49:00+01:00',
arrivalDelay: 90,
arrivalPlatform: '3a',
plannedArrivalPlatform: '2',
stopovers: [ /* … */ ]
}
```
### `polyline` option
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.
We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken from the [VBB profile](../p/vbb):
```js
{
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {
type: 'station',
id: '900000070301',
name: 'U Alt-Mariendorf',
/* … */
},
geometry: {
type: 'Point',
coordinates: [13.3875, 52.43993] // longitude, latitude
}
},
/* … */
{
type: 'Feature',
properties: {
type: 'station',
id: '900000017101',
name: 'U Mehringdamm',
/* … */
},
geometry: {
type: 'Point',
coordinates: [13.38892, 52.49448] // longitude, latitude
}
},
/* … */
{
// intermediate point, without associated station
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [13.28599, 52.58742] // longitude, latitude
}
},
{
type: 'Feature',
properties: {
type: 'station',
id: '900000089301',
name: 'U Alt-Tegel',
/* … */
},
geometry: {
type: 'Point',
coordinates: [13.28406, 52.58915] // longitude, latitude
}
}
]
}
```
@@ -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: { /* … */ },
// …
}
]
```
@@ -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.