Verified TransJakarta realtime stack: anonymous MQTT bus feed (wss://mqtt.tj.co.id:8084/mqtt), guest REST API (tijeapi.transjakarta.co.id), official GTFS. Cross-capture verification vs third-party tracker, connection recipes, snapshot tool.
63 lines
2.4 KiB
JavaScript
63 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// tj-snapshot.mjs — capture a live snapshot of the TransJakarta bus feed (MQTT over WSS).
|
|
//
|
|
// The broker (wss://mqtt.tj.co.id:8084/mqtt) is anonymous. This tool connects,
|
|
// subscribes, and records the last known state of every bus it sees.
|
|
//
|
|
// Setup: npm install mqtt (in this directory, or a parent dir)
|
|
// Usage: node tj-snapshot.mjs [--topic /mobile_armada/#] [--seconds 60] [--out snapshot.json]
|
|
//
|
|
// Output: summary on stdout; with --out, a JSON file:
|
|
// { capturedAt, durationSec, topic, messages, busCount, buses: { <bus_body_no>: {...} } }
|
|
//
|
|
// Notes: undocumented infrastructure — be gentle; one connection is enough.
|
|
// Whole fleet: '/mobile_armada/#'. One area: '/mobile_armada/0018-0022/#'.
|
|
|
|
import mqtt from 'mqtt';
|
|
import fs from 'fs';
|
|
|
|
const args = process.argv.slice(2);
|
|
const getArg = (name, fallback) => {
|
|
const i = args.indexOf(name);
|
|
return i >= 0 && args[i + 1] ? args[i + 1] : fallback;
|
|
};
|
|
|
|
const topic = getArg('--topic', '/mobile_armada/#');
|
|
const seconds = parseInt(getArg('--seconds', '60'), 10) || 60;
|
|
const outFile = getArg('--out', '');
|
|
|
|
const out = { capturedAt: new Date().toISOString(), durationSec: seconds, topic, messages: 0, buses: {} };
|
|
|
|
const client = mqtt.connect('wss://mqtt.tj.co.id:8084/mqtt', {
|
|
protocolVersion: 4, keepalive: 30, clean: true, connectTimeout: 15000, reconnectPeriod: 0
|
|
});
|
|
|
|
client.on('connect', () => client.subscribe(topic, { qos: 0 }));
|
|
client.on('error', (e) => console.error('MQTT error:', e.message));
|
|
|
|
client.on('message', (t, payload) => {
|
|
out.messages++;
|
|
try {
|
|
const d = JSON.parse(payload.toString());
|
|
if (!d.bus_body_no) return;
|
|
out.buses[d.bus_body_no] = {
|
|
lat: d.latitude, lng: d.longitude, bearing: d.bearing,
|
|
route: d.route_code, trip: d.trip_id, headsign: d.trip_headsign,
|
|
type: d.type, direction: d.direction,
|
|
currStop: d.curr_stops, nextStop: d.next_stops,
|
|
etaNextStopMin: d.estimated_time_next_stops,
|
|
lastUpdate: d.timestamp, mqttTopic: t
|
|
};
|
|
} catch { /* skip malformed */ }
|
|
});
|
|
|
|
setTimeout(() => {
|
|
out.busCount = Object.keys(out.buses).length;
|
|
console.log(`Captured ${out.messages} messages, ${out.busCount} unique buses in ${seconds}s`);
|
|
if (outFile) {
|
|
fs.writeFileSync(outFile, JSON.stringify(out));
|
|
console.log(`Snapshot written to ${outFile}`);
|
|
}
|
|
client.end(true);
|
|
process.exit(0);
|
|
}, seconds * 1000);
|