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.
- package/package.json +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.
|
|
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
|
-
"
|
|
49
|
+
"zenbus": "^1.0.0"
|
|
50
50
|
}
|
|
51
51
|
}
|
package/zenbus-next-bus.js
CHANGED
|
@@ -1,98 +1,4 @@
|
|
|
1
|
-
var
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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; }
|