diff --git a/p/vbb/example.js b/p/vbb/example.js new file mode 100644 index 00000000..a347a4fc --- /dev/null +++ b/p/vbb/example.js @@ -0,0 +1,16 @@ +'use strict' + +const createClient = require('../..') +const vbbProfile = require('.') + +const client = createClient(vbbProfile) + +client.journeys('900000003201', '900000024101', {results: 1}) +// client.departures('900000013102', {duration: 1}) +// client.locations('Alexanderplatz', {results: 2}) +// client.nearby(52.5137344, 13.4744798, {distance: 60}) +// client.radar(52.52411, 13.41002, 52.51942, 13.41709, {results: 10}) +.then((data) => { + console.log(data) + // console.log(require('util').inspect(data, {depth: null})) +}, console.error) diff --git a/p/vbb/index.js b/p/vbb/index.js new file mode 100644 index 00000000..9bbfe0bc --- /dev/null +++ b/p/vbb/index.js @@ -0,0 +1,78 @@ +'use strict' + +const shorten = require('vbb-short-station-name') + +const _formatStation = require('../../format/station') +const _parseLine = require('../../parse/line') +const _parseLocation = require('../../parse/location') + +const modes = require('./modes') + +const transformReqBody = (body) => { + body.client = {type: 'IPA', id: 'BVG'} + body.ext = 'VBB.2' + body.ver = '1.11' + body.auth = {type: 'AID', aid: 'hafas-vbb-apps'} + + return body +} + +const parseLine = (profile, l) => { + const res = _parseLine(profile, l) + + res.mode = res.product = null + if ('class' in res) { + const data = modes.bitmasks[parseInt(res.class)] + if (data) { + res.mode = data.mode + res.product = data.product + } + } + + return res +} + +const parseLocation = (profile, l) => { + const res = _parseLocation(profile, l) + res.name = shorten(res.name) + return res +} + +const isIBNR = /^\d{9,}$/ +const formatStation = (id) => { + // todo: convert short to long IDs + if (!isIBNR.test(id)) throw new Error('station ID must be an IBNR.') + return _formatStation(id) +} + +const defaultProducts = { + suburban: true, + subway: true, + tram: true, + bus: true, + ferry: true, + express: true, + regional: true +} +const formatProducts = (products) => { + products = Object.assign(Object.create(null), defaultProducts, products) + return { + type: 'PROD', + mode: 'INC', + value: modes.stringifyBitmask(products) + '' + } +} + +const vbbProfile = { + timezone: 'Europe/Berlin', + endpoint: 'https://fahrinfo.vbb.de/bin/mgate.exe', + transformReqBody, + + parseLocation, + parseLine, + parseProducts: modes.parseBitmask, + + formatProducts +} + +module.exports = vbbProfile diff --git a/p/vbb/modes.js b/p/vbb/modes.js new file mode 100644 index 00000000..fbbb49f9 --- /dev/null +++ b/p/vbb/modes.js @@ -0,0 +1,146 @@ +'use strict' + +// todo: remove useless keys +const m = { + suburban: { + category: 0, + bitmask: 1, + name: 'S-Bahn', + mode: 'train', + short: 'S', + type: 'suburban', + color: '#008c4f', + unicode: '🚈', + ansi: ['green'] // `chalk` color code + }, + + subway: { + category: 1, + bitmask: 2, + name: 'U-Bahn', + mode: 'train', + short: 'U', + type: 'subway', + color: '#0067ac', + unicode: 'πŸš‡', + ansi: ['blue'] // `chalk` color code + }, + + tram: { + category: 2, + bitmask: 4, + name: 'Tram', + mode: 'train', + short: 'T', + type: 'tram', + color: '#e3001b', + unicode: 'πŸš‹', + ansi: ['red'] // `chalk` color code + }, + + bus: { + category: 3, + bitmask: 8, + name: 'Bus', + mode: 'train', + short: 'B', + type: 'bus', + color: '#922A7D', + unicode: '🚌', + ansi: ['dim', 'magenta'] // `chalk` color codes + }, + + ferry: { + category: 4, + bitmask: 16, + name: 'FΓ€hre', + mode: 'train', + short: 'F', + type: 'ferry', + color: '#099bd6', + unicode: '🚒', + ansi: ['cyan'] // `chalk` color code + }, + + express: { + category: 5, + bitmask: 32, + name: 'IC/ICE', + mode: 'train', + short: 'E', + type: 'express', + color: '#f4e613', + unicode: 'πŸš„', + ansi: ['yellow'] // `chalk` color code + }, + + regional: { + category: 6, + bitmask: 64, + name: 'RB/RE', + mode: 'train', + short: 'R', + type: 'regional', + color: '#D9222A', + unicode: 'πŸš†', + ansi: ['red'] // `chalk` color code + }, + + unknown: { + category: null, + bitmask: 0, + name: 'unknown', + mode: null, + short: '?', + type: 'unknown', + color: '#555555', + unicode: '?', + ansi: ['gray'] // `chalk` color code + } +} + +m.bitmasks = [] +m.bitmasks[1] = m.suburban +m.bitmasks[2] = m.subway +m.bitmasks[4] = m.tram +m.bitmasks[8] = m.bus +m.bitmasks[16] = m.ferry +m.bitmasks[32] = m.express +m.bitmasks[64] = m.regional + + +m.categories = [ + m.suburban, + m.subway, + m.tram, + m.bus, + m.ferry, + m.express, + m.regional, + m.unknown +] + +// m.parseCategory = (category) => { +// return m.categories[parseInt(category)] || m.unknown +// } + +// todo: move up +m.stringifyBitmask = (types) => { + let bitmask = 0 + for (let type in types) { + if (types[type] === true) bitmask += m[type].bitmask + } + return bitmask +} + +// todo: move up +m.parseBitmask = (bitmask) => { + let types = {}, i = 1 + do { + types[m.bitmasks[i].type] = !!(bitmask & i) + i *= 2 + } while (m.bitmasks[i] && m.bitmasks[i].type) + return types +} + +module.exports = m diff --git a/package.json b/package.json index f699a671..c94c295a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "moment-timezone": "^0.5.13", "pinkie-promise": "^2.0.1", "query-string": "^5.0.0", - "slugg": "^1.2.0" + "slugg": "^1.2.0", + "vbb-short-station-name": "^0.4.0" }, "devDependencies": { "db-stations": "^1.25.0", diff --git a/readme.md b/readme.md index de67f1d4..9807b0d3 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,7 @@ **A client for HAFAS public transport APIs**. Sort of like [public-transport-enable](https://github.com/schildbach/public-transport-enabler), but with a smaller scope. It also contains customisations for the following transport networks: - [Deutsche Bahn](https://en.wikipedia.org/wiki/Deutsche_Bahn) – [src](p/db/index.js) +- [Berlin public transport](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) – [src](p/vbb/index.js) [![npm version](https://img.shields.io/npm/v/hafas-client.svg)](https://www.npmjs.com/package/hafas-client) [![build status](https://img.shields.io/travis/derhuerst/hafas-client.svg)](https://travis-ci.org/derhuerst/hafas-client) diff --git a/test/vbb.js b/test/vbb.js index 976b7d0b..f5f1c9e8 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -271,7 +271,7 @@ test('locations', async (t) => { -test('radar', async (t) => { +test.skip('radar', async (t) => { const vehicles = await client.radar(52.52411, 13.41002, 52.51942, 13.41709, { duration: 5 * 60, when })