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,13 @@
{
"auth": {
"type": "AID",
"aid": "4vV1AcH3N511icH"
},
"client": {
"type": "WEB",
"id": "AVV_AACHEN",
"name": "webapp"
},
"endpoint": "https://auskunft.avv.de/bin/mgate.exe",
"defaultLanguage": "de"
}
@@ -0,0 +1,53 @@
'use strict'
const createClient = require('../..')
const vosProfile = require('.')
const client = createClient(vosProfile, 'hafas-client-example')
const rwth = '1057'
const kronenberg = '1397'
// client.journeys(rwth, kronenberg, {results: 1, stopovers: true})
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// client.departures(rwth, {duration: 1})
// client.arrivals(rwth, {duration: 10, linesOfStops: true})
// client.radar({
// north: 50.78141,
// west: 6.06031,
// south: 50.75022,
// east: 6.10316,
// }, {results: 10})
client.locations('kronenberg', {results: 3})
// client.stop(rwth, {linesOfStops: true})
// client.nearby({
// type: 'location',
// latitude: 50.770607,
// longitude: 6.104637,
// }, {distance: 500})
// client.reachableFrom({
// type: 'location',
// id: '990000745',
// address: 'Aachen, Charlottenstraße 11',
// latitude: 50.770607,
// longitude: 6.104637,
// }, {
// maxDuration: 8,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,102 @@
'use strict'
const baseProfile = require('./base.json')
const products = [{
id: 'regional-train',
mode: 'train',
bitmasks: [1],
name: 'Regionalzug',
short: 'Regionalzug',
default: true,
}, {
id: 'long-distance-train',
mode: 'train',
bitmasks: [2],
name: 'Fernzug',
short: 'Fernzug',
default: true,
}, {
id: 'express-train',
mode: 'train',
bitmasks: [4],
name: 'ICE/Thalys',
short: 'ICE/Thalys',
default: true,
}, {
id: 'fernbus',
mode: 'bus',
bitmasks: [8],
name: 'Fernbus',
short: 'Fernbus',
default: true,
}, {
id: 'suburban-train',
mode: 'train',
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true,
}, {
id: 'subway',
mode: 'train',
bitmasks: [32],
name: 'U-Bahn',
short: 'U',
default: true,
}, {
id: 'tram',
mode: 'train',
bitmasks: [64],
name: 'Straßenbahn',
short: 'Straßenbahn',
default: true,
}, {
id: 'bus',
mode: 'bus',
bitmasks: [128],
name: 'Bus',
short: 'Bus',
default: true,
}, {
id: 'added-bus',
mode: 'bus',
bitmasks: [256],
name: 'Bus, Verstärkerfahrt',
short: 'Bus V',
default: true,
}, {
id: 'on-call',
mode: 'taxi',
bitmasks: [512],
name: 'Bedarfsverkehr',
short: 'Bedarfsverkehr',
default: true,
}, {
id: 'ferry',
mode: 'watercraft',
bitmasks: [1024],
name: 'Fähre',
short: 'Fähre',
default: true,
}]
const avvProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
ver: '1.26',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
reachableFrom: true,
remarks: true,
remarksGetPolyline: false,
}
module.exports = avvProfile
@@ -0,0 +1,13 @@
# AVV profile for `hafas-client`
[*Aachener Verkehrsverbund (AVV)*](https://de.wikipedia.org/wiki/Aachener_Verkehrsverbund) is the local transport provider of [Aachen](https://en.wikipedia.org/wiki/Aachen). This profile adds *AVV* support to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const avvProfile = require('hafas-client/p/AVV')
// create a client with AVV profile
const client = createClient(avvProfile, 'my-awesome-program')
```
@@ -0,0 +1,13 @@
{
"auth": {
"type": "AID",
"aid": "kEwHkFUCIL500dym"
},
"client": {
"type": "WEB",
"id": "BART",
"name": "webapp"
},
"endpoint": "https://planner.bart.gov/bin/mgate.exe",
"defaultLanguage": "en"
}
@@ -0,0 +1,53 @@
'use strict'
const createClient = require('../..')
const vosProfile = require('.')
const client = createClient(vosProfile, 'hafas-client-example')
const fremont = '100013296'
const embarcadero = '100013295'
// client.journeys(fremont, embarcadero, {results: 1, stopovers: true})
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// client.departures(fremont, {duration: 1})
// client.arrivals(fremont, {duration: 10, linesOfStops: true})
// client.radar({
// north: 37.8735,
// west: -122.5250,
// south: 37.6884,
// east: -122.2105,
// }, {results: 10})
client.locations('embarcadero', {results: 3})
// client.stop(fremont, {linesOfStops: true})
// client.nearby({
// type: 'location',
// latitude: 38.554779,
// longitude: -121.738798,
// }, {distance: 500})
// client.reachableFrom({
// type: 'location',
// id: '980557173',
// address: '1000 Alice St, Davis, 95616',
// latitude: 38.554779,
// longitude: -121.738798,
// }, {
// maxDuration: 8,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,66 @@
'use strict'
const baseProfile = require('./base.json')
const products = [{
id: 'bart',
mode: 'train',
bitmasks: [128],
name: 'BART',
short: 'BART',
default: true,
}, {
id: 'regional-train',
mode: 'train',
bitmasks: [8],
name: 'regional trains (Caltrain, Capitol Corridor, ACE)',
short: 'regional trains',
default: true,
}, {
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true,
}, {
id: 'ferry',
mode: 'watercraft',
bitmasks: [64],
name: 'Ferry',
short: 'Ferry',
default: true,
}, {
id: 'tram',
mode: 'train',
bitmasks: [256],
name: 'Tram',
short: 'Tram',
default: true,
}, {
id: 'cable-car',
mode: 'train',
bitmasks: [4],
name: 'cable car',
short: 'cable car',
default: true,
}]
const bartProfile = {
...baseProfile,
locale: 'en-US',
timezone: 'America/Los_Angeles',
ver: '1.40',
products,
trip: true,
radar: true,
reachableFrom: true,
departuresGetPasslist: false, // `departures()` method: support for `getPasslist` field?
departuresStbFltrEquiv: false, // `departures()` method: support for `stbFltrEquiv` field?
refreshJourneyUseOutReconL: true,
}
module.exports = bartProfile
@@ -0,0 +1,13 @@
# BART profile for `hafas-client`
[*Bay Area Rapid Transit (BART)*](https://en.wikipedia.org/wiki/Bay_Area_Rapid_Transit) is the rapid transit public transportation system serving the [San Francisco Bay Area](https://en.wikipedia.org/wiki/San_Francisco_Bay_Area). This profile adds *BART* support to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const bartProfile = require('hafas-client/p/BART')
// create a client with BART profile
const client = createClient(bartProfile, 'my-awesome-program')
```
@@ -0,0 +1,13 @@
{
"auth": {
"type": "AID",
"aid": "3jkAncud78HSoqclmN54812A"
},
"client": {
"type": "WEB",
"id": "HAFAS",
"name": "webapp"
},
"endpoint": "https://bls.hafas.de/bin/mgate.exe",
"defaultLanguage": "de"
}
@@ -0,0 +1,53 @@
'use strict'
const createClient = require('../..')
const blsProfile = require('.')
const client = createClient(blsProfile, 'hafas-client-example')
const bernDennigkofengässli = '8590093'
const münsingenSpital = '8578932'
client.journeys(bernDennigkofengässli, münsingenSpital, {results: 1, stopovers: true})
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// client.departures(bernDennigkofengässli, {duration: 1})
// client.arrivals(bernDennigkofengässli, {duration: 10, linesOfStops: true})
// client.radar({
// north: 46.969,
// west: 7.3941,
// south: 46.921,
// east: 7.5141,
// }, {results: 10})
// client.locations('münsingen spital', {results: 3})
// client.stop(bernDennigkofengässli, {linesOfStops: true})
// client.nearby({
// type: 'location',
// latitude: 53.554422,
// longitude: 9.977934
// }, {distance: 500})
// client.reachableFrom({
// type: 'location',
// id: '990017698',
// address: 'Bern, Schänzlihalde 17',
// latitude: 46.952835,
// longitude: 7.447527,
// }, {
// maxDuration: 10,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,94 @@
'use strict'
const baseProfile = require('./base.json')
const products = [{
id: 'ice',
mode: 'train',
bitmasks: [1],
name: 'ICE',
short: 'ICE',
default: true,
}, {
id: 'ic-ec',
mode: 'train',
bitmasks: [2],
name: 'IC/EC',
short: 'IC/EC',
default: true,
}, {
id: 'ir',
mode: 'train',
bitmasks: [4],
name: 'IR',
short: 'IR',
default: true,
}, {
id: 'local-train',
mode: 'train',
bitmasks: [8],
name: 'Nahverkehr',
short: 'Nahverkehr',
default: true,
}, {
id: 'watercraft',
mode: 'watercraft',
bitmasks: [16],
name: 'Schiff',
short: 'Schiff',
default: true,
}, {
id: 's-bahn',
mode: 'train',
bitmasks: [32],
name: 'S-Bahn',
short: 'S',
default: true,
}, {
id: 'bus',
mode: 'bus',
bitmasks: [64],
name: 'Bus',
short: 'Bus',
default: true,
}, {
id: 'funicular',
mode: 'gondola',
bitmasks: [128],
name: 'Seilbahn',
short: 'Seilbahn',
default: true,
}, {
id: 'tram',
mode: 'train',
bitmasks: [512],
name: 'Tram',
short: 'Tram',
default: true,
}, {
id: 'car-shuttle-train',
mode: 'train',
bitmasks: [4096],
name: 'Autoverlad',
short: 'Autoverlad',
default: true,
}]
const blsProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
ver: '1.46',
products,
trip: true,
radar: true,
refreshJourneyUseOutReconL: true,
reachableFrom: true,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
}
module.exports = blsProfile
@@ -0,0 +1,15 @@
# BLS profile for `hafas-client`
[*BLS AG*](https://en.wikipedia.org/wiki/BLS_AG) is the local transport provider of the [Canton of Bern](https://en.wikipedia.org/wiki/Canton_of_Bern). This profile adds *BLS* support to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const blsProfile = require('hafas-client/p/BLS')
// create a client with BLS profile
const client = createClient(blsProfile, 'my-awesome-program')
```
Check out the [code examples](example.js).
@@ -0,0 +1,16 @@
{
"auth": {
"type": "AID",
"aid": "Mz0YdF9Fgx0Mb9"
},
"client": {
"type": "IPA",
"id": "BVG",
"v": "6020000",
"name": "FahrInfo"
},
"endpoint": "https://bvg.hafas.cloud/bin/mgate.exe",
"ext": "BVG.1",
"ver": "1.44",
"defaultLanguage": "de"
}
@@ -0,0 +1,48 @@
'use strict'
const createClient = require('../..')
const vbbProfile = require('.')
const client = createClient(vbbProfile, 'hafas-client-example')
// Hauptbahnhof to Charlottenburg
client.journeys('900000003201', '900000024101', {results: 1, polylines: true})
// client.departures('900000013102', {duration: 1})
// client.arrivals('900000013102', {duration: 10, linesOfStops: true})
// client.locations('Alexanderplatz', {results: 2})
// client.stop('900000042101', {linesOfStops: true}) // Spichernstr
// client.nearby({
// type: 'location',
// latitude: 52.5137344,
// longitude: 13.4744798
// }, {distance: 60})
// client.radar({
// north: 52.52411,
// west: 13.41002,
// south: 52.51942,
// east: 13.41709
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// address: '13353 Berlin-Wedding, Torfstr. 17',
// latitude: 52.541797,
// longitude: 13.350042
// }, {
// when: new Date('2018-08-27T10:00:00+0200'),
// maxDuration: 10
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,207 @@
'use strict'
const shorten = require('vbb-short-station-name')
const {to12Digit, to9Digit} = require('vbb-translate-ids')
const parseLineName = require('vbb-parse-line')
const getStations = require('vbb-stations')
const {parseHook} = require('../../lib/profile-hooks')
const {
parseLine: _parseLine,
parseLocation: _parseLocation,
parseArrival: _parseArrival,
parseDeparture: _parseDeparture,
parseStopover: _parseStopover,
parseJourneyLeg: _parseJourneyLeg,
formatStation: _formatStation,
} = require('../../lib/default-profile')
const baseProfile = require('./base.json')
const products = require('./products')
// todo: there's also a referenced icon `{"res":"occup_fig_{low,mid}"}`
const addOccupancy = (item, occupancyCodes) => {
const remIdx = (item.remarks || [])
.findIndex(r => r.code && occupancyCodes.has(r.code))
if (remIdx < 0) return;
const rem = item.remarks[remIdx]
item.occupancy = occupancyCodes.get(rem.code)
item.remarks = [
...item.remarks.slice(0, remIdx),
...item.remarks.slice(remIdx + 1),
]
}
const stopoverOccupancyCodes = new Map([
['text.occup.loc.max.11', 'low'],
['text.occup.loc.max.12', 'medium'],
['text.occup.loc.max.13', 'high'],
])
const journeyLegOccupancyCodes = new Map([
['text.occup.jny.max.11', 'low'],
['text.occup.jny.max.12', 'medium'],
['text.occup.jny.max.13', 'high'],
])
// todo: https://m.tagesspiegel.de/berlin/fahrerlebnis-wie-im-regionalexpress-so-faehrt-es-sich-in-der-neuen-express-s-bahn/25338674.html
const parseLineWithMoreDetails = ({parsed}, p) => {
parsed.name = p.name.replace(/^(bus|tram)\s+/i, '')
const details = parseLineName(parsed.name)
parsed.symbol = details.symbol
parsed.nr = details.nr
parsed.metro = details.metro
parsed.express = details.express
parsed.night = details.night
return parsed
}
const parseLocation = ({parsed}, l) => {
if (parsed.type === 'stop' || parsed.type === 'station') {
parsed.name = shorten(parsed.name)
parsed.id = to12Digit(parsed.id)
if (!parsed.location.latitude || !parsed.location.longitude) {
const [s] = getStations(parsed.id)
if (s) Object.assign(parsed.location, s.location)
}
}
return parsed
}
// todo: S45, S46?
const ringbahnClockwise = /^ringbahn s\s?41$/i
const ringbahnAnticlockwise = /^ringbahn s\s?42$/i
const parseDepartureRenameRingbahn = ({parsed}, dep) => {
if (parsed.line && parsed.line.product === 'suburban') {
const d = parsed.direction && parsed.direction.trim()
if (ringbahnClockwise.test(d)) {
parsed.direction = 'Ringbahn S41 ⟳'
} else if (ringbahnAnticlockwise.test(d)) {
parsed.direction = 'Ringbahn S42 ⟲'
}
}
return parsed
}
const parseArrivalRenameRingbahn = ({parsed}, arr) => {
if (parsed.line && parsed.line.product === 'suburban') {
const p = parsed.provenance && parsed.provenance.trim()
if (ringbahnClockwise.test(p)) {
parsed.provenance = 'Ringbahn S41 ⟳'
} else if (ringbahnAnticlockwise.test(p)) {
parsed.provenance = 'Ringbahn S42 ⟲'
}
}
return parsed
}
const parseArrDepWithOccupancy = ({parsed}, d) => {
addOccupancy(parsed, stopoverOccupancyCodes)
return parsed
}
const parseStopoverWithOccupancy = ({parsed}, st, date) => {
addOccupancy(parsed, stopoverOccupancyCodes)
return parsed
}
const parseJourneyLegWithBerlkönig = (ctx, leg, date) => {
if (leg.type === 'KISS') {
const icon = ctx.common.icons[leg.icoX]
if (icon && icon.type === 'prod_berl') {
const res = _parseJourneyLeg(ctx, {
...leg, type: 'WALK'
}, date)
delete res.walking
const mcp = leg.dep.mcp || {}
const mcpData = mcp.mcpData || {}
// todo: mcp.lid
// todo: mcpData.occupancy, mcpData.type
// todo: journey.trfRes.bkgData
res.line = {
type: 'line',
id: null, // todo
// todo: fahrtNr?
name: mcpData.providerName,
public: true,
mode: 'taxi',
product: 'berlkoenig'
// todo: operator
}
return res
}
}
return _parseJourneyLeg(ctx, leg, date)
}
const parseJourneyLegWithOccupancy = ({parsed}, leg, date) => {
if (leg.type === 'JNY') {
addOccupancy(parsed, journeyLegOccupancyCodes)
}
return parsed
}
const validIBNR = /^\d+$/
const formatStation = (id) => {
if ('string' !== typeof id) throw new Error('station ID must be a string.')
const l = id.length
if ((l !== 7 && l !== 9 && l !== 12) || !validIBNR.test(id)) {
throw new Error('station ID must be a valid IBNR.')
}
// BVG has some 7-digit stations. We don't convert them to 12 digits,
// because it only recognizes in the 7-digit format. see derhuerst/vbb-hafas#22
if (l !== 7) id = to9Digit(id)
return _formatStation(id)
}
// use the Berlkönig ride sharing service?
const requestJourneysWithBerlkoenig = ({opt}, query) => {
if (('numF' in query) && opt.berlkoenig) {
// todo: check if this is still true
throw new Error('The `berlkoenig` and `results` options are mutually exclusive.')
}
query.jnyFltrL.push({type: 'GROUP', mode: 'INC', value: 'OEV'})
if (opt.berlkoenig) query.jnyFltrL.push({type: 'GROUP', mode: 'INC', value: 'BERLKOENIG'})
query.gisFltrL = [{meta: 'foot_speed_normal', type: 'M', mode: 'FB'}]
return query
}
// todo: adapt/extend `vbb-parse-ticket` to support the BVG markup
const bvgProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
transformJourneysQuery: requestJourneysWithBerlkoenig,
products,
parseLine: parseHook(_parseLine, parseLineWithMoreDetails),
parseLocation: parseHook(_parseLocation, parseLocation),
parseStationName: (ctx, name) => shorten(name),
parseArrival: parseHook(
parseHook(_parseArrival, parseArrivalRenameRingbahn),
parseArrDepWithOccupancy,
),
parseDeparture: parseHook(
parseHook(_parseDeparture, parseDepartureRenameRingbahn),
parseArrDepWithOccupancy,
),
parseStopover: parseHook(_parseStopover, parseStopoverWithOccupancy),
parseJourneyLeg: parseHook(
parseJourneyLegWithBerlkönig,
parseJourneyLegWithOccupancy,
),
formatStation,
departuresGetPasslist: false, // `departures()` method: support for `getPasslist` field?
departuresStbFltrEquiv: false, // `departures()` method: support for `stbFltrEquiv` field?
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
refreshJourney: true,
reachableFrom: true
}
module.exports = bvgProfile
@@ -0,0 +1,60 @@
'use strict'
module.exports = [
{
id: 'suburban',
mode: 'train',
bitmasks: [1],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'subway',
mode: 'train',
bitmasks: [2],
name: 'U-Bahn',
short: 'U',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [4],
name: 'Tram',
short: 'T',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [8],
name: 'Bus',
short: 'B',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [16],
name: 'Fähre',
short: 'F',
default: true
},
{
id: 'express',
mode: 'train',
bitmasks: [32],
name: 'IC/ICE',
short: 'E',
default: true
},
{
id: 'regional',
mode: 'train',
bitmasks: [64],
name: 'RB/RE',
short: 'R',
default: true
}
]
@@ -0,0 +1,43 @@
# BVG profile for `hafas-client`
[*Verkehrsverbund Berlin-Brandenburg (BVG)*](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) is the major local transport provider in [Berlin](https://en.wikipedia.org/wiki/Berlin). This profile adds *BVG*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const bvgProfile = require('hafas-client/p/bvg')
// create a client with BVG profile
const client = createClient(bvgProfile, 'my-awesome-program')
```
## Customisations
- parses *BVG*-specific products (such as *X-Bus*)
- supports [BerlKönig `journey` legs](#berlkoenig)
- strips parts from station names that are unnecessary in the Berlin context
- parses line names to give more information (e.g. "Is it an express bus?")
- renames *Ringbahn* line names to contain `⟳` and `⟲`
### BerlKönig
BVG has recently announced [a ride-sharing service called *BerlKönig*](https://www.berlkoenig.de). Pass `berlkoenig: true` into `journeys()` to get special legs:
```js
{
mode: 'walking',
departure: // …
arrival: // …
origin: // …
destination: // …
line: {
type: 'line',
name: 'BerlKönig',
public: true,
mode: 'taxi',
product: 'berlkoenig'
}
}
```
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "ALT2vl7LAFDFu2dz"
},
"client": {
"type": "IPH",
"id": "HAFAS",
"v": "4000000",
"name": "cflPROD-STORE"
},
"endpoint": "https://horaires.cfl.lu/bin/mgate.exe",
"ver": "1.43",
"defaultLanguage": "fr"
}
@@ -0,0 +1,52 @@
'use strict'
const createClient = require('../..')
const cflProfile = require('.')
const client = createClient(cflProfile, 'hafas-client-example')
const mersch = '9864348'
const bruxellesCentral = '8800003'
// from Mersch to Bruxelles Central
client.journeys(mersch, bruxellesCentral, {results: 1})
// .then(({journeys}) => {
// const leg = journeys[0].legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.locations('mersch', {results: 3})
// client.stop(mersch)
// client.nearby({
// type: 'location',
// latitude: 49.7523,
// longitude: 6.1103
// }, {distance: 500})
// client.departures(mersch, {duration: 5})
// client.arrivals(mersch, {duration: 10, linesOfStops: true})
// client.radar({
// north: 49.9,
// west: 6.11,
// south: 49.7,
// east: 6.13
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// id: '980005067',
// address: '7557 Mersch, Rue Mies 1',
// latitude: 49.746044,
// longitude: 6.102228,
// }, {
// maxDuration: 30,
// })
.then(data => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,28 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const transformReqBody = (ctx, body) => {
body.lang = 'de'
return body
}
const cflProfile = {
...baseProfile,
locale: 'de-LU',
timezone: 'Europe/Luxembourg',
transformReqBody,
products: products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
reachableFrom: true,
remarksGetPolyline: false,
}
module.exports = cflProfile;
@@ -0,0 +1,45 @@
'use strict'
module.exports = [
// todo: other bits
{
id: 'express-train',
mode: 'train',
bitmasks: [1, 2],
name: 'TGV, ICE, EuroCity',
short: 'TGV/ICE/EC',
default: true
},
{
id: 'local-train',
mode: 'train',
bitmasks: [8, 16],
name: 'local trains',
short: 'local',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [256],
name: 'tram',
short: 'tram',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'bus',
short: 'bus',
default: true
},
{
id: 'gondola',
mode: 'gondola',
bitmasks: [512],
name: 'Fun', // taken from the horaires.cfl.lu website
short: 'Fun', // abbreviation for funicular?
default: true
}
]
@@ -0,0 +1,18 @@
# CFL profile for `hafas-client`
The [*Société Nationale des Chemins de Fer Luxembourgeois (CFL)*](https://en.wikipedia.org/wiki/Société_Nationale_des_Chemins_de_Fer_Luxembourgeois) is the national railway company of [Luxembourg](https://en.wikipedia.org/wiki/Luxembourg). This profile adds *CFL*-specific customisations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const cflProfile = require('hafas-client/p/cfl')
// create a client with CFL profile
const client = createClient(cflProfile)
```
## Customisations
- *CFL*-specific products (such as [*Standseilbahn_Pfaffenthal-Kirchberg*](https://de.wikipedia.org/wiki/Standseilbahn_Pfaffenthal-Kirchberg))
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "ioslaskdcndrjcmlsd"
},
"client": {
"type": "IPH",
"id": "CMTA",
"v": "2",
"name": "CapMetro"
},
"endpoint": "https://capmetro.hafas.cloud/bin/mgate.exe",
"ver": "1.40",
"defaultLanguage": "en"
}
@@ -0,0 +1,48 @@
'use strict'
const createClient = require('../..')
const cmtaProfile = require('.')
const client = createClient(cmtaProfile, 'hafas-client-example')
// Broadie Oaks to Domain
client.journeys('000002370', '000005919', {results: 1, polylines: true})
// client.departures('000002370', {duration: 1})
// client.arrivals('000002370', {duration: 10, linesOfStops: true})
// client.locations('Westgate', {results: 2})
// client.stop('000005534') // Downtown light rail station
// client.nearby({
// type: 'location',
// latitude: 30.266222,
// longitude: -97.746058
// }, {distance: 60})
// client.radar({
// north: 30.240877,
// west: -97.804588,
// south: 30.225378,
// east: -97.786692
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// address: '604 W 9TH ST, Austin, TX 78701',
// latitude: 30.272910,
// longitude: -97.747883
// }, {
// when: new Date('2018-08-27T10:00:00+0200'),
// maxDuration: 15
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,23 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const cmtaProfile = {
...baseProfile,
locale: 'en-US',
timezone: 'America/Chicago',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
refreshJourney: true,
reachableFrom: true,
remarks: true, // `.svcResL[0].res.msgL[]` is missing though 🤔
}
module.exports = cmtaProfile
@@ -0,0 +1,28 @@
'use strict'
module.exports = [
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'MetroBus',
short: 'B',
default: true
},
{
id: 'rapid',
mode: 'bus',
bitmasks: [4096],
name: 'MetroRapid',
short: 'R',
default: true
},
{
id: 'rail',
mode: 'train',
bitmasks: [8],
name: 'MetroRail',
short: 'M',
default: true
}
]
@@ -0,0 +1,18 @@
# CMTA profile for `hafas-client`
[*Capital Metropolitan Transportation Authority (CMTA)* or *CapMetro*](https://en.wikipedia.org/wiki/Capital_Metropolitan_Transportation_Authority) is a public transportation provider serving [Austin, Texas](https://en.wikipedia.org/wiki/Austin,_Texas) metropolitan area. This profile adds *CMTA*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const cmtaProfile = require('hafas-client/p/cmta')
// create a client with CMTA profile
const client = createClient(cmtaProfile, 'my-awesome-program')
```
## Customisations
- parses *CMTA*-specific products (such as *MetroRapid* and *MetroRail*)
@@ -0,0 +1,14 @@
{
"auth": {
"type": "AID",
"aid": "XNFGL2aSkxfDeK8N4waOZnsdJ"
},
"client": {
"type": "WEB",
"id": "DART",
"name": "webapp"
},
"endpoint": "https://dart.hafas.de/bin/mgate.exe",
"ver": "1.35",
"defaultLanguage": "en"
}
@@ -0,0 +1,54 @@
'use strict'
const createClient = require('../..')
const dartProfile = require('.')
const client = createClient(dartProfile, 'hafas-client example')
const mlkJrPkwyAdamsAveDsm2055 = '100002702'
const se5thStEHackleyAveDsm2294 = '100004972'
// client.journeys(mlkJrPkwyAdamsAveDsm2055, se5thStEHackleyAveDsm2294, {
// results: 1, stopovers: true,
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs.find(l => !!l.line)
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// client.departures(mlkJrPkwyAdamsAveDsm2055, {duration: 10})
// client.arrivals(mlkJrPkwyAdamsAveDsm2055, {linesOfStops: true})
client.locations('adams ave', {results: 3})
// client.stop(mlkJrPkwyAdamsAveDsm2055, {linesOfStops: true})
// client.nearby({
// type: 'location',
// id: '980010311',
// address: 'Austraße 37, 6700 Bludenz',
// latitude: 41.6056,
// longitude: -93.5916,
// }, {distance: 1000})
// client.reachableFrom({
// type: 'location',
// latitude: 41.613584,
// longitude: -93.881803,
// address: 'Laurel St, Waukee, 50263',
// }, {
// maxDuration: 20,
// })
// client.radar({
// north: 41.6266,
// west: -93.7299,
// south: 41.5503,
// east: -93.5699,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,29 @@
'use strict'
const baseProfile = require('./base.json')
const products = [{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true,
}]
const dartProfile = {
...baseProfile,
locale: 'en-US',
timezone: 'America/Chicago',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
radar: true,
}
module.exports = dartProfile
@@ -0,0 +1,17 @@
# DART profile for `hafas-client`
[*Des Moines Area Rapid Transit (DART)*](https://en.wikipedia.org/wiki/Des_Moines_Area_Regional_Transit) is the local transport provider of [Des Moines](https://en.wikipedia.org/wiki/Des_Moines_metropolitan_area), Iowa, USA. This profile adds *DART* support to `hafas-client`.
*Note:* This profile *does not* support [*Dallas Area Rapid Transit (DART)*](https://de.wikipedia.org/wiki/Verkehrsverbund_Vorarlberg) in [Dallas–Fort Worth](https://en.wikipedia.org/wiki/Dallas–Fort_Worth_metroplex), Texas, USA.
## Usage
```js
const createClient = require('hafas-client')
const dartProfile = require('hafas-client/p/dart')
// create a client with DART profile
const client = createClient(dartProfile, 'my-awesome-program')
```
Check out the [code examples](example.js).
@@ -0,0 +1,16 @@
{
"auth": {
"type": "AID",
"aid": "OGBAqytjHhCvr0J4"
},
"client": {
"type": "AND",
"id": "DB-REGIO",
"v": 100021,
"name": "DB Busradar NRW"
},
"endpoint": "https://db-regio.hafas.de/bin/hci/mgate.exe",
"ext": "DB.REGIO.1",
"ver": "1.24",
"defaultLanguage": "de"
}
@@ -0,0 +1,41 @@
'use strict'
const createClient = require('../../')
const dbbusradarnrwProfile = require('.')
const client = createClient(dbbusradarnrwProfile, 'hafas-client-example')
// Hagen Bauhaus to Schwerte Bahnhof
// returns hafas error PARSE
client.journeys('3307002', '3357026', {results: 1})
// .then(({journeys}) => {
// const leg = journeys[0].legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.departures('3307002', {duration: 60})
// client.arrivals('3307002', {duration: 30, linesOfStops: true})
// client.locations('Hagen Vorhalle')
// client.stop('3307002') // Hagen Bauhaus
// client.nearby({
// type: 'location',
// latitude: 51.38,
// longitude: 7.45
// }, {results: 1})
// client.radar({
// north: 51.5,
// west: 7.2,
// south: 51.2,
// east: 7.8
// }, {results: 10})
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
}, console.error)
@@ -0,0 +1,96 @@
'use strict'
const baseProfile = require('./base.json')
// DB Busradar NRW app does not allow selecting specific modes of transport to filter results,
// so the bitmasks had to be determined by querying some stations and looking at the results..
const products = [
{
id: 'national-express',
mode: 'train',
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
},
{
id: 'national',
mode: 'train',
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
},
// todo: not always true when a station has RE stopping at it
// maybe something else?
{
id: 'regional-express',
mode: 'train',
bitmasks: [4],
name: 'Regionalexpress',
short: 'RE',
default: true
},
// todo: also used for replacement service incl. S-Bahn replacement
{
id: 'regional',
mode: 'train',
bitmasks: [8],
name: 'Regionalzug',
short: 'RB/RE',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [64],
name: 'Ferry',
short: 'F',
default: true
},
// todo: are `128` & `256` unused?
{
id: 'taxi',
mode: 'taxi',
bitmasks: [512],
name: 'AnrufSammelTaxi',
short: 'AST',
default: true
}
]
const dbBusradarNrwProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
products: products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
journeysOutFrwd: false,
trip: true,
radar: true,
remarks: true, // `.svcResL[0].res.msgL[]` is missing though 🤔
lines: false, // `.svcResL[0].res.lineL[]` is missing 🤔
}
module.exports = dbBusradarNrwProfile
@@ -0,0 +1,19 @@
# DB Busradar NRW profile for `hafas-client`
[*DB Busradar NRW*](https://www.bahn.de/westfalenbus/view/fahrplan/busradar.shtml) is a mobile application used in [North Rhine-Westphalia](https://en.wikipedia.org/wiki/North_Rhine-Westphalia).
It shows realtime locations and arrival/departure information for vehicles operated by bus companies which are part of [DB Regio Bus](https://www.dbregio.de/db_regio/view/wir/bus.shtml) in NRW, namely:
- [BVR Busverkehr Rheinland GmbH](https://www.rheinlandbus.de/) (DB Rheinlandbus)
- [WB Westfalen Bus GmbH](https://www.westfalenbus.de/) (DB Westfalenbus)
- [BVO Busverkehr Ostwestfalen GmbH](https://www.ostwestfalen-lippe-bus.de) (DB Ostwestfalen-Lippe-Bus)
This profile adapts `hafas-client` to the HAFAS endpoint used by the application.
## Usage
```js
const createClient = require('hafas-client')
const dbbusradarnrwProfile = require('hafas-client/p/db-busradar-nrw')
// create a client with DB Busradar NRW profile
const client = createClient(dbbusradarnrwProfile, 'my-awesome-program')
```
@@ -0,0 +1,17 @@
{
"auth": {
"type": "AID",
"aid": "n91dB8Z77MLdoR0K"
},
"salt": "6264493855566A34304B356676787766",
"client": {
"type": "AND",
"id": "DB",
"v": 19040000,
"name": "DB Navigator"
},
"endpoint": "https://reiseauskunft.bahn.de/bin/mgate.exe",
"ext": "DB.R20.12.b",
"ver": "1.34",
"defaultLanguage": "en"
}
@@ -0,0 +1,59 @@
'use strict'
const createClient = require('../../')
const dbProfile = require('.')
const client = createClient(dbProfile, 'hafas-client-example')
// Berlin Jungfernheide to München Hbf
client.journeys('8011167', '8000261', {results: 1, tickets: true})
// .then(({journeys}) => {
// const leg = journeys[0].legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.departures('8011167', {duration: 1})
// client.arrivals('8011167', {duration: 10, linesOfStops: true})
// client.locations('Berlin Jungfernheide')
// client.locations('Atze Musiktheater', {poi: true, addressses: false, fuzzy: false})
// client.stop('8000309') // Regensburg Hbf
// client.nearby({
// type: 'location',
// latitude: 52.4751309,
// longitude: 13.3656537
// }, {results: 1})
// client.reachableFrom({
// type: 'location',
// address: '13353 Berlin-Wedding, Torfstr. 17',
// latitude: 52.541797,
// longitude: 13.350042
// }, {
// when: new Date('2018-08-27T10:00:00+0200'),
// maxDuration: 50
// })
// client.radar({
// north: 52.52411,
// west: 13.41002,
// south: 52.51942,
// east: 13.41709
// }, {results: 10})
// client.journeys('8011113', '8000261', {
// departure: Date.now() - 2 * 60 * 60 * 1000,
// results: 1, stopovers: true, transfers: 1
// })
// .then(({journeys}) => {
// const leg = journeys[0].legs.find(l => l.line && l.line.product === 'nationalExpress')
// const prevStopover = leg.stopovers.find((st) => {
// return st.departure && Date.parse(st.departure) < Date.now()
// })
// return client.journeysFromTrip(leg.tripId, prevStopover, '8000207')
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
}, console.error)
@@ -0,0 +1,510 @@
'use strict'
const trim = require('lodash/trim')
const uniqBy = require('lodash/uniqBy')
const slugg = require('slugg')
const without = require('lodash/without')
const {parseHook} = require('../../lib/profile-hooks')
const _parseJourney = require('../../parse/journey')
const _parseJourneyLeg = require('../../parse/journey-leg')
const _parseLine = require('../../parse/line')
const _parseArrival = require('../../parse/arrival')
const _parseDeparture = require('../../parse/departure')
const _parseHint = require('../../parse/hint')
const _parseLocation = require('../../parse/location')
const _formatStation = require('../../format/station')
const {bike} = require('../../format/filters')
const products = require('./products')
const baseProfile = require('./base.json')
const formatLoyaltyCard = require('./loyalty-cards').format
const transformReqBody = (ctx, body) => {
const req = body.svcReqL[0] || {}
// see https://pastebin.com/qZ9WS3Cx
req.cfg = {...req.cfg, rtMode: 'HYBRID'} // todo: use `REALTIME`?
return body
}
const slices = (n, arr) => {
const initialState = {slices: [], count: Infinity}
return arr.reduce(({slices, count}, item) => {
if (count >= n) {
slices.push([item])
count = 1
} else {
slices[slices.length - 1].push(item)
count++
}
return {slices, count}
}, initialState).slices
}
const parseGrid = (g) => {
// todo: g.type, e.g. `S`
// todo: respect `g.itemL[].(col|row)`?
// todo
// parseGrid is being called by parseLocWithDetails, which is being called as
// profile.parseLocation by profile.parseCommon, parseCommon hasn't finished
// resolving all references yet, so we have to resolve them manually here.
// This would be fixed if we resolve references on-the-fly or in a recursive/
// iterative process.
return {
title: g.title,
rows: slices(g.nCols, g.itemL.map(item => (
Array.isArray(item.hints) && item.hints[0] ||
Array.isArray(item.remarkRefs) && item.remarkRefs[0] && item.remarkRefs[0].hint ||
{}
))),
}
}
const ausstattungKeys = Object.assign(Object.create(null), {
'3-s-zentrale': '3SZentrale',
'parkplatze': 'parkingLots',
'fahrrad-stellplatze': 'bicycleParkingRacks',
'opnv-anbindung': 'localPublicTransport',
'wc': 'toilets',
'schliessfacher': 'lockers',
'reisebedarf': 'travelShop',
'stufenfreier-zugang': 'stepFreeAccess',
'ein-umsteigehilfe': 'boardingAid',
'taxi-am-bahnhof': 'taxis'
})
const parseAusstattungVal = (val) => {
val = val.toLowerCase()
return val === 'ja' ? true : (val === 'nein' ? false : val)
}
const parseAusstattungGrid = (g) => {
// filter duplicate hint rows
const rows = uniqBy(g.rows, ([key, val]) => key + ':' + val)
const res = {}
Object.defineProperty(res, 'raw', {value: rows})
for (let [key, val] of rows) {
key = ausstattungKeys[slugg(key)]
if (key) res[key] = parseAusstattungVal(val)
}
return res
}
const parseReisezentrumÖffnungszeiten = (g) => {
const res = {}
for (const [dayOfWeek, val] of g.rows) res[dayOfWeek] = val
res.raw = g.rows
return res
}
const parseLocWithDetails = ({parsed, common}, l) => {
if (!parsed) return parsed
if (parsed.type !== 'stop' && parsed.type !== 'station') return parsed
if (Array.isArray(l.gridL)) {
const resolveCells = grid => ({
...grid,
rows: grid.rows.map(row => row.map(cell => cell && cell.text)),
})
let grids = l.gridL
.map(grid => parseGrid(grid, common))
.map(resolveCells)
const ausstattung = grids.find(g => slugg(g.title) === 'ausstattung')
if (ausstattung) {
parsed.facilities = parseAusstattungGrid(ausstattung)
}
const öffnungszeiten = grids.find(g => slugg(g.title) === 'offnungszeiten-reisezentrum')
if (öffnungszeiten) {
parsed.reisezentrumOpeningHours = parseReisezentrumÖffnungszeiten(öffnungszeiten)
}
grids = without(grids, ausstattung, öffnungszeiten)
if (grids.length > 0) parsed.grids = grids
}
return parsed
}
// https://www.bahn.de/p/view/service/buchung/auslastungsinformation.shtml
const loadFactors = []
loadFactors[1] = 'low-to-medium'
loadFactors[2] = 'high'
loadFactors[3] = 'very-high'
loadFactors[4] = 'exceptionally-high'
const parseLoadFactor = (opt, tcocL, tcocX) => {
const cls = opt.firstClass ? 'FIRST' : 'SECOND'
const load = tcocX.map(i => tcocL[i]).find(lf => lf.c === cls)
return load && loadFactors[load.r] || null
}
const parseArrOrDepWithLoadFactor = ({parsed, res, opt}, d) => {
if (d.stbStop.dTrnCmpSX && Array.isArray(d.stbStop.dTrnCmpSX.tcocX)) {
const load = parseLoadFactor(opt, res.common.tcocL || [], d.stbStop.dTrnCmpSX.tcocX)
if (load) parsed.loadFactor = load
}
return parsed
}
const transformJourneysQuery = ({opt}, query) => {
const filters = query.jnyFltrL
if (opt.bike) filters.push(bike)
query.trfReq = {
jnyCl: opt.firstClass === true ? 1 : 2,
tvlrProf: [{
type: 'E',
redtnCard: opt.loyaltyCard
? formatLoyaltyCard(opt.loyaltyCard)
: null
}],
cType: 'PK'
}
return query
}
// todo: fix this
// line: {
// type: 'line',
// id: '5-vbbbvb-x9',
// fahrtNr: '52496',
// name: 'X9',
// public: true,
// mode: 'bus',
// product: 'bus',
// operator: {type: 'operator', id: 'nahreisezug', name: 'Nahreisezug'}
// }
const parseLineWithAdditionalName = ({parsed}, l) => {
if (l.nameS && ['bus', 'tram', 'ferry'].includes(l.product)) {
parsed.name = l.nameS
}
if (l.addName) {
parsed.additionalName = parsed.name
parsed.name = l.addName
}
return parsed
}
// todo: sotRating, conSubscr, isSotCon, showARSLink, sotCtxt
// todo: conSubscr, showARSLink, useableTime
const parseJourneyWithPrice = ({parsed}, raw) => {
parsed.price = null
// todo: find cheapest, find discounts
// todo: write a parser like vbb-parse-ticket
// {
// "statusCode": "OK",
// "fareSetL": [
// {
// "fareL": [
// {
// "isFromPrice": true,
// "isPartPrice": false,
// "isBookable": true,
// "isUpsell": false,
// "targetCtx": "D",
// "buttonText": "To offer selection",
// "price": {
// "amount": 11400
// }
// }
// ]
// }
// ]
// }
if (
raw.trfRes &&
Array.isArray(raw.trfRes.fareSetL) &&
raw.trfRes.fareSetL[0] &&
Array.isArray(raw.trfRes.fareSetL[0].fareL) &&
raw.trfRes.fareSetL[0].fareL[0]
) {
const tariff = raw.trfRes.fareSetL[0].fareL[0]
if (tariff.price && tariff.price.amount >= 0) { // wat
parsed.price = {
amount: tariff.price.amount / 100,
currency: 'EUR',
hint: null
}
}
}
return parsed
}
const parseJourneyLegWithLoadFactor = ({parsed, res, opt}, raw) => {
const tcocX = raw.jny && raw.jny.dTrnCmpSX && raw.jny.dTrnCmpSX.tcocX
if (Array.isArray(tcocX) && Array.isArray(res.common.tcocL)) {
const load = parseLoadFactor(opt, res.common.tcocL, tcocX)
if (load) parsed.loadFactor = load
}
return parsed
}
// todo:
// [ { type: 'hint',
// code: 'P5',
// text: 'Es gilt ein besonderer Fahrpreis' }
const hintsByCode = Object.assign(Object.create(null), {
fb: {
type: 'hint',
code: 'bicycle-conveyance',
summary: 'bicycles conveyed'
},
fr: {
type: 'hint',
code: 'bicycle-conveyance-reservation',
summary: 'bicycles conveyed, subject to reservation'
},
nf: {
type: 'hint',
code: 'no-bicycle-conveyance',
summary: 'bicycles not conveyed'
},
k2: {
type: 'hint',
code: '2nd-class-only',
summary: '2. class only'
},
eh: {
type: 'hint',
code: 'boarding-ramp',
summary: 'vehicle-mounted boarding ramp available'
},
ro: {
type: 'hint',
code: 'wheelchairs-space',
summary: 'space for wheelchairs'
},
oa: {
type: 'hint',
code: 'wheelchairs-space-reservation',
summary: 'space for wheelchairs, subject to reservation'
},
wv: {
type: 'hint',
code: 'wifi',
summary: 'WiFi available'
},
wi: {
type: 'hint',
code: 'wifi',
summary: 'WiFi available'
},
sn: {
type: 'hint',
code: 'snacks',
summary: 'snacks available for purchase'
},
mb: {
type: 'hint',
code: 'snacks',
summary: 'snacks available for purchase'
},
mp: {
type: 'hint',
code: 'snacks',
summary: 'snacks available for purchase at the seat'
},
bf: {
type: 'hint',
code: 'barrier-free',
summary: 'barrier-free'
},
rg: {
type: 'hint',
code: 'barrier-free-vehicle',
summary: 'barrier-free vehicle'
},
bt: {
type: 'hint',
code: 'on-board-bistro',
summary: 'Bordbistro available'
},
br: {
type: 'hint',
code: 'on-board-restaurant',
summary: 'Bordrestaurant available'
},
ki: {
type: 'hint',
code: 'childrens-area',
summary: `children's area available`
},
kk: {
type: 'hint',
code: 'parents-childrens-compartment',
summary: `parent-and-children compartment available`
},
kr: {
type: 'hint',
code: 'kids-service',
summary: 'DB Kids Service available'
},
ls: {
type: 'hint',
code: 'power-sockets',
summary: 'power sockets available'
},
ev: {
type: 'hint',
code: 'replacement-service',
summary: 'replacement service'
},
kl: {
type: 'hint',
code: 'air-conditioned',
summary: 'air-conditioned vehicle'
},
r0: {
type: 'hint',
code: 'upward-escalator',
summary: 'upward escalator'
},
au: {
type: 'hint',
code: 'elevator',
summary: 'elevator available'
},
ck: {
type: 'hint',
code: 'komfort-checkin',
summary: 'Komfort-Checkin available'
},
it: {
type: 'hint',
code: 'ice-sprinter',
summary: 'ICE Sprinter service'
},
rp: {
type: 'hint',
code: 'compulsory-reservation',
summary: 'compulsory seat reservation'
},
rm: {
type: 'hint',
code: 'optional-reservation',
summary: 'optional seat reservation'
},
scl: {
type: 'hint',
code: 'all-2nd-class-seats-reserved',
summary: 'all 2nd class seats reserved'
},
acl: {
type: 'hint',
code: 'all-seats-reserved',
summary: 'all seats reserved'
},
sk: {
type: 'hint',
code: 'oversize-luggage-forbidden',
summary: 'oversize luggage not allowed'
},
hu: {
type: 'hint',
code: 'animals-forbidden',
summary: 'animals not allowed, except guide dogs'
},
ik: {
type: 'hint',
code: 'baby-cot-required',
summary: 'baby cot/child seat required'
},
ee: {
type: 'hint',
code: 'on-board-entertainment',
summary: 'on-board entertainment available'
},
toilet: {
type: 'hint',
code: 'toilet',
summary: 'toilet available'
},
oc: {
type: 'hint',
code: 'wheelchair-accessible-toilet',
summary: 'wheelchair-accessible toilet available'
},
iz: {
type: 'hint',
code: 'intercity-2',
summary: 'Intercity 2'
}
})
const codesByText = Object.assign(Object.create(null), {
'journey cancelled': 'journey-cancelled', // todo: German variant
'stop cancelled': 'stop-cancelled', // todo: change to `stopover-cancelled`, German variant
'signal failure': 'signal-failure',
'signalstörung': 'signal-failure',
'additional stop': 'additional-stopover', // todo: German variant
'platform change': 'changed platform', // todo: use dash, German variant
})
const parseHintByCode = ({parsed}, raw) => {
// plain-text hints used e.g. for stop metadata
if (raw.type === 'K') {
return {type: 'hint', text: raw.txtN}
}
if (raw.type === 'A') {
const hint = hintsByCode[raw.code && raw.code.trim().toLowerCase()]
if (hint) {
return Object.assign({text: raw.txtN}, hint)
}
}
if (parsed && raw.txtN) {
const text = trim(raw.txtN.toLowerCase(), ' ()')
if (codesByText[text]) parsed.code = codesByText[text]
}
return parsed
}
const isIBNR = /^\d{6,}$/
const formatStation = (id) => {
if (!isIBNR.test(id)) throw new Error('station ID must be an IBNR.')
return _formatStation(id)
}
// todo: find option for absolute number of results
const dbProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
addChecksum: true,
transformReqBody,
transformJourneysQuery,
products: products,
parseLocation: parseHook(_parseLocation, parseLocWithDetails),
parseJourney: parseHook(_parseJourney, parseJourneyWithPrice),
parseJourneyLeg: parseHook(_parseJourneyLeg, parseJourneyLegWithLoadFactor),
parseLine: parseHook(_parseLine, parseLineWithAdditionalName),
parseArrival: parseHook(_parseArrival, parseArrOrDepWithLoadFactor),
parseDeparture: parseHook(_parseDeparture, parseArrOrDepWithLoadFactor),
parseHint: parseHook(_parseHint, parseHintByCode),
formatStation,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
journeysFromTrip: true,
radar: true,
reachableFrom: true,
lines: false, // `.svcResL[0].res.lineL[]` is missing 🤔
}
module.exports = dbProfile
@@ -0,0 +1,31 @@
'use strict'
// todo: generate from https://reiseauskunft.bahn.de/addons/fachkonfig-utf8.cfg ?
const c = {
NONE: Symbol('no loyalty card'),
BAHNCARD: Symbol('Bahncard'),
VORTEILSCARD: Symbol('VorteilsCard'),
HALBTAXABO: Symbol('HalbtaxAbo'),
VOORDEELURENABO: Symbol('Voordeelurenabo'),
SHCARD: Symbol('SH-Card'),
GENERALABONNEMENT: Symbol('General-Abonnement')
}
// see https://gist.github.com/juliuste/202bb04f450a79f8fa12a2ec3abcd72d
const formatLoyaltyCard = (data) => {
if (data.type === c.BAHNCARD) {
if (data.discount === 25) return data.class === 1 ? 1 : 2
if (data.discount === 50) return data.class === 1 ? 3 : 4
}
if (data.type === c.VORTEILSCARD) return 9
if (data.type === c.HALBTAXABO) return data.railplus ? 10 : 11
if (data.type === c.VOORDEELURENABO) return data.railplus ? 12 : 13
if (data.type === c.SHCARD) return 14
if (data.type === c.GENERALABONNEMENT) return 15
return 0
}
module.exports = {
data: c,
format: formatLoyaltyCard
}
@@ -0,0 +1,85 @@
'use strict'
// todo: https://gist.github.com/anonymous/d3323a5d2d6e159ed42b12afd0380434#file-haf_products-properties-L1-L95
module.exports = [
{
id: 'nationalExpress',
mode: 'train',
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
},
{
id: 'national',
mode: 'train',
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
},
{
id: 'regionalExp', // todo: rename
mode: 'train',
bitmasks: [4],
name: 'RegionalExpress & InterRegio',
short: 'RE/IR',
default: true
},
{
id: 'regional',
mode: 'train',
bitmasks: [8],
name: 'Regio',
short: 'RB',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [64],
name: 'Ferry',
short: 'F',
default: true
},
{
id: 'subway',
mode: 'train',
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [256],
name: 'Tram',
short: 'T',
default: true
},
{
id: 'taxi',
mode: 'taxi',
bitmasks: [512],
name: 'Group Taxi',
short: 'Taxi',
default: true
}
]
@@ -0,0 +1,82 @@
# DB profile for `hafas-client`
[*Deutsche Bahn (DB)*](https://en.wikipedia.org/wiki/Deutsche_Bahn) is the largest German long-distance public transport company. This profile adds *DB*-specific customizations to `hafas-client`. Consider using [`db-hafas`](https://github.com/derhuerst/db-hafas#db-hafas), to always get the customized client right away.
## Usage
```js
const createClient = require('hafas-client')
const dbProfile = require('hafas-client/p/db')
// create a client with DB profile
const client = createClient(dbProfile, 'my-awesome-program')
```
## Customisations
- supports 1st and 2nd class with `journey()`
- supports [their loyalty cards](https://en.wikipedia.org/wiki/Deutsche_Bahn#Tickets) with `journey()`
- parses *DB*-specific products (such as *InterCity-Express*)
- exposes the cheapest ticket price for a `journey`
- parses [*DB*-specific station info](#additional-station-info)
### additional station info
With the `db` profile, `hafas-client` will return more station information whenever the endpoint provides it:
```js
{
type: 'station',
id: '8004585',
name: 'Oberstdorf',
// …
facilities: {
'3SZentrale': '089/13081055',
parkingLots: true,
bicycleParkingRacks: true,
localPublicTransport: true,
toilets: true,
lockers: true,
travelShop: true,
stepFreeAccess: true,
boardingAid: 'ja, um voranmeldung unter 01806 512 512* wird gebeten',
taxis: true
},
reisezentrumOpeningHours: {
Mo: '08:00-18:00',
Di: '08:00-18:00',
Mi: '08:00-18:00',
Do: '08:00-18:00',
Fr: '08:00-18:00',
Sa: '09:00-14:00',
So: '09:00-14:00'
},
// …
stops: [{
type: 'stop',
id: '965503',
name: 'Busbahnhof, Oberstdorf',
// …
reisezentrumOpeningHours: {
Mo: '08:00-18:00',
Di: '08:00-18:00',
Mi: '08:00-18:00',
Do: '08:00-18:00',
Fr: '08:00-18:00',
Sa: '09:00-14:00',
So: '09:00-14:00'
}
}]
}
```
## Using the `loyaltyCard` option
```js
const {data: loyaltyCards} = require('hafas-client/p/db/loyalty-cards')
hafas.journeys(from, to, {
loyaltyCard: {type: data.BAHNCARD, discount: 25}
})
```
@@ -0,0 +1,17 @@
{
"auth": {
"type": "AID",
"aid": "andcXUmC9Mq6hjrwDIGd2l3oiaMrTUzyH"
},
"salt": "pqjM3iKEGOAhYbX76k9R5zutv",
"client": {
"type": "AND",
"id": "HVV",
"v": "4020100",
"name": "HVVPROD_ADHOC"
},
"endpoint": "https://hvv-app.hafas.de/bin/mgate.exe",
"ext": "HVV.1",
"ver": "1.18",
"defaultLanguage": "de"
}
@@ -0,0 +1,47 @@
'use strict'
const createClient = require('../..')
const hvvProfile = require('.')
const client = createClient(hvvProfile, 'hafas-client-example')
// client.journeys('116', '5900', {results: 1, polylines: true})
// client.departures('116', {duration: 1})
// client.arrivals('116', {duration: 10, linesOfStops: true})
client.locations('dammtor', {results: 2})
// client.stop('116', {linesOfStops: true}) // Dammtor
// client.nearby({
// type: 'location',
// latitude: 53.554422,
// longitude: 9.977934
// }, {distance: 60})
// client.radar({
// north: 53.55,
// west: 9.95,
// south: 52.51,
// east: 10
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// address: 'Hamburg, Holstenwall 9',
// latitude: 53.553766,
// longitude: 9.977514
// }, {
// when: new Date('2019-05-16T10:00:00+0200'),
// maxDuration: 8
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,24 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const hvvProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
// baseProfile.salt is interpreted as hex by hafas-client
salt: Buffer.from('pqjM3iKEGOAhYbX76k9R5zutv', 'utf8'),
addMicMac: true,
products,
trip: true,
radar: true,
refreshJourney: true,
reachableFrom: true,
remarksGetPolyline: false,
lines: false, // fails with `FAIL` "HCI Service: request failed"
}
module.exports = hvvProfile
@@ -0,0 +1,92 @@
'use strict'
module.exports = [ // todo: what is `512`?
{
id: 'subway',
mode: 'train',
bitmasks: [1],
name: 'U-Bahn',
short: 'U',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [2],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'akn',
mode: 'train',
bitmasks: [4],
name: 'AKN',
short: 'A',
default: true
},
{
id: 'regional-express-train',
mode: 'train',
bitmasks: [8],
name: 'RegionalExpress',
short: 'RE',
default: true
},
{
id: 'regional-train',
mode: 'train',
bitmasks: [16],
name: 'Regionalbahn',
short: 'RB',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [32],
name: 'Fähre',
short: 'F',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [128],
name: 'Bus',
short: 'Bus',
default: true
},
{
id: 'express-bus',
mode: 'bus',
bitmasks: [256],
name: 'Schnellbus',
short: 'Schnellbus',
default: true
},
{
id: 'anruf-sammel-taxi',
mode: null, // todo
bitmasks: [1024],
name: 'Anruf-Sammel-Taxi',
short: 'AST',
default: true
},
{
id: 'long-distance-train',
mode: 'train',
bitmasks: [4096, 64],
name: 'Fernzug',
short: 'ICE/IC/EC/EN',
default: false
},
{
id: 'long-distance-bus',
mode: 'bus',
bitmasks: [2048],
name: 'Fernbus',
short: 'Fernbus',
default: false
}
]
@@ -0,0 +1,18 @@
# HVV profile for `hafas-client`
[*Hamburger Verkehrsverbund (HVV)*](https://en.wikipedia.org/wiki/Hamburger_Verkehrsverbund) is the major local transport provider in [Hamburg](https://en.wikipedia.org/wiki/Hamburg). This profile adds *HVV*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const hvvProfile = require('hafas-client/p/hvv')
// create a client with HVV profile
const client = createClient(hvvProfile, 'my-awesome-program')
```
## Customisations
- parses *HVV*-specific products (such as *AKN*)
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "nasa-apps"
},
"client": {
"type": "IPH",
"id": "NASA",
"v": "4000200",
"name": "nasaPROD"
},
"endpoint": "https://reiseauskunft.insa.de/bin/mgate.exe",
"ver": "1.44",
"defaultLanguage": "de"
}
@@ -0,0 +1,46 @@
'use strict'
const createClient = require('../..')
const insaProfile = require('.')
const client = createClient(insaProfile, 'hafas-client-example')
const hellestr1 = {
type: 'location',
id: '980801263',
address: 'Magdeburg - Leipziger Straße, Hellestraße 1',
latitude: 52.116706, longitude: 11.621821
}
// from Magdeburg-Neustadt to Magdeburg-Buckau
client.journeys('008010226', '008013456', {results: 1})
// client.departures('008010226', { duration: 5 })
// client.arrivals('8010226', {duration: 10, linesOfStops: true})
// client.locations('Magdeburg Hbf', {results: 2})
// client.locations('Kunstmuseum Kloster Unser Lieben Frauen Magdeburg', {results: 2})
// client.stop('008010226') // Magdeburg-Neustadt
// client.nearby(hellestr1)
// client.radar({
// north: 52.148364,
// west: 11.600826,
// south: 52.108486,
// east: 11.651451
// }, {results: 10})
// client.reachableFrom(hellestr1, {maxDuration: 10})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name)
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
.then(data => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,21 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const insaProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
products: products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
trip: true,
radar: true,
refreshJourneyUseOutReconL: true,
reachableFrom: true,
}
module.exports = insaProfile;
@@ -0,0 +1,60 @@
'use strict'
module.exports = [
{
id: 'nationalExpress',
mode: 'train',
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
},
{
id: 'national',
mode: 'train',
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
},
{
id: 'regional',
mode: 'train',
bitmasks: [8],
name: 'RegionalExpress & RegionalBahn',
short: 'RE/RB',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [32],
name: 'Tram',
short: 'T',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [64, 128],
name: 'Bus',
short: 'B',
default: true
},
{
id: 'tourismTrain',
mode: 'train',
bitmasks: [256],
name: 'Tourism Train',
short: 'TT',
default: true
}
]
@@ -0,0 +1,18 @@
# INSA profile for `hafas-client`
The [Nahverkehr Sachsen-Anhalt (NASA)](https://de.wikipedia.org/wiki/Nahverkehrsservice_Sachsen-Anhalt) offers [Informationssystem Nahverkehr Sachsen-Anhalt (INSA)](https://insa.de) to distribute their public transport data.
## Usage
```js
const createClient = require('hafas-client')
const insaProfile = require('hafas-client/p/insa')
// create a client with INSA profile
const client = createClient(insaProfile, 'my-awesome-program')
```
## Customisations
- parses *INSA*-specific products (such as *Tourism Train*)
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "GITvwi3BGOmTQ2a5"
},
"client": {
"type": "IPH",
"id": "INVG",
"v": "1040000",
"name": "invgPROD-APPSTORE-LIVE"
},
"endpoint": "https://fpa.invg.de/bin/mgate.exe",
"ver": "1.39",
"defaultLanguage": "de"
}
@@ -0,0 +1,45 @@
'use strict'
const createClient = require('../..')
const cmtaProfile = require('.')
const client = createClient(cmtaProfile, 'hafas-client-example')
const ingolstadtHbf = '8000183'
const audiParkplatz = '84999'
client.journeys(ingolstadtHbf, audiParkplatz, {results: 1})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.departures(ingolstadtHbf, {duration: 5})
// client.arrivals(ingolstadtHbf, {duration: 10, stationLines: true})
// client.locations('tillystr 1', {results: 2})
// client.station(audiParkplatz) // Audi Parkplatz
// client.nearby({
// type: 'location',
// latitude: 48.74453,
// longitude: 11.43733
// }, {distance: 200})
// client.radar({
// north: 48.74453,
// west: 11.42733,
// south: 48.73453,
// east: 11.43733
// }, {results: 10})
// todo: `reachableFrom` with `Ingolstadt, Tillystraße 1` 48.745769 | 11.432814
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,21 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const invgProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
refreshJourney: true,
}
module.exports = invgProfile
@@ -0,0 +1,69 @@
'use strict'
module.exports = [
// https://github.com/public-transport/hafas-client/issues/93#issuecomment-437868265
{
id: 'bus',
mode: 'bus',
bitmasks: [1, 16],
name: 'Bus',
short: 'Bus',
default: true // the other `bus` has `false`
},
{
id: 'express-train',
mode: 'train',
bitmasks: [2],
name: 'High-speed train',
short: 'Train',
default: false
},
{
id: 'regional-train',
mode: 'train',
bitmasks: [4],
name: 'Regional train',
short: 'Train',
default: false
},
{
id: 'local-train',
mode: 'train',
bitmasks: [8],
name: 'Nahverkehrszug',
short: 'Zug',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [32],
name: 'Ferry',
short: 'Ferry',
default: false
},
{
id: 'subway',
mode: 'train',
bitmasks: [64],
name: 'Subway',
short: 'Subway',
default: false
},
{
id: 'tram',
mode: 'train',
bitmasks: [128],
name: 'Tram',
short: 'Tram',
default: false
},
{
id: 'on-demand',
mode: 'bus', // todo: correct?
bitmasks: [256],
name: 'On-demand traffic',
short: 'on demand',
default: false
}
]
@@ -0,0 +1,18 @@
# INVG profile for `hafas-client`
[*Ingolstädter Verkehrsgesellschaft (INVG)*](https://de.wikipedia.org/wiki/Ingolstädter_Verkehrsgesellschaft) is a public transportation provider serving [Ingolstadt, Germany](https://en.wikipedia.org/wiki/Ingolstadt). This profile adds *INVG*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const invgProfile = require('hafas-client/p/invg')
// create a client with INVG profile
const client = createClient(invgProfile, 'my-awesome-program')
```
## Customisations
- parses *INVG*-specific products
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "P9bplgVCGnozdgQE"
},
"client": {
"type": "IPA",
"id": "IRISHRAIL",
"v": "4000100",
"name": "IrishRailPROD-APPSTORE"
},
"endpoint": "https://journeyplanner.irishrail.ie/bin/mgate.exe",
"ver": "1.33",
"defaultLanguage": "en"
}
@@ -0,0 +1,36 @@
'use strict'
const createClient = require('../..')
const irishProfile = require('.')
const client = createClient(irishProfile, 'hafas-client example')
// from Dublin to Belfast Central
client.journeys('9909002', '9990840', {results: 1})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// client.departures('9909002', {duration: 5})
// client.arrivals('9909002', {duration: 10, linesOfStops: true})
// client.locations('Dublin', {results: 2})
// client.locations('Hochschule Dublin', {poi: true, addressses: false, fuzzy: false})
// client.stop('9909002') // Dublin
// client.nearby({
// type: 'location',
// latitude: 53.353,
// longitude: -6.247
// }, {distance: 200})
// client.radar({
// north: 53.35,
// west: -6.245,
// south: 53.34,
// east: -6.244
// }, {results: 10})
.then(data => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,28 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const transformReqBody = (ctx, body) => {
body.lang = 'ga'
return body
}
const irishRailProfile = {
...baseProfile,
locale: 'en-IE',
timezone: 'Europe/Dublin',
transformReqBody,
salt: Buffer.from('i5s7m3q9z6b4k1c2', 'utf8'),
addMicMac: true,
products: products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourney: false, // fails with `CGI_READ_FAILED`
trip: true,
radar: true,
}
module.exports = irishRailProfile;
@@ -0,0 +1,38 @@
'use strict'
module.exports = [
{
id: 'national-train',
mode: 'train',
bitmasks: [2],
name: 'InterCity',
short: 'IC',
default: true
},
// todo: 4
{
id: 'local-train',
mode: 'train',
bitmasks: [8],
name: 'Commuter',
short: 'Commuter',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [16],
name: 'Dublin Area Rapid Transit',
short: 'DART',
default: true
},
// todo: 32
{
id: 'luas',
mode: 'train',
bitmasks: [64],
name: 'LUAS Tram',
short: 'LUAS',
default: true
}
]
@@ -0,0 +1,18 @@
# Irish Rail profile for `hafas-client`
The [*Iarnród Éireann* (Irish Rail)](https://en.wikipedia.org/wiki/Iarnród_Éireann) is the national railway company of Ireland. This profile adds *Iarnród Éireann*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const irishProfile = require('hafas-client/p/irish-rail')
// create a client with Irish Rail profile
const client = createClient(irishProfile, 'my-awesome-program')
```
## Customisations
- parses *Irish Rail*-specific products (such as *LUAS* or *DART*)
@@ -0,0 +1,13 @@
{
"auth": {
"type": "AID",
"aid": "wf7mcf9bv3nv8g5f"
},
"client": {
"type": "WEB",
"id": "VAO",
"name": "webapp"
},
"endpoint": "https://fahrplan.ivb.at/bin/mgate.exe",
"defaultLanguage": "de"
}
@@ -0,0 +1,6 @@
-----BEGIN CERTIFICATE-----
MIIE6jCCA9KgAwIBAgIQCjUI1VwpKwF9+K1lwA/35DANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0yMDA5MjQwMDAwMDBaFw0zMDA5MjMyMzU5NTlaME8xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxKTAnBgNVBAMTIERpZ2lDZXJ0IFRMUyBSU0EgU0hBMjU2IDIwMjAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUuzZUdwvN1PWNvsnO3DZuUfMRNUrUpmRh8sCuxkB+Uu3Ny5CiDt3+PE0J6aqXodgojlEVbbHp9YwlHnLDQNLtKS4VbL8Xlfs7uHyiUDe5pSQWYQYE9XE0nw6Ddng9/n00tnTCJRpt8OmRDtV1F0JuJ9x8piLhMbfyOIJVNvwTRYAIuE//i+p1hJInuWraKImxW8oHzf6VGo1bDtN+I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGBAfr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6JWvHgXjkkDKa77SU+kFbnO8lwZV21reacroicgE7XQPUDTITAHk+qZ9QIDAQABo4IBrjCCAaowHQYDVR0OBBYEFLdrouqoqoSMeeq02g+YssWVdrn0MB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNydDB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDAGA1UdIAQpMCcwBwYFZ4EMAQEwCAYGZ4EMAQIBMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEBAHert3onPa679n/gWlbJhKrKW3EX3SJH/E6f7tDBpATho+vFScH90cnfjK+URSxGKqNjOSD5nkoklEHIqdninFQFBstcHL4AGw+oWv8Zu2XHFq8hVt1hBcnpj5h232sb0HIMULkwKXq/YFkQZhM6LawVEWwtIwwCPgU7/uWhnOKK24fXSuhe50gG66sSmvKvhMNbg0qZgYOrAKHKCjxMoiWJKiKnpPMzTFuMLhoClw+dj20tlQj7T9rxkTgl4ZxuYRiHas6xuwAwapu3r9rxxZf+ingkquqTgLozZXq8oXfpf2kUCwA/d5KxTVtzhwoT0JzI8ks5T1KESaZMkE4f97Q=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQkCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
@@ -0,0 +1,49 @@
'use strict'
const createClient = require('../..')
const ivbProfile = require('.')
const client = createClient(ivbProfile, 'hafas-client example')
const innsbruckGriesauweg = '476162400'
const völsWest = '476431800'
// client.journeys(innsbruckGriesauweg, völsWest, {
// results: 1, stopovers: true,
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// client.departures(innsbruckGriesauweg, {duration: 1})
// client.arrivals(innsbruckGriesauweg, {duration: 10, linesOfStops: true})
client.locations('griesauweg', {results: 3})
// client.stop(innsbruckGriesauweg, {linesOfStops: true})
// client.nearby({
// type: 'location',
// id: '980076175',
// address: 'Lönsstraße 9, 6020 Innsbruck',
// latitude: 47.262765,
// longitude: 11.419851,
// }, {distance: 1000})
// client.reachableFrom({
// type: 'location',
// id: '980076175',
// address: 'Lönsstraße 9, 6020 Innsbruck',
// latitude: 47.262765,
// longitude: 11.419851,
// }, {
// maxDuration: 30,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,104 @@
'use strict'
const {readFileSync} = require('fs')
const {join} = require('path')
const {Agent} = require('https')
const baseProfile = require('./base.json')
const products = [{
id: 'train-and-s-bahn',
mode: 'train',
bitmasks: [1, 2],
name: 'Bahn & S-Bahn',
short: 'Bahn & S-Bahn',
default: true,
}, {
id: 'u-bahn',
mode: 'train',
bitmasks: [4],
name: 'U-Bahn',
short: 'U-Bahn',
default: true,
}, {
id: 'tram',
mode: 'train',
bitmasks: [16],
name: 'Straßenbahn',
short: 'Straßenbahn',
default: true,
}, {
id: 'city-bus',
mode: 'bus',
bitmasks: [128],
name: 'Stadtbus',
short: 'Stadtbus',
default: true,
}, {
id: 'regional-bus',
mode: 'bus',
bitmasks: [64],
name: 'Regionalbus',
short: 'Regionalbus',
default: true,
}, {
id: 'long-distance-bus',
mode: 'bus',
bitmasks: [32],
name: 'Fernbus',
short: 'Fernbus',
default: true,
}, {
id: 'other-bus',
mode: 'bus',
bitmasks: [2048],
name: 'sonstige Busse',
short: 'sonstige Busse',
default: true,
}, {
id: 'aerial-lift',
mode: 'gondola',
bitmasks: [256],
name: 'Seil-/Zahnradbahn',
short: 'Seil-/Zahnradbahn',
default: true,
}, {
id: 'ferry',
mode: 'watercraft',
bitmasks: [512],
name: 'Schiff',
short: 'Schiff',
default: true,
}, {
id: 'on-call',
mode: 'taxi',
bitmasks: [1024],
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
// `fahrplan.ivb.at:443` doesn't provide the necessary CA certificate chain for
// Node.js to trust the certificate, so we manually add it.
// todo: fix this properly, e.g. by letting them know
const ca = readFileSync(join(__dirname, 'digicert-tls-rsa-sha256-2020-ca1.crt.pem'))
const agent = new Agent({ca})
const transformReq = (ctx, req) => ({...req, agent})
const ivbProfile = {
...baseProfile,
ver: '1.32',
transformReq,
locale: 'at-DE',
timezone: 'Europe/Vienna',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
module.exports = ivbProfile
@@ -0,0 +1,15 @@
# IVB profile for `hafas-client`
[*Innsbrucker Verkehrsbetriebe (IVB)*](https://de.wikipedia.org/wiki/Innsbrucker_Verkehrsbetriebe_und_Stubaitalbahn) is the local transport provider of [Innsbruck](https://en.wikipedia.org/wiki/Innsbruck). This profile adds *IVB* support to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const ivbProfile = require('hafas-client/p/ivb')
// create a client with IVB profile
const client = createClient(ivbProfile, 'my-awesome-program')
```
Check out the [code examples](example.js).
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "Kdf0LNRWYg5k3499"
},
"client": {
"type": "IPH",
"id": "DB-REGIO-NRW",
"v": "6000300",
"name": "NRW"
},
"endpoint": "https://nrw.hafas.de/bin/mgate.exe",
"ver": "1.34",
"defaultLanguage": "de"
}
@@ -0,0 +1,54 @@
'use strict'
const {inspect} = require('util')
const createClient = require('../..')
const cflProfile = require('.')
const client = createClient(cflProfile, 'hafas-client-example')
const soest = '8000076'
const aachenHbf = '8000001'
client.journeys(soest, aachenHbf, {results: 1, stopovers: true})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.locations('soest', {results: 3})
// client.reachableFrom({
// type: 'location',
// id: '980301639',
// latitude: 51.387609,
// longitude: 6.684019,
// address: 'Duisburg, Am Mühlenberg 1',
// }, {
// maxDuration: 15,
// })
// client.nearby({
// type: 'location',
// latitude: 51.4503,
// longitude: 6.6581,
// }, {distance: 1200})
// client.station(soest)
// client.departures(soest, {duration: 20})
// client.radar({
// north: 51.4358,
// west: 6.7625,
// south: 51.4214,
// east: 6.7900,
// }, {results: 10})
// client.remarks()
.then(data => {
console.log(inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,22 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const cflProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
trip: true,
radar: true,
reachableFrom: true,
refreshJourneyUseOutReconL: true,
remarks: true,
}
module.exports = cflProfile;
@@ -0,0 +1,78 @@
'use strict'
module.exports = [
{
id: 'regional-train',
mode: 'train',
bitmasks: [8],
// todo: specify explicitly which
name: 'regional train',
short: 'regional train',
default: true,
},
{
id: 'urban-train',
mode: 'train',
bitmasks: [16],
name: 'urban train',
short: 'urban train',
default: true,
},
{
id: 'subway',
mode: 'train',
bitmasks: [128],
name: 'subway',
short: 'subway',
default: true,
},
{
id: 'tram',
mode: 'train',
bitmasks: [256],
name: 'tram',
short: 'tram',
default: true,
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'bus',
short: 'bus',
default: true,
},
{
id: 'dial-a-ride',
mode: 'taxi',
bitmasks: [512],
name: 'dial-a-ride',
short: 'dial-a-ride',
default: true,
},
{
id: 'long-distance-train',
mode: 'train',
bitmasks: [4],
// todo: specify explicitly which
name: 'long-distance train',
short: 'long-distance train',
default: true,
},
{
id: 'express-train',
mode: 'train',
bitmasks: [1],
name: 'ICE',
short: 'ICE',
default: true,
},
{
id: 'ec-ic',
mode: 'ec-ic',
bitmasks: [2],
name: 'EC/IC',
short: 'EC/IC',
default: true,
}
]
@@ -0,0 +1,18 @@
# *mobil.nrw* profile for `hafas-client`
[*mobil.nrw*](https://www.mobil.nrw) is the name of the travel planning service of the [*NRW-Tarif*](https://de.wikipedia.org/wiki/NRW-Tarif). This profile adds *mobil.nrw*-specific customisations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const mobilNrwProfile = require('hafas-client/p/mobil-nrw')
// create a client with mobil.nrw profile
const client = createClient(mobilNrwProfile)
```
## Customisations
- *mobil.nrw*-specific products
@@ -0,0 +1,14 @@
{
"auth": {
"type": "AID",
"aid": "SkC81GuwuzL4e0"
},
"client": {
"type": "WEB",
"id": "MMILUX",
"name": "webapp"
},
"endpoint": "https://cdt.hafas.de/bin/mgate.exe",
"ver": "1.43",
"defaultLanguage": "de"
}
@@ -0,0 +1,52 @@
'use strict'
const createClient = require('../..')
const mobiliteitProfile = require('.')
const client = createClient(mobiliteitProfile, 'hafas-client example')
const mersch = '160904011'
const luxembourgCentral = '200405060'
// from Mersch to Bruxelles Central
client.journeys(mersch, luxembourgCentral, {results: 1})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.locations('mersch', {results: 3})
// client.stop(mersch)
// client.nearby({
// type: 'location',
// latitude: 49.7523,
// longitude: 6.1103
// }, {distance: 500})
// client.departures(mersch, {duration: 5})
// client.arrivals(mersch, {duration: 10, linesOfStops: true})
// client.radar({
// north: 49.9,
// west: 6.11,
// south: 49.7,
// east: 6.13
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// id: '990005227',
// address: 'Mersch, Rue Mies 1',
// latitude: 49.746044, longitude: 6.102228,
// }, {
// maxDuration: 30
// })
.then(data => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,22 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const mobiliteitLuProfile = {
...baseProfile,
locale: 'de-LU',
timezone: 'Europe/Luxembourg',
products: products,
trip: true,
radar: true,
reachableFrom: true,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
}
module.exports = mobiliteitLuProfile;
@@ -0,0 +1,44 @@
'use strict'
module.exports = [
{
id: 'express-train',
mode: 'train',
bitmasks: [1],
name: 'local train (TGV/ICE)',
short: 'TGV/ICE',
default: true
},
{
id: 'national-train',
mode: 'train',
bitmasks: [2, 4],
name: 'national train (IC/RE/IRE)',
short: 'IC/RE/IRE',
default: true
},
{
id: 'local-train',
mode: 'train',
bitmasks: [8],
name: 'local train (RB/TER)',
short: 'RB/TER',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [256],
name: 'Tram',
short: 'Tram',
default: true
}
]
@@ -0,0 +1,18 @@
# mobiliteit.lu profile for `hafas-client`
[*mobiliteit.lu*](https://www.mobiliteit.lu) provides public transport data for Luxembourg. This profile adds *mobiliteit.lu*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const mobiliteitProfile = require('hafas-client/p/mobiliteit-lu')
// create a client with mobiliteit.lu profile
const client = createClient(mobiliteitProfile, 'my-awesome-program')
```
## Customisations
- parses *mobiliteit.lu*-specific products
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "r0Ot9FLFNAFxijLW"
},
"client": {
"type": "IPH",
"id": "NAHSH",
"v": "3000700",
"name": "NAHSHPROD"
},
"endpoint": "https://nah.sh.hafas.de/bin/mgate.exe",
"ver": "1.30",
"defaultLanguage": "de"
}
@@ -0,0 +1,52 @@
'use strict'
const createClient = require('../..')
const nahshProfile = require('.')
const client = createClient(nahshProfile, 'hafas-client-example')
// Flensburg Hbf to Kiel Hbf
client.journeys('8000103', '8000199', {results: 10, tickets: true})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.departures('8000199', {duration: 10})
// client.arrivals('8000199', {duration: 5, linesOfStops: true})
// client.trip('1|30161|5|100|14032018', 'Bus 52')
// client.locations('Schleswig', {results: 1})
// client.stop('706990') // Kiel Holunderbusch
// client.nearby({
// type: 'location',
// latitude: 54.295691,
// longitude: 10.116424
// }, {distance: 60})
// client.reachableFrom({
// type: 'location',
// address: 'Husum, Berliner Straße 80',
// latitude: 54.488995,
// longitude: 9.056263
// }, {
// when: new Date('2018-08-27T10:00:00+0200'),
// maxDuration: 20
// })
// client.radar({
// north: 54.4,
// west: 10.0,
// south: 54.2,
// east: 10.2
// }, {
// results: 10,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,88 @@
'use strict'
const {parseHook} = require('../../lib/profile-hooks')
const _parseLocation = require('../../parse/location')
const _parseJourney = require('../../parse/journey')
const _parseMovement = require('../../parse/movement')
const baseProfile = require('./base.json')
const products = require('./products')
// todo: journey prices
const fixLocation = ({parsed}, l) => {
// weird fix for empty lines, e.g. IC/EC at Flensburg Hbf
if (parsed.lines) {
parsed.lines = parsed.lines.filter(x => x.id && x.name)
}
// remove leading zeroes, todo
if (parsed.id && parsed.id.length > 0) {
parsed.id = parsed.id.replace(/^0+/, '')
}
return parsed
}
const parseJourneyWithTickets = ({parsed}, j) => {
if (
j.trfRes &&
Array.isArray(j.trfRes.fareSetL) &&
j.trfRes.fareSetL.length > 0
) {
parsed.tickets = []
for (let t of j.trfRes.fareSetL) {
const tariff = t.desc
if (!tariff || !Array.isArray(t.fareL)) continue
for (let v of t.fareL) {
const variant = v.name
if(!variant) continue
const ticket = {
name: [tariff, variant].join(' - '),
tariff,
variant
}
if (v.prc && Number.isInteger(v.prc) && v.cur) {
ticket.amount = v.prc/100
ticket.currency = v.cur
} else {
ticket.amount = null
ticket.hint = 'No pricing information available.'
}
parsed.tickets.push(ticket)
}
}
}
return parsed
}
const fixMovement = ({parsed}, m) => {
// filter out empty nextStopovers entries
parsed.nextStopovers = parsed.nextStopovers.filter((f) => {
return f.stop !== null || f.arrival !== null || f.departure !== null
})
return parsed
}
const nahshProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
products,
parseLocation: parseHook(_parseLocation, fixLocation),
parseJourney: parseHook(_parseJourney, parseJourneyWithTickets),
parseMovement: parseHook(_parseMovement, fixMovement),
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
reachableFrom: true,
}
module.exports = nahshProfile
@@ -0,0 +1,86 @@
'use strict'
const p = [
{
id: 'nationalExpress',
mode: 'train',
bitmasks: [1],
name: 'High-speed rail',
short: 'ICE/HSR',
default: true
},
{
id: 'national',
mode: 'train',
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
},
{ // todo: also includes EN?
id: 'interregional',
mode: 'train',
bitmasks: [4],
name: 'Interregional',
short: 'IR',
default: true
},
{
id: 'regional',
mode: 'train',
bitmasks: [8],
name: 'Regional & RegionalExpress',
short: 'RB/RE',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [64],
name: 'Ferry',
short: 'F',
default: true
},
{
id: 'subway',
mode: 'train',
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [256],
name: 'Tram',
short: 'T',
default: true
},
{
id: 'onCall',
mode: 'bus', // todo: is this correct?
bitmasks: [512],
name: 'On-call transit',
short: 'on-call',
default: true
}
]
module.exports = p
@@ -0,0 +1,18 @@
# NAH.SH profile for `hafas-client`
[*Nahverkehrsverbund Schleswig-Holstein (NAH.SH)*](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) is the transportation authority for regional transport in [Schleswig-Holstein](https://en.wikipedia.org/wiki/Schleswig-Holstein). This profile adds *NAH.SH*-specific customizations to `hafas-client`. Consider using [`nahsh-hafas`](https://github.com/juliuste/nahsh-hafas), to always get the customized client right away.
## Usage
```js
const createClient = require('hafas-client')
const nahshProfile = require('hafas-client/p/nahsh')
// create a client with NAH.SH profile
const client = createClient(nahshProfile, 'my-awesome-program')
```
## Customisations
- parses *NAH.SH*-specific products
@@ -0,0 +1,16 @@
{
"auth": {
"type": "AID",
"aid": "Kt8eNOH7qjVeSxNA"
},
"client": {
"type": "IPH",
"id": "NVV",
"v": "5000300",
"name": "NVVMobilPROD_APPSTORE"
},
"endpoint": "https://auskunft.nvv.de/auskunft/bin/app/mgate.exe",
"ext": "NVV.6.0",
"ver": "1.45",
"defaultLanguage": "de"
}
@@ -0,0 +1,49 @@
'use strict'
const createClient = require('../..')
const nvvProfile = require('.')
const client = createClient(nvvProfile, 'hafas-client-example')
// Scheidemannplatz to Auestadion
client.journeys('2200073', '2200042', {results: 1, polylines: true})
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs.find(l => !!l.line)
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.departures('2200005', {duration: 1})
// client.arrivals('2200005', {duration: 10, linesOfStops: true})
// client.locations('kirchweg', {results: 2})
// client.stop('2200005', {linesOfStops: true}) // Kassel Kirchweg
// client.nearby({
// type: 'location',
// latitude: 51.313,
// longitude: 9.4654
// }, {distance: 400})
// client.radar({
// north: 51.320153,
// west: 9.458359,
// south: 51.304304,
// east: 9.493672
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// id: '990100251',
// address: 'Kassel, Heckerstraße 2',
// latitude: 51.308108,
// longitude: 9.475152
// }, {
// when: new Date('2019-04-02T10:00:00+0200'),
// maxDuration: 10
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,21 @@
'use strict'
const baseProfile = require('./base.json')
const products = require('./products')
const nvvProfile = {
...baseProfile,
locale: 'de-DE',
timezone: 'Europe/Berlin',
products: products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
reachableFrom: true,
}
module.exports = nvvProfile
@@ -0,0 +1,61 @@
'use strict'
module.exports = [
// todo: what is `256`?
{
id: 'express',
mode: 'train',
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
},
{
id: 'national',
mode: 'train',
bitmasks: [2],
name: 'EuroCity/InterCity',
short: 'EC/IC',
default: true
},
{
id: 'regional',
mode: 'train',
bitmasks: [4],
name: 'Regionalzug',
short: 'RE/RB',
default: true
},
{
id: 'regiotram',
mode: 'train',
bitmasks: [1024, 16, 8], // it is `1048` actually
name: 'RegioTram',
short: 'RegioTram',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [4, 32],
name: 'Tram',
short: 'Tram',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [128, 64], // it is `192` actually
name: 'Bus',
short: 'Bus',
default: true
},
{
id: 'on-call',
mode: 'taxi', // todo: or `bus`?
bitmasks: [512],
name: 'AnrufSammelTaxi',
short: 'Sammeltaxi',
default: true
}
]
@@ -0,0 +1,18 @@
# NVV profile for `hafas-client`
[*Nordhessischer Verkehrsverbund (NVV)*](https://en.wikipedia.org/wiki/Nordhessischer_Verkehrsverbund) is a local transport association in [Hesse](https://en.wikipedia.org/wiki/Hesse). This profile adds *NVV*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const nvvProfile = require('hafas-client/p/nvv')
// create a client with NVV profile
const client = createClient(nvvProfile, 'my-awesome-program')
```
## Customisations
- parses *NVV*-specific products (such as *RegioTram*)
@@ -0,0 +1,15 @@
{
"auth": {
"type": "AID",
"aid": "OWDL4fE4ixNiPBBm"
},
"client": {
"type": "IPH",
"id": "OEBB",
"v": "6030600",
"name": "oebbPROD-ADHOC"
},
"endpoint": "https://fahrplan.oebb.at/bin/mgate.exe",
"ver": "1.41",
"defaultLanguage": "de"
}
@@ -0,0 +1,48 @@
'use strict'
const createClient = require('../..')
const oebbProfile = require('.')
const client = createClient(oebbProfile, 'hafas-client-example')
// Wien Westbahnhof to Salzburg Hbf
client.journeys('1291501', '8100002', {results: 1})
// .then(({journeys}) => {
// const leg = journeys[0].legs[0]
// return client.trip(leg.tripId, leg.line.name)
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// client.departures('8100002', {duration: 1})
// client.arrivals('8100002', {duration: 10, linesOfStops: true})
// client.locations('Salzburg', {results: 2})
// client.stop('8100173') // Graz Hbf
// client.nearby({
// type: 'location',
// latitude: 47.812851,
// longitude: 13.045604
// }, {distance: 60})
// client.radar({
// north: 47.827203,
// west: 13.001261,
// south: 47.773278,
// east: 13.07562
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// id: '970053039',
// name: 'Graz, BILLA, Hauptplatz',
// latitude: 47.070656,
// longitude: 15.438002
// }, {
// when: new Date('2018-08-27T10:00:00+0200'),
// maxDuration: 20
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,66 @@
'use strict'
// todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L5
// todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L47-L234
const {parseHook} = require('../../lib/profile-hooks')
const _parseLocation = require('../../parse/location')
const _parseMovement = require('../../parse/movement')
const baseProfile = require('./base.json')
const products = require('./products')
// ÖBB has some 'stations' **in austria** with no departures/products,
// like station entrances, that are actually POIs.
const fixWeirdPOIs = ({parsed}) => {
if (
(parsed.type === 'station' || parsed.type === 'stop') &&
!parsed.products &&
parsed.name &&
parsed.id && parsed.id.length !== 7
) {
return Object.assign({
type: 'location',
id: parsed.id,
poi: true,
name: parsed.name
}, parsed.location)
}
return parsed
}
const fixMovement = ({parsed}, m) => {
// filter out POIs
// todo: make use of them, as some of them specify fare zones
parsed.nextStopovers = parsed.nextStopovers.filter(st => {
let s = st.stop || {}
if (s.station) s = s.station
return s.type === 'stop' || s.type === 'station'
})
parsed.frames = parsed.frames.filter((f) => {
return f.origin.type !== 'location' && f.destination.type !== 'location'
})
return parsed
}
const oebbProfile = {
...baseProfile,
locale: 'de-AT',
timezone: 'Europe/Vienna',
defaultLanguage: 'de',
products: products,
parseLocation: parseHook(_parseLocation, fixWeirdPOIs),
parseMovement: parseHook(_parseMovement, fixMovement),
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
reachableFrom: true,
lines: false, // `.svcResL[0].res.lineL[]` is missing 🤔
}
module.exports = oebbProfile
@@ -0,0 +1,84 @@
'use strict'
module.exports = [
{
id: 'nationalExpress',
mode: 'train',
bitmasks: [1],
name: 'InterCityExpress & RailJet',
short: 'ICE/RJ',
default: true
},
{
id: 'national',
mode: 'train',
bitmasks: [2, 4],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
},
{
id: 'interregional',
mode: 'train',
bitmasks: [8, 4096],
name: 'Durchgangszug & EuroNight',
short: 'D/EN',
default: true
},
{
id: 'regional',
mode: 'train',
bitmasks: [16],
name: 'Regional & RegionalExpress',
short: 'R/REX',
default: true
},
{
id: 'suburban',
mode: 'train',
bitmasks: [32],
name: 'S-Bahn',
short: 'S',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [64],
name: 'Bus',
short: 'B',
default: true
},
{
id: 'ferry',
mode: 'watercraft',
bitmasks: [128],
name: 'Ferry',
short: 'F',
default: true
},
{
id: 'subway',
mode: 'train',
bitmasks: [256],
name: 'U-Bahn',
short: 'U',
default: true
},
{
id: 'tram',
mode: 'train',
bitmasks: [512],
name: 'Tram',
short: 'T',
default: true
},
{
id: 'onCall',
mode: null, // todo
bitmasks: [2048],
name: 'on-call transit, lifts, etc',
short: 'on-call/lift',
default: true
}
]
@@ -0,0 +1,19 @@
# ÖBB profile for `hafas-client`
[*Österreichische Bundesbahnen (ÖBB)*](https://en.wikipedia.org/wiki/Austrian_Federal_Railways) is the largest Austrian long-distance public transport company. This profile adds *ÖBB*-specific customizations to `hafas-client`. Consider using [`oebb-hafas`](https://github.com/juliuste/oebb-hafas#oebb-hafas), to always get the customized client right away.
## Usage
```js
const createClient = require('hafas-client')
const oebbProfile = require('hafas-client/p/oebb')
// create a client with ÖBB profile
const client = createClient(oebbProfile, 'my-awesome-program')
```
## Customisations
- parses *ÖBB*-specific products (such as *RailJet*)
- parses invalid empty stations from the API as [`location`](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#location-objects)s
@@ -0,0 +1,14 @@
{
"auth": {
"type": "AID",
"aid": "and20201hf7mcf9bv3nv8g5f"
},
"client": {
"type": "AND",
"id": "VAO"
},
"endpoint": "https://app.verkehrsauskunft.at/bin/mgate.exe",
"ext": "VAO.11",
"ver": "1.27",
"defaultLanguage": "de"
}
@@ -0,0 +1,49 @@
'use strict'
const createClient = require('../..')
const vvvProfile = require('.')
const client = createClient(vvvProfile, 'hafas-client example')
const linzTheatergasse = '444670100'
const amstettenStadtbad = '431507400'
// client.journeys(linzTheatergasse, amstettenStadtbad, {
// results: 1, stopovers: true,
// })
// .then(({journeys}) => {
// const [journey] = journeys
// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true})
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs.find(l => !!l.line)
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
client.departures(linzTheatergasse, {duration: 12 * 60})
// client.arrivals(linzTheatergasse, {duration: 10, linesOfStops: true})
// client.locations('theatergasse', {results: 3})
// client.stop(linzTheatergasse, {linesOfStops: true})
// client.nearby({
// type: 'location',
// id: '980115190',
// address: 'Steingasse 19, 4020 Linz',
// latitude: 48.301181,
// longitude: 14.284057,
// }, {distance: 1000})
// client.reachableFrom({
// type: 'location',
// id: '980115190',
// address: 'Steingasse 19, 4020 Linz',
// latitude: 48.301181,
// longitude: 14.284057,
// }, {
// maxDuration: 30,
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,100 @@
'use strict'
const baseProfile = require('./base.json')
const products = [{
id: 'train-and-s-bahn',
mode: 'train',
bitmasks: [1, 2],
name: 'Bahn & S-Bahn',
short: 'Bahn & S-Bahn',
default: true,
}, {
id: 'u-bahn',
mode: 'train',
bitmasks: [4],
name: 'U-Bahn',
short: 'U-Bahn',
default: true,
}, {
id: 'tram',
mode: 'train',
bitmasks: [16],
name: 'Straßenbahn',
short: 'Straßenbahn',
default: true,
}, {
id: 'city-bus',
mode: 'bus',
bitmasks: [128],
name: 'Stadtbus',
short: 'Stadtbus',
default: true,
}, {
id: 'regional-bus',
mode: 'bus',
bitmasks: [64],
name: 'Regionalbus',
short: 'Regionalbus',
default: true,
}, {
id: 'long-distance-bus',
mode: 'bus',
bitmasks: [32],
name: 'Fernbus',
short: 'Fernbus',
default: true,
}, {
id: 'other-bus',
mode: 'bus',
bitmasks: [2048],
name: 'sonstige Busse',
short: 'sonstige Busse',
default: true,
}, {
id: 'aerial-lift',
mode: 'gondola',
bitmasks: [256],
name: 'Seil-/Zahnradbahn',
short: 'Seil-/Zahnradbahn',
default: true,
}, {
id: 'ferry',
mode: 'watercraft',
bitmasks: [512],
name: 'Schiff',
short: 'Schiff',
default: true,
}, {
id: 'on-call',
mode: 'taxi',
bitmasks: [1024],
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
const vosProfile = {
...baseProfile,
auth: {
type: 'USER',
aid: 'and20201hf7mcf9bv3nv8g5f',
user: 'mobile',
pw: '87a6f8ZbnBih32',
},
addMicMac: true,
salt: '6633673735743766726667323938336A',
locale: 'at-DE',
timezone: 'Europe/Vienna',
products,
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
module.exports = vosProfile
@@ -0,0 +1,15 @@
# OÖVV profile for `hafas-client`
[*Oberösterreichischer Verkehrsverbund (OÖVV)*](https://de.wikipedia.org/wiki/Oberösterreichischer_Verkehrsverbund) is the local transport provider of [Upper Austria](https://en.wikipedia.org/wiki/Upper_Austria). This profile adds *OÖVV* support to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const oövvProfile = require('hafas-client/p/ooevv')
// create a client with OÖVV profile
const client = createClient(oövvProfile, 'my-awesome-program')
```
Check out the [code examples](example.js).
@@ -0,0 +1,13 @@
{
"auth": {
"type": "AID",
"aid": "DrxJYtYZQpEBCtcb"
},
"client": {
"type": "AND",
"id": "HAFAS"
},
"endpoint": "https://mobil.rozklad-pkp.pl:8019/bin/mgate.exe",
"ver": "1.21",
"defaultLanguage": "en"
}
@@ -0,0 +1,46 @@
'use strict'
const createClient = require('../..')
const pkpProfile = require('.')
const client = createClient(pkpProfile, 'hafas-client-example')
const wrocławGł = '5100069'
const krakówGł = '5100028'
client.journeys(krakówGł, wrocławGł, {results: 1, polylines: true})
// client.departures(krakówGł, {duration: 10})
// client.arrivals(krakówGł, {duration: 10, linesOfStops: true})
// client.locations('kraków', {results: 2})
// client.stop(krakówGł, {linesOfStops: true})
// client.nearby({
// type: 'location',
// latitude: 50.067192,
// longitude: 19.947423
// }, {distance: 60})
// client.radar({
// north: 50.2,
// west: 19.8,
// south: 49.9,
// east: 20.1
// }, {results: 10})
// client.reachableFrom({
// type: 'location',
// address: 'Bydgoszcz, Dworcowa 100',
// latitude: 53.1336648,
// longitude: 17.9908571
// }, {
// when: new Date(),
// maxDuration: 20
// })
// .then(({journeys}) => {
// const [journey] = journeys
// const leg = journey.legs[0]
// return client.trip(leg.tripId, leg.line.name, {polyline: true})
// })
.then((data) => {
console.log(require('util').inspect(data, {depth: null, colors: true}))
})
.catch(console.error)
@@ -0,0 +1,35 @@
'use strict'
const {parseHook} = require('../../lib/profile-hooks')
const parseLocation = require('../../parse/location')
const baseProfile = require('./base.json')
const products = require('./products')
const trimStopName = ({parsed}, l) => {
if (parsed.type === 'stop' || parsed.type === 'station' && parsed.name) {
parsed.name = parsed.name.replace(/(^-|-$)/g, '')
}
return parsed
}
const pkpProfile = {
...baseProfile,
locale: 'pl-PL',
timezone: 'Europe/Warsaw',
products,
parseLocation: parseHook(parseLocation, trimStopName),
trip: true,
radar: true,
refreshJourney: false,
reachableFrom: true,
remarks: false, // seems like ver >= 1.20 is required
departuresGetPasslist: false,
departuresStbFltrEquiv: false,
}
module.exports = pkpProfile
@@ -0,0 +1,36 @@
'use strict'
module.exports = [
{
id: 'high-speed-train',
mode: 'train',
bitmasks: [1, 2],
name: 'ExpressInterCity & ExpressInterCity Premium & InterCityExpress',
short: 'EIC/EIP/ICE',
default: true
},
{
id: 'long-distance-train',
mode: 'train',
bitmasks: [4],
name: 'InterCity & Twoje Linie Kolejowe & EuroCity & EuroNight',
short: 'IC/TLK/EC/EN',
default: true
},
{
id: 'regional-train',
mode: 'train',
bitmasks: [8],
name: 'Regional',
short: 'R',
default: true
},
{
id: 'bus',
mode: 'bus',
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
}
]
@@ -0,0 +1,18 @@
# PKP profile for `hafas-client`
[*Polskie Koleje Państwowe (PKP)*](https://en.wikipedia.org/wiki/Polish_State_Railways) is the major national transport provider in Poland. This profile adds *PKP*-specific customizations to `hafas-client`.
## Usage
```js
const createClient = require('hafas-client')
const pkpProfile = require('hafas-client/p/pkp')
// create a client with PKP profile
const client = createClient(pkpProfile, 'my-awesome-program')
```
## Customisations
- parses *PKP*-specific products (such as *TLK*)

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