node-red-zenbus 1.0.12 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/zenbus-next-bus.js +7 -109
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-zenbus",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Real-time next bus ETA from Zenbus networks via direct API (protobuf)",
5
5
  "type": "commonjs",
6
6
  "main": "zenbus-next-bus.js",
@@ -46,6 +46,6 @@
46
46
  "node": ">=18"
47
47
  },
48
48
  "dependencies": {
49
- "protobufjs": "^8.0.1"
49
+ "zenbus": "^1.0.0"
50
50
  }
51
51
  }
@@ -1,98 +1,4 @@
1
- var protobuf = require('protobufjs');
2
-
3
- var BASE = 'https://zenbus.net';
4
-
5
- function fetchProto(url, Type) {
6
- return fetch(url)
7
- .then(function (r) { return r.arrayBuffer(); })
8
- .then(function (ab) { return Type.decode(Buffer.from(ab)); });
9
- }
10
-
11
- function secsToHHMM(secs) {
12
- var h = Math.floor(secs / 3600);
13
- var m = Math.floor((secs % 3600) / 60);
14
- return h + 'h' + String(m).padStart(2, '0');
15
- }
16
-
17
- function createClient(opts) {
18
- var alias = opts.alias, itinerary = opts.itinerary, stop = opts.stop;
19
- return fetch(BASE + '/poll/cdn/zenbus.proto')
20
- .then(function (r) { return r.text(); })
21
- .then(function (protoText) {
22
- var root = protobuf.parse(protoText).root;
23
- var StaticMessage = root.lookupType('zenbus_realtime.StaticMessage');
24
- var LiveMessage = root.lookupType('zenbus_realtime.LiveMessage');
25
-
26
- return fetchProto(BASE + '/publicapp/static-data?alias=' + alias, StaticMessage)
27
- .then(function (staticData) {
28
- var shape = (staticData.shape || []).find(function (s) { return s.itineraryId && s.itineraryId.toString() === itinerary; });
29
- var stopAnchor = shape && (shape.anchor || []).find(function (a) { return a.stopId && a.stopId.toString() === stop; });
30
- var stopIndex = stopAnchor ? (stopAnchor.stopIndexInItinerary != null ? stopAnchor.stopIndexInItinerary : -1) : -1;
31
- var stopDistanceM = stopAnchor ? (stopAnchor.distanceTravelled || 0) : 0;
32
- var stopEntry = (staticData.stop || []).find(function (s) { return s.stopId && s.stopId.toString() === stop; });
33
- var stopName = stopEntry ? stopEntry.name : 'Unknown';
34
- var itin = (staticData.itinerary || []).find(function (i) { return i.itineraryId && i.itineraryId.toString() === itinerary; });
35
- var lineEntry = (staticData.line || []).find(function (l) { return itin && l.lineId && l.lineId.toString() === (itin.lineId && itin.lineId.toString()); });
36
- var lineCode = lineEntry ? lineEntry.code : 'Unknown';
37
- var pollUrl = BASE + '/publicapp/poll?alias=' + alias + '&itinerary=' + itinerary;
38
-
39
- return {
40
- stopName: stopName,
41
- lineCode: lineCode,
42
- poll: function () {
43
- return fetchProto(pollUrl, LiveMessage).then(function (liveData) {
44
- var now = new Date();
45
- var midnightUtcSecs = (liveData.timetable && liveData.timetable[0] && liveData.timetable[0].midnight && liveData.timetable[0].midnight.toNumber)
46
- ? liveData.timetable[0].midnight.toNumber()
47
- : Math.floor(new Date(now).setHours(0, 0, 0, 0) / 1000);
48
- var nowSecs = Math.floor(now.getTime() / 1000) - midnightUtcSecs;
49
-
50
- var allColumns = (liveData.tripColumn || []).slice();
51
- (liveData.timetable || []).forEach(function (tt) {
52
- (tt.column || []).forEach(function (col) { allColumns.push(col); });
53
- });
54
-
55
- var candidates = [];
56
- allColumns.forEach(function (tc) {
57
- var est = (tc.estimactual || []).find(function (s) { return s.stopIndexInItinerary === stopIndex; });
58
- var etaSecs = est ? (est.arrival || est.departure || 0) : 0;
59
- if (!etaSecs || etaSecs <= nowSecs) return;
60
-
61
- var vehicleDist = tc.distanceTravelled || 0;
62
- var hasStarted = tc.previousIndexInItinerary >= 0 && tc.pos && tc.pos.length > 0;
63
- if (hasStarted && vehicleDist > stopDistanceM) return;
64
-
65
- var remainingDist = hasStarted ? Math.max(0, stopDistanceM - vehicleDist) : stopDistanceM;
66
- var aimed = (tc.aimed || []).find(function (s) { return s.stopIndexInItinerary === stopIndex; });
67
- var schedSecs = aimed ? (aimed.arrival || aimed.departure || aimed.arriparture || 0) : 0;
68
-
69
- candidates.push({
70
- etaMinutes: Math.round((etaSecs - nowSecs) / 60),
71
- distanceM: Math.round(remainingDist),
72
- estimatedArrival: secsToHHMM(etaSecs),
73
- scheduledTime: schedSecs ? secsToHHMM(schedSecs) : null,
74
- isLive: hasStarted
75
- });
76
- });
77
-
78
- candidates.sort(function (a, b) { return a.etaMinutes - b.etaMinutes; });
79
- var best = candidates[0] || null;
80
- var second = candidates.find(function (c, i) { return i > 0 && c.isLive; }) || null;
81
-
82
- return {
83
- stop: stopName,
84
- line: lineCode,
85
- timestamp: now.toISOString(),
86
- now: secsToHHMM(nowSecs),
87
- next: best,
88
- secondBus: second
89
- };
90
- });
91
- }
92
- };
93
- });
94
- });
95
- }
1
+ var zenbusReady = import('zenbus/core');
96
2
 
97
3
  module.exports = function (RED) {
98
4
 
@@ -106,10 +12,12 @@ module.exports = function (RED) {
106
12
 
107
13
  node.status({ fill: 'yellow', shape: 'ring', text: 'initializing...' });
108
14
 
109
- createClient({
110
- alias: config.alias,
111
- itinerary: config.itinerary,
112
- stop: config.stop
15
+ zenbusReady.then(function (mod) {
16
+ return mod.createClient({
17
+ alias: config.alias,
18
+ itinerary: config.itinerary,
19
+ stop: config.stop
20
+ });
113
21
  }).then(function (c) {
114
22
  if (closing) return;
115
23
  client = c;
@@ -147,16 +55,6 @@ module.exports = function (RED) {
147
55
  });
148
56
  }
149
57
 
150
- node.on('input', function (msg, send, done) {
151
- send = send || function () { node.send.apply(node, arguments); };
152
- done = done || function (err) { if (err) node.error(err, msg); };
153
- if (!client) { done(new Error('Client not initialized')); return; }
154
- client.poll().then(function (data) {
155
- send({ payload: data });
156
- done();
157
- }).catch(function (e) { done(e); });
158
- });
159
-
160
58
  node.on('close', function (removed, done) {
161
59
  closing = true;
162
60
  if (timer) { clearTimeout(timer); timer = null; }