iobroker.jetframe 1.0.0
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/LICENSE +21 -0
- package/README.md +357 -0
- package/admin/SF-Pro.ttf +0 -0
- package/admin/admin.d.ts +65 -0
- package/admin/frame.html +982 -0
- package/admin/frame.html.bak-aircraft-card-real-row-20260518-1608 +1236 -0
- package/admin/frame.html.bak-aircraft-card-structure-20260518-1517 +1236 -0
- package/admin/frame.html.bak-aircraft-logo-id-fix-20260518-1639 +1239 -0
- package/admin/frame.html.bak-shortcut-test +1236 -0
- package/admin/frame.html.bak-tablet-class-20260518-1729 +1239 -0
- package/admin/heatmap.html +216 -0
- package/admin/index.html +268 -0
- package/admin/index_m.html +1749 -0
- package/admin/jetframe.css +1260 -0
- package/admin/jetframe.css.bak-airbus-landscape-fix +4630 -0
- package/admin/jetframe.css.bak-aircraft-card-clean-equal-20260518-1438 +4899 -0
- package/admin/jetframe.css.bak-aircraft-card-real-row-20260518-1608 +4814 -0
- package/admin/jetframe.css.bak-aircraft-card-row-left-20260518-1525 +4604 -0
- package/admin/jetframe.css.bak-aircraft-card-slim-equal-20260518-1446 +4647 -0
- package/admin/jetframe.css.bak-aircraft-card-structure-20260518-1517 +4646 -0
- package/admin/jetframe.css.bak-aircraft-inline-final-20260518-1527 +4654 -0
- package/admin/jetframe.css.bak-aircraft-row-compact-fix-20260518-1639 +4763 -0
- package/admin/jetframe.css.bak-before-aircrafttype-purge +4818 -0
- package/admin/jetframe.css.bak-before-cleanup +4670 -0
- package/admin/jetframe.css.bak-before-remove-tablet-only-20260518-1711 +4896 -0
- package/admin/jetframe.css.bak-before-tablet-layout-rework-20260518-1650 +4914 -0
- package/admin/jetframe.css.bak-clean-duplicate-fonts-20260518-1340 +4975 -0
- package/admin/jetframe.css.bak-clean-old-index-fix-20260518-1937 +5167 -0
- package/admin/jetframe.css.bak-hardleft-airbus +4751 -0
- package/admin/jetframe.css.bak-index-iphone-landscape-20260518-1931 +5030 -0
- package/admin/jetframe.css.bak-index-landscape-final-20260518-1941 +5167 -0
- package/admin/jetframe.css.bak-index-landscape-real-20260518-1936 +5186 -0
- package/admin/jetframe.css.bak-landscape-compact-jumbo-bold-20260518-1343 +4802 -0
- package/admin/jetframe.css.bak-logo-align-final +4551 -0
- package/admin/jetframe.css.bak-logo-final2 +4551 -0
- package/admin/jetframe.css.bak-narrowbody-font-fix +4992 -0
- package/admin/jetframe.css.bak-nuke-airbus-align +4790 -0
- package/admin/jetframe.css.bak-pill-balance-20260518-1603 +4773 -0
- package/admin/jetframe.css.bak-pill-balance-fix +4910 -0
- package/admin/jetframe.css.bak-radar-fix-fonts +4710 -0
- package/admin/jetframe.css.bak-shortcut-test +4899 -0
- package/admin/jetframe.css.bak-smaller-aircraft-card-fonts-20260518-1345 +4897 -0
- package/admin/jetframe.css.bak-tablet-fix-real-20260518-1748 +4945 -0
- package/admin/jetframe.css.bak-tablet-fullscreen-fix-20260518-1804 +4972 -0
- package/admin/jetframe.css.bak-tablet-landscape-layout-20260518-1645 +4802 -0
- package/admin/jetframe.css.bak-tablet-layout-final-20260518-1839 +4802 -0
- package/admin/jetframe.css.bak-tablet-layout-v3-20260518-1729 +4802 -0
- package/admin/jetframe.css.bak-tablet-layout-v4-20260518-1801 +4957 -0
- package/admin/jetframe.css.bak-tablet-layout-v5-20260518-1843 +4970 -0
- package/admin/jetframe.css.bak-tablet-layout-v6-20260518-1848 +4958 -0
- package/admin/jetframe.css.bak-tablet-layout-v7-20260518-1909 +4985 -0
- package/admin/jetframe.css.bak-tablet-only-landscape-v2-20260518-1707 +4802 -0
- package/admin/jetframe.css.bak-tablet-pages-final-20260519-1857 +5188 -0
- package/admin/jetframe.css.bak-tablet-pages-final-20260519-1859 +5347 -0
- package/admin/jetframe.css.bak-tablet-pages-v2-20260519-190807 +5349 -0
- package/admin/jetframe.css.bak-typography-align-final +4818 -0
- package/admin/jetframe.png +0 -0
- package/admin/manifest.webmanifest +15 -0
- package/admin/src/app.tsx +58 -0
- package/admin/src/components/settings.tsx +97 -0
- package/admin/src/i18n/de.json +11 -0
- package/admin/src/i18n/en.json +11 -0
- package/admin/src/i18n/es.json +11 -0
- package/admin/src/i18n/fr.json +11 -0
- package/admin/src/i18n/i18n.d.ts +28 -0
- package/admin/src/i18n/it.json +11 -0
- package/admin/src/i18n/nl.json +11 -0
- package/admin/src/i18n/pl.json +11 -0
- package/admin/src/i18n/pt.json +11 -0
- package/admin/src/i18n/ru.json +11 -0
- package/admin/src/i18n/uk.json +11 -0
- package/admin/src/i18n/zh-cn.json +11 -0
- package/admin/src/index.tsx +25 -0
- package/admin/stats.html +228 -0
- package/admin/style.css +32 -0
- package/admin/tsconfig.json +11 -0
- package/admin/words.js +46 -0
- package/build/lib/adsb.js +218 -0
- package/build/lib/adsb.js.map +7 -0
- package/build/lib/airportNamesDe.js +131 -0
- package/build/lib/airportNamesDe.js.map +7 -0
- package/build/lib/airports.js +281 -0
- package/build/lib/airports.js.map +7 -0
- package/build/lib/classify.js +339 -0
- package/build/lib/classify.js.map +7 -0
- package/build/lib/config.js +103 -0
- package/build/lib/config.js.map +7 -0
- package/build/lib/flightInfo.js +1409 -0
- package/build/lib/flightInfo.js.map +7 -0
- package/build/lib/geo.js +84 -0
- package/build/lib/geo.js.map +7 -0
- package/build/lib/images.js +422 -0
- package/build/lib/images.js.map +7 -0
- package/build/lib/specialLiveries.js +342 -0
- package/build/lib/specialLiveries.js.map +7 -0
- package/build/lib/states.js +971 -0
- package/build/lib/states.js.map +7 -0
- package/build/lib/staticFiles.js +73 -0
- package/build/lib/staticFiles.js.map +7 -0
- package/build/lib/types.js +17 -0
- package/build/lib/types.js.map +7 -0
- package/build/lib/visConfig.js +52 -0
- package/build/lib/visConfig.js.map +7 -0
- package/build/main.js +1454 -0
- package/build/main.js.map +7 -0
- package/io-package.json +169 -0
- package/package.json +82 -0
package/build/main.js
ADDED
|
@@ -0,0 +1,1454 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var utils = __toESM(require("@iobroker/adapter-core"));
|
|
25
|
+
var https = __toESM(require("https"));
|
|
26
|
+
var http = __toESM(require("http"));
|
|
27
|
+
var import_staticFiles = require("./lib/staticFiles");
|
|
28
|
+
var import_airports = require("./lib/airports");
|
|
29
|
+
var import_specialLiveries = require("./lib/specialLiveries");
|
|
30
|
+
var import_config = require("./lib/config");
|
|
31
|
+
var import_adsb = require("./lib/adsb");
|
|
32
|
+
var import_classify = require("./lib/classify");
|
|
33
|
+
var import_states = require("./lib/states");
|
|
34
|
+
var import_images = require("./lib/images");
|
|
35
|
+
var import_visConfig = require("./lib/visConfig");
|
|
36
|
+
var import_flightInfo = require("./lib/flightInfo");
|
|
37
|
+
class Jetframe extends utils.Adapter {
|
|
38
|
+
timer = null;
|
|
39
|
+
statisticsTimer = null;
|
|
40
|
+
liveTarget = null;
|
|
41
|
+
liveInfo = null;
|
|
42
|
+
liveStarted = 0;
|
|
43
|
+
lastStartKey = "";
|
|
44
|
+
lastStartTs = 0;
|
|
45
|
+
lastImageSaveKey = "";
|
|
46
|
+
lastIdleRunwayText = "";
|
|
47
|
+
constructor(options = {}) {
|
|
48
|
+
super({
|
|
49
|
+
...options,
|
|
50
|
+
name: "jetframe"
|
|
51
|
+
});
|
|
52
|
+
this.on("ready", this.onReady.bind(this));
|
|
53
|
+
this.on("unload", this.onUnload.bind(this));
|
|
54
|
+
this.on("stateChange", this.onStateChange.bind(this));
|
|
55
|
+
}
|
|
56
|
+
async onReady() {
|
|
57
|
+
this.log.info("JetFrame Adapter gestartet");
|
|
58
|
+
await (0, import_staticFiles.copyStaticFiles)(this);
|
|
59
|
+
try {
|
|
60
|
+
const config = (0, import_config.readConfig)(this);
|
|
61
|
+
await (0, import_states.ensureStates)(this, config);
|
|
62
|
+
await this.ensureStatisticsStates(config.dpRoot);
|
|
63
|
+
await this.resetTodayStatisticsIfNeeded(`${config.dpRoot}.statistics`);
|
|
64
|
+
this.scheduleStatisticsRotation(config.dpRoot);
|
|
65
|
+
await this.ensureProbableRunwayStates(config.dpRoot);
|
|
66
|
+
await this.ensureIdleRunwayState(config.dpRoot);
|
|
67
|
+
this.subscribeStates("clearImageCache");
|
|
68
|
+
this.log.debug("[JetFrame] States OK");
|
|
69
|
+
await (0, import_images.ensureImageDirs)(this, this.logDebug.bind(this), this.logWarn.bind(this));
|
|
70
|
+
await (0, import_visConfig.writeVisConfig)(this, this.config, this.logDebug.bind(this), this.logWarn.bind(this));
|
|
71
|
+
this.log.debug("[JetFrame] Images OK");
|
|
72
|
+
(0, import_airports.updateAirportJson)(this, this.logDebug.bind(this), this.logWarn.bind(this)).catch((e) => {
|
|
73
|
+
this.logWarn(`Airport DB Update Fehler: ${this.errorText(e)}`);
|
|
74
|
+
});
|
|
75
|
+
(0, import_specialLiveries.updateSpecialLiveries)(this, this.logDebug.bind(this), this.logWarn.bind(this)).catch((e) => {
|
|
76
|
+
this.logWarn(`Special-Liveries DB Update Fehler: ${this.errorText(e)}`);
|
|
77
|
+
});
|
|
78
|
+
this.log.debug("[JetFrame] Starte Loop");
|
|
79
|
+
this.loop().catch((e) => {
|
|
80
|
+
this.logError(`Loop Start Fehler: ${this.errorText(e)}`);
|
|
81
|
+
});
|
|
82
|
+
} catch (e) {
|
|
83
|
+
this.logError(`onReady Fehler: ${this.errorText(e)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async onStateChange(id, state) {
|
|
87
|
+
if (!state || state.ack || state.val !== true) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const config = (0, import_config.readConfig)(this);
|
|
91
|
+
if (id !== `${config.dpRoot}.clearImageCache`) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
await (0, import_images.clearImageCache)(this, this.logDebug.bind(this), this.logWarn.bind(this));
|
|
96
|
+
await this.setForeignStateAsync(`${config.dpRoot}.clearImageCache`, false, true);
|
|
97
|
+
this.log.info("JetFrame Bild-/Logo-Cache wurde geleert");
|
|
98
|
+
} catch (e) {
|
|
99
|
+
this.logWarn(`Cache leeren fehlgeschlagen: ${this.errorText(e)}`);
|
|
100
|
+
await this.setForeignStateAsync(`${config.dpRoot}.clearImageCache`, false, true);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async ensureIdleRunwayState(dpRoot) {
|
|
104
|
+
try {
|
|
105
|
+
await this.setForeignObjectNotExistsAsync(`${dpRoot}.idleRunwayText`, {
|
|
106
|
+
type: "state",
|
|
107
|
+
common: {
|
|
108
|
+
name: "Idle active runway text",
|
|
109
|
+
type: "string",
|
|
110
|
+
role: "text",
|
|
111
|
+
read: true,
|
|
112
|
+
write: false
|
|
113
|
+
},
|
|
114
|
+
native: {}
|
|
115
|
+
});
|
|
116
|
+
} catch (e) {
|
|
117
|
+
this.logWarn(`idleRunwayText State konnte nicht erstellt werden: ${this.errorText(e)}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async updateIdleRunway(a, config) {
|
|
121
|
+
var _a;
|
|
122
|
+
const track = Number(a.trackDeg || a.track || 0);
|
|
123
|
+
if (!Number.isFinite(track) || !track) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
let runways = [];
|
|
127
|
+
try {
|
|
128
|
+
const st = await this.getForeignStateAsync(`${config.dpRoot}.airportjson`);
|
|
129
|
+
const airports = JSON.parse(String((st == null ? void 0 : st.val) || "[]"));
|
|
130
|
+
const airportIata = String(((_a = config.airport) == null ? void 0 : _a.iata) || "").trim().toUpperCase();
|
|
131
|
+
const airport = Array.isArray(airports) ? airports.find(
|
|
132
|
+
(x) => String(x.iata || x.IATA || "").trim().toUpperCase() === airportIata
|
|
133
|
+
) : null;
|
|
134
|
+
runways = Array.isArray(airport == null ? void 0 : airport.runways) ? airport.runways : [];
|
|
135
|
+
} catch {
|
|
136
|
+
runways = [];
|
|
137
|
+
}
|
|
138
|
+
let bestName = "";
|
|
139
|
+
let bestGroup = "";
|
|
140
|
+
let bestDiff = 999;
|
|
141
|
+
for (const rw of runways) {
|
|
142
|
+
const sides = [
|
|
143
|
+
{ name: rw.leIdent, heading: rw.leHeadingDeg },
|
|
144
|
+
{ name: rw.heIdent, heading: rw.heHeadingDeg }
|
|
145
|
+
];
|
|
146
|
+
for (const side of sides) {
|
|
147
|
+
const name = String(side.name || "").trim().toUpperCase();
|
|
148
|
+
const heading = Number(side.heading);
|
|
149
|
+
if (!name || !Number.isFinite(heading)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
let diff = Math.abs(track - heading);
|
|
153
|
+
if (diff > 180) {
|
|
154
|
+
diff = 360 - diff;
|
|
155
|
+
}
|
|
156
|
+
if (diff < bestDiff) {
|
|
157
|
+
bestDiff = diff;
|
|
158
|
+
bestName = name;
|
|
159
|
+
bestGroup = name.replace(/[LCR]$/i, "");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
let runway = "";
|
|
164
|
+
if (bestName && bestDiff <= 35) {
|
|
165
|
+
runway = `RWY ${bestGroup || bestName} aktiv`;
|
|
166
|
+
} else {
|
|
167
|
+
runway = `Aktive Richtung ${Math.round(track)}\xB0`;
|
|
168
|
+
}
|
|
169
|
+
const mode = a.mode === "LANDING" ? "Landungen" : a.mode === "TAKEOFF" ? "Starts" : "Traffic";
|
|
170
|
+
const text = `${runway} \xB7 ${mode}`;
|
|
171
|
+
if (text === this.lastIdleRunwayText) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.lastIdleRunwayText = text;
|
|
175
|
+
await this.setForeignStateAsync(`${config.dpRoot}.idleRunwayText`, text, true);
|
|
176
|
+
}
|
|
177
|
+
async ensureProbableRunwayStates(dpRoot) {
|
|
178
|
+
const bases = [`${dpRoot}.current`, `${dpRoot}.airport`, `${dpRoot}.overflight`];
|
|
179
|
+
for (const base of bases) {
|
|
180
|
+
await this.ensureSimpleState(`${base}.probableRunway`, "Probable runway", "string", "text");
|
|
181
|
+
await this.ensureSimpleState(`${base}.probableRunwayText`, "Probable runway text", "string", "text");
|
|
182
|
+
await this.ensureSimpleState(`${base}.probableRunwayHeading`, "Probable runway heading", "number", "value");
|
|
183
|
+
await this.ensureSimpleState(
|
|
184
|
+
`${base}.probableRunwayDiffDeg`,
|
|
185
|
+
"Probable runway difference degrees",
|
|
186
|
+
"number",
|
|
187
|
+
"value"
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async ensureSimpleState(id, name, type, role) {
|
|
192
|
+
try {
|
|
193
|
+
await this.setForeignObjectNotExistsAsync(id, {
|
|
194
|
+
type: "state",
|
|
195
|
+
common: {
|
|
196
|
+
name,
|
|
197
|
+
type,
|
|
198
|
+
role,
|
|
199
|
+
read: true,
|
|
200
|
+
write: false
|
|
201
|
+
},
|
|
202
|
+
native: {}
|
|
203
|
+
});
|
|
204
|
+
const st = await this.getForeignStateAsync(id);
|
|
205
|
+
if (!st) {
|
|
206
|
+
await this.setForeignStateAsync(id, type === "number" ? 0 : type === "boolean" ? false : "", true);
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
this.logWarn(`State konnte nicht erstellt/initialisiert werden: ${id} | ${this.errorText(e)}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async applyProbableRunway(a, config) {
|
|
213
|
+
var _a;
|
|
214
|
+
const track = Number(a.trackDeg || a.track || 0);
|
|
215
|
+
if (!Number.isFinite(track) || !track) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
let runways = [];
|
|
219
|
+
try {
|
|
220
|
+
const st = await this.getForeignStateAsync(`${config.dpRoot}.airportjson`);
|
|
221
|
+
const airports = JSON.parse(String((st == null ? void 0 : st.val) || "[]"));
|
|
222
|
+
const airportIata = String(((_a = config.airport) == null ? void 0 : _a.iata) || "").trim().toUpperCase();
|
|
223
|
+
const airport = Array.isArray(airports) ? airports.find(
|
|
224
|
+
(x) => String(x.iata || x.IATA || "").trim().toUpperCase() === airportIata
|
|
225
|
+
) : null;
|
|
226
|
+
runways = Array.isArray(airport == null ? void 0 : airport.runways) ? airport.runways : [];
|
|
227
|
+
} catch {
|
|
228
|
+
runways = [];
|
|
229
|
+
}
|
|
230
|
+
let bestName = "";
|
|
231
|
+
let bestGroup = "";
|
|
232
|
+
let bestHeading = 0;
|
|
233
|
+
let bestDiff = 999;
|
|
234
|
+
for (const rw of runways) {
|
|
235
|
+
const sides = [
|
|
236
|
+
{ name: rw.leIdent, heading: rw.leHeadingDeg },
|
|
237
|
+
{ name: rw.heIdent, heading: rw.heHeadingDeg }
|
|
238
|
+
];
|
|
239
|
+
for (const side of sides) {
|
|
240
|
+
const name = String(side.name || "").trim().toUpperCase();
|
|
241
|
+
const heading = Number(side.heading);
|
|
242
|
+
if (!name || !Number.isFinite(heading)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
let diff = Math.abs(track - heading);
|
|
246
|
+
if (diff > 180) {
|
|
247
|
+
diff = 360 - diff;
|
|
248
|
+
}
|
|
249
|
+
if (diff < bestDiff) {
|
|
250
|
+
bestDiff = diff;
|
|
251
|
+
bestName = name;
|
|
252
|
+
bestGroup = name.replace(/[LCR]$/i, "");
|
|
253
|
+
bestHeading = heading;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!bestName || bestDiff > 35) {
|
|
258
|
+
a.probableRunway = "";
|
|
259
|
+
a.probableRunwayText = "";
|
|
260
|
+
a.probableRunwayHeading = 0;
|
|
261
|
+
a.probableRunwayDiffDeg = 0;
|
|
262
|
+
a.runwayConfidence = 0;
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const modeIcon = a.mode === "LANDING" ? "\u{1F6EC}" : a.mode === "TAKEOFF" ? "\u{1F6EB}" : "\u{1F4E1}";
|
|
266
|
+
const modeText = a.mode === "LANDING" ? "Landung" : a.mode === "TAKEOFF" ? "Start" : "Traffic";
|
|
267
|
+
const confidence = Math.max(0, Math.min(100, Math.round(100 - bestDiff / 35 * 100)));
|
|
268
|
+
a.probableRunway = bestGroup || bestName;
|
|
269
|
+
a.probableRunwayHeading = Math.round(bestHeading);
|
|
270
|
+
a.probableRunwayDiffDeg = Math.round(bestDiff);
|
|
271
|
+
a.runwayConfidence = confidence;
|
|
272
|
+
a.probableRunwayText = `${modeIcon} vermutlich RWY ${bestGroup || bestName} \xB7 ${modeText}`;
|
|
273
|
+
}
|
|
274
|
+
async ensureStatisticsStates(dpRoot) {
|
|
275
|
+
const base = `${dpRoot}.statistics`;
|
|
276
|
+
await this.ensureSimpleState(`${base}.totalFlightsSeen`, "Total flights seen", "number", "value");
|
|
277
|
+
await this.ensureSimpleState(`${base}.landings`, "Landings", "number", "value");
|
|
278
|
+
await this.ensureSimpleState(`${base}.departures`, "Departures", "number", "value");
|
|
279
|
+
await this.ensureSimpleState(`${base}.overflights`, "Overflights", "number", "value");
|
|
280
|
+
await this.ensureSimpleState(`${base}.lastFlight`, "Last flight", "string", "text");
|
|
281
|
+
await this.ensureSimpleState(`${base}.lastAirline`, "Last airline", "string", "text");
|
|
282
|
+
await this.ensureSimpleState(`${base}.lastRoute`, "Last route", "string", "text");
|
|
283
|
+
await this.ensureSimpleState(`${base}.lastRegistration`, "Last registration", "string", "text");
|
|
284
|
+
await this.ensureSimpleState(`${base}.lastSeen`, "Last seen", "string", "text");
|
|
285
|
+
await this.ensureSimpleState(`${base}.airlineRanking`, "Airline ranking JSON", "string", "json");
|
|
286
|
+
await this.ensureSimpleState(`${base}.airlineRankingText`, "Airline ranking text", "string", "text");
|
|
287
|
+
await this.ensureSimpleState(`${base}.aircraftTypeRanking`, "Aircraft type ranking JSON", "string", "json");
|
|
288
|
+
await this.ensureSimpleState(`${base}.aircraftTypeRankingText`, "Aircraft type ranking text", "string", "text");
|
|
289
|
+
await this.ensureSimpleState(`${base}.registrationRanking`, "Registration ranking JSON", "string", "json");
|
|
290
|
+
await this.ensureSimpleState(`${base}.registrationRankingText`, "Registration ranking text", "string", "text");
|
|
291
|
+
await this.ensureSimpleState(`${base}.runwayUsageRanking`, "Runway usage ranking JSON", "string", "json");
|
|
292
|
+
await this.ensureSimpleState(`${base}.runwayUsageRankingText`, "Runway usage ranking text", "string", "text");
|
|
293
|
+
await this.ensureSimpleState(`${base}.airlineRunwayRanking`, "Airline runway ranking JSON", "string", "json");
|
|
294
|
+
await this.ensureSimpleState(
|
|
295
|
+
`${base}.airlineRunwayRankingText`,
|
|
296
|
+
"Airline runway ranking text",
|
|
297
|
+
"string",
|
|
298
|
+
"text"
|
|
299
|
+
);
|
|
300
|
+
await this.ensureSimpleState(`${base}.flightLogHistory`, "Flight log history JSON", "string", "json");
|
|
301
|
+
await this.ensureSimpleState(`${base}.flightLogHistoryText`, "Flight log history text", "string", "text");
|
|
302
|
+
await this.ensureSimpleState(`${base}.today.date`, "Today date", "string", "text");
|
|
303
|
+
await this.ensureSimpleState(`${base}.today.totalFlights`, "Today total flights", "number", "value");
|
|
304
|
+
await this.ensureSimpleState(`${base}.today.landings`, "Today landings", "number", "value");
|
|
305
|
+
await this.ensureSimpleState(`${base}.today.departures`, "Today departures", "number", "value");
|
|
306
|
+
await this.ensureSimpleState(`${base}.today.overflights`, "Today overflights", "number", "value");
|
|
307
|
+
await this.ensureSimpleState(`${base}.today.firstSeen`, "Today first seen", "string", "text");
|
|
308
|
+
await this.ensureSimpleState(`${base}.today.lastSeen`, "Today last seen", "string", "text");
|
|
309
|
+
await this.ensureSimpleState(`${base}.today.lastFlight`, "Today last flight", "string", "text");
|
|
310
|
+
await this.ensureSimpleState(`${base}.today.lastAirline`, "Today last airline", "string", "text");
|
|
311
|
+
await this.ensureSimpleState(`${base}.today.lastRoute`, "Today last route", "string", "text");
|
|
312
|
+
await this.ensureSimpleState(`${base}.today.lastRegistration`, "Today last registration", "string", "text");
|
|
313
|
+
await this.ensureSimpleState(`${base}.today.topAirline`, "Today top airline", "string", "text");
|
|
314
|
+
await this.ensureSimpleState(`${base}.today.topRoute`, "Today top route", "string", "text");
|
|
315
|
+
await this.ensureSimpleState(`${base}.today.airlineRanking`, "Today airline ranking JSON", "string", "json");
|
|
316
|
+
await this.ensureSimpleState(
|
|
317
|
+
`${base}.today.airlineRankingText`,
|
|
318
|
+
"Today airline ranking text",
|
|
319
|
+
"string",
|
|
320
|
+
"text"
|
|
321
|
+
);
|
|
322
|
+
await this.ensureSimpleState(`${base}.today.routeRanking`, "Today route ranking JSON", "string", "json");
|
|
323
|
+
await this.ensureSimpleState(`${base}.today.routeRankingText`, "Today route ranking text", "string", "text");
|
|
324
|
+
await this.ensureSimpleState(`${base}.today.hourly`, "Today hourly flights JSON", "string", "json");
|
|
325
|
+
await this.ensureSimpleState(`${base}.today.hourlyText`, "Today hourly flights text", "string", "text");
|
|
326
|
+
await this.ensureSimpleState(`${base}.today.bestSpotterHour`, "Today best spotter hour", "string", "text");
|
|
327
|
+
await this.ensureSimpleState(
|
|
328
|
+
`${base}.today.currentHourFlights`,
|
|
329
|
+
"Today current hour flights",
|
|
330
|
+
"number",
|
|
331
|
+
"value"
|
|
332
|
+
);
|
|
333
|
+
await this.ensureSimpleState(`${base}.today.rushHourNow`, "Rush hour now", "boolean", "indicator");
|
|
334
|
+
await this.ensureSimpleState(`${base}.today.rushHourText`, "Rush hour text", "string", "text");
|
|
335
|
+
await this.ensureSimpleState(`${base}.today.a380Count`, "Today A380 count", "number", "value");
|
|
336
|
+
await this.ensureSimpleState(`${base}.today.b747Count`, "Today B747 count", "number", "value");
|
|
337
|
+
await this.ensureSimpleState(
|
|
338
|
+
`${base}.today.heavyAircraftCount`,
|
|
339
|
+
"Today heavy aircraft count",
|
|
340
|
+
"number",
|
|
341
|
+
"value"
|
|
342
|
+
);
|
|
343
|
+
await this.ensureSimpleState(
|
|
344
|
+
`${base}.today.specialLiveryCount`,
|
|
345
|
+
"Today special livery count",
|
|
346
|
+
"number",
|
|
347
|
+
"value"
|
|
348
|
+
);
|
|
349
|
+
await this.ensureSimpleState(`${base}.yesterday.date`, "Yesterday date", "string", "text");
|
|
350
|
+
await this.ensureSimpleState(`${base}.yesterday.totalFlights`, "Yesterday total flights", "number", "value");
|
|
351
|
+
await this.ensureSimpleState(`${base}.yesterday.landings`, "Yesterday landings", "number", "value");
|
|
352
|
+
await this.ensureSimpleState(`${base}.yesterday.departures`, "Yesterday departures", "number", "value");
|
|
353
|
+
await this.ensureSimpleState(`${base}.yesterday.overflights`, "Yesterday overflights", "number", "value");
|
|
354
|
+
await this.ensureSimpleState(
|
|
355
|
+
`${base}.yesterday.bestSpotterHour`,
|
|
356
|
+
"Yesterday best spotter hour",
|
|
357
|
+
"string",
|
|
358
|
+
"text"
|
|
359
|
+
);
|
|
360
|
+
await this.ensureSimpleState(
|
|
361
|
+
`${base}.yesterday.bestHourFlights`,
|
|
362
|
+
"Yesterday best hour flights",
|
|
363
|
+
"number",
|
|
364
|
+
"value"
|
|
365
|
+
);
|
|
366
|
+
await this.ensureSimpleState(`${base}.yesterday.topAirline`, "Yesterday top airline", "string", "text");
|
|
367
|
+
await this.ensureSimpleState(`${base}.yesterday.topRoute`, "Yesterday top route", "string", "text");
|
|
368
|
+
await this.ensureSimpleState(`${base}.yesterday.a380Count`, "Yesterday A380 count", "number", "value");
|
|
369
|
+
await this.ensureSimpleState(`${base}.yesterday.b747Count`, "Yesterday B747 count", "number", "value");
|
|
370
|
+
await this.ensureSimpleState(
|
|
371
|
+
`${base}.yesterday.heavyAircraftCount`,
|
|
372
|
+
"Yesterday heavy aircraft count",
|
|
373
|
+
"number",
|
|
374
|
+
"value"
|
|
375
|
+
);
|
|
376
|
+
await this.ensureSimpleState(
|
|
377
|
+
`${base}.yesterday.specialLiveryCount`,
|
|
378
|
+
"Yesterday special livery count",
|
|
379
|
+
"number",
|
|
380
|
+
"value"
|
|
381
|
+
);
|
|
382
|
+
await this.ensureSimpleState(`${base}.yesterday.hourly`, "Yesterday hourly JSON", "string", "json");
|
|
383
|
+
await this.ensureSimpleState(
|
|
384
|
+
`${base}.yesterday.airlineRankingText`,
|
|
385
|
+
"Yesterday airline ranking text",
|
|
386
|
+
"string",
|
|
387
|
+
"text"
|
|
388
|
+
);
|
|
389
|
+
await this.ensureSimpleState(
|
|
390
|
+
`${base}.yesterday.routeRankingText`,
|
|
391
|
+
"Yesterday route ranking text",
|
|
392
|
+
"string",
|
|
393
|
+
"text"
|
|
394
|
+
);
|
|
395
|
+
await this.ensureSimpleState(`${base}.history.daily`, "Daily statistics history JSON", "string", "json");
|
|
396
|
+
await this.ensureSimpleState(`${base}.history.dailyText`, "Daily statistics history text", "string", "text");
|
|
397
|
+
await this.ensureSimpleState(`${base}.alltime.bestDayDate`, "Alltime best day date", "string", "text");
|
|
398
|
+
await this.ensureSimpleState(`${base}.alltime.bestDayFlights`, "Alltime best day flights", "number", "value");
|
|
399
|
+
await this.ensureSimpleState(`${base}.alltime.bestHour`, "Alltime best hour", "string", "text");
|
|
400
|
+
await this.ensureSimpleState(`${base}.alltime.bestHourFlights`, "Alltime best hour flights", "number", "value");
|
|
401
|
+
await this.ensureSimpleState(`${base}.alltime.bestAirline`, "Alltime best airline", "string", "text");
|
|
402
|
+
await this.ensureSimpleState(`${base}.alltime.bestRoute`, "Alltime best route", "string", "text");
|
|
403
|
+
await this.ensureSimpleState(
|
|
404
|
+
`${base}.alltime.bestAircraftType`,
|
|
405
|
+
"Alltime best aircraft type",
|
|
406
|
+
"string",
|
|
407
|
+
"text"
|
|
408
|
+
);
|
|
409
|
+
await this.ensureSimpleState(`${base}.alltime.a380Count`, "Alltime A380 count", "number", "value");
|
|
410
|
+
await this.ensureSimpleState(`${base}.alltime.b747Count`, "Alltime B747 count", "number", "value");
|
|
411
|
+
await this.ensureSimpleState(
|
|
412
|
+
`${base}.alltime.heavyAircraftCount`,
|
|
413
|
+
"Alltime heavy aircraft count",
|
|
414
|
+
"number",
|
|
415
|
+
"value"
|
|
416
|
+
);
|
|
417
|
+
await this.ensureSimpleState(
|
|
418
|
+
`${base}.alltime.specialLiveryCount`,
|
|
419
|
+
"Alltime special livery count",
|
|
420
|
+
"number",
|
|
421
|
+
"value"
|
|
422
|
+
);
|
|
423
|
+
await this.ensureSimpleState(
|
|
424
|
+
`${base}.alltime.bestSpecialDayDate`,
|
|
425
|
+
"Alltime best special day date",
|
|
426
|
+
"string",
|
|
427
|
+
"text"
|
|
428
|
+
);
|
|
429
|
+
await this.ensureSimpleState(
|
|
430
|
+
`${base}.alltime.bestSpecialDayCount`,
|
|
431
|
+
"Alltime best special day count",
|
|
432
|
+
"number",
|
|
433
|
+
"value"
|
|
434
|
+
);
|
|
435
|
+
await this.ensureSimpleState(`${base}.alltime.hourly`, "Alltime hourly JSON", "string", "json");
|
|
436
|
+
await this.ensureSimpleState(
|
|
437
|
+
`${base}.alltime.airlineRanking`,
|
|
438
|
+
"Alltime airline ranking JSON",
|
|
439
|
+
"string",
|
|
440
|
+
"json"
|
|
441
|
+
);
|
|
442
|
+
await this.ensureSimpleState(
|
|
443
|
+
`${base}.alltime.airlineRankingText`,
|
|
444
|
+
"Alltime airline ranking text",
|
|
445
|
+
"string",
|
|
446
|
+
"text"
|
|
447
|
+
);
|
|
448
|
+
await this.ensureSimpleState(`${base}.alltime.routeRanking`, "Alltime route ranking JSON", "string", "json");
|
|
449
|
+
await this.ensureSimpleState(
|
|
450
|
+
`${base}.alltime.routeRankingText`,
|
|
451
|
+
"Alltime route ranking text",
|
|
452
|
+
"string",
|
|
453
|
+
"text"
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
async readNumberState(id) {
|
|
457
|
+
try {
|
|
458
|
+
const st = await this.getForeignStateAsync(id);
|
|
459
|
+
const n = Number((st == null ? void 0 : st.val) || 0);
|
|
460
|
+
return Number.isFinite(n) ? n : 0;
|
|
461
|
+
} catch {
|
|
462
|
+
return 0;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async readJsonState(id, fallback) {
|
|
466
|
+
try {
|
|
467
|
+
const st = await this.getForeignStateAsync(id);
|
|
468
|
+
const raw = String((st == null ? void 0 : st.val) || "").trim();
|
|
469
|
+
if (!raw) {
|
|
470
|
+
return fallback;
|
|
471
|
+
}
|
|
472
|
+
return JSON.parse(raw);
|
|
473
|
+
} catch {
|
|
474
|
+
return fallback;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
topRankingText(entries, limit = 5) {
|
|
478
|
+
return entries.slice(0, limit).map(([name, count], index) => `${index + 1}. ${name} \xB7 ${count}`).join("\n");
|
|
479
|
+
}
|
|
480
|
+
sortedRanking(data, limit = 30) {
|
|
481
|
+
return Object.entries(data).filter(([name]) => !!this.clean(name)).sort((aEntry, bEntry) => bEntry[1] - aEntry[1] || aEntry[0].localeCompare(bEntry[0])).slice(0, limit);
|
|
482
|
+
}
|
|
483
|
+
async incrementRankingState(id, textId, key, limit = 30, textLimit = 5) {
|
|
484
|
+
const cleanKey = this.clean(key);
|
|
485
|
+
if (!cleanKey) {
|
|
486
|
+
return [];
|
|
487
|
+
}
|
|
488
|
+
const ranking = await this.readJsonState(id, {});
|
|
489
|
+
ranking[cleanKey] = (ranking[cleanKey] || 0) + 1;
|
|
490
|
+
const sorted = this.sortedRanking(ranking, limit);
|
|
491
|
+
await this.setForeignStateAsync(id, JSON.stringify(Object.fromEntries(sorted)), true);
|
|
492
|
+
await this.setForeignStateAsync(textId, this.topRankingText(sorted, textLimit), true);
|
|
493
|
+
return sorted;
|
|
494
|
+
}
|
|
495
|
+
async updateGlobalDetailedStatistics(base, a, info) {
|
|
496
|
+
const type = this.clean(a.aircraftTypeText) || this.clean(a.aircraftModel) || this.clean(a.aircraftType) || this.clean(a.type) || "Unbekannt";
|
|
497
|
+
await this.incrementRankingState(`${base}.aircraftTypeRanking`, `${base}.aircraftTypeRankingText`, type, 40, 8);
|
|
498
|
+
if (info.registration) {
|
|
499
|
+
await this.incrementRankingState(
|
|
500
|
+
`${base}.registrationRanking`,
|
|
501
|
+
`${base}.registrationRankingText`,
|
|
502
|
+
info.registration,
|
|
503
|
+
50,
|
|
504
|
+
8
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
const runway = this.clean(a.probableRunway || "");
|
|
508
|
+
const runwayConfidence = Number(a.runwayConfidence || 0);
|
|
509
|
+
if (runway && runwayConfidence >= 40) {
|
|
510
|
+
await this.incrementRankingState(
|
|
511
|
+
`${base}.runwayUsageRanking`,
|
|
512
|
+
`${base}.runwayUsageRankingText`,
|
|
513
|
+
runway,
|
|
514
|
+
30,
|
|
515
|
+
8
|
|
516
|
+
);
|
|
517
|
+
const airlineRunwayKey = `${info.airline} \u2192 RWY ${runway}`;
|
|
518
|
+
await this.incrementRankingState(
|
|
519
|
+
`${base}.airlineRunwayRanking`,
|
|
520
|
+
`${base}.airlineRunwayRankingText`,
|
|
521
|
+
airlineRunwayKey,
|
|
522
|
+
50,
|
|
523
|
+
8
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
const historyId = `${base}.flightLogHistory`;
|
|
527
|
+
const historyTextId = `${base}.flightLogHistoryText`;
|
|
528
|
+
const history = await this.readJsonState(historyId, []);
|
|
529
|
+
const entry = {
|
|
530
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
531
|
+
mode: info.mode,
|
|
532
|
+
phase: this.clean(a.flightPhase || ""),
|
|
533
|
+
callsign: info.callsign,
|
|
534
|
+
airline: info.airline,
|
|
535
|
+
route: info.route,
|
|
536
|
+
originIata: this.clean(a.originIata || ""),
|
|
537
|
+
destIata: this.clean(a.destIata || ""),
|
|
538
|
+
registration: info.registration,
|
|
539
|
+
aircraftType: type,
|
|
540
|
+
runway,
|
|
541
|
+
runwayConfidence: Number.isFinite(runwayConfidence) ? runwayConfidence : 0
|
|
542
|
+
};
|
|
543
|
+
history.unshift(entry);
|
|
544
|
+
const limitedHistory = history.slice(0, 200);
|
|
545
|
+
const historyText = limitedHistory.slice(0, 10).map((item) => {
|
|
546
|
+
const d = new Date(item.ts);
|
|
547
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
548
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
549
|
+
const icon = item.mode === "LANDING" ? "\u{1F6EC}" : item.mode === "TAKEOFF" ? "\u{1F6EB}" : "\u{1F6E9}\uFE0F";
|
|
550
|
+
const rw = item.runway ? ` \xB7 RWY ${item.runway}` : "";
|
|
551
|
+
const routeText = item.route ? ` \xB7 ${item.route}` : "";
|
|
552
|
+
return `${hh}:${mm} ${icon} ${item.callsign || "?"} \xB7 ${item.airline || "?"}${routeText}${rw}`;
|
|
553
|
+
}).join("\n");
|
|
554
|
+
await this.setForeignStateAsync(historyId, JSON.stringify(limitedHistory), true);
|
|
555
|
+
await this.setForeignStateAsync(historyTextId, historyText, true);
|
|
556
|
+
}
|
|
557
|
+
isA380(a) {
|
|
558
|
+
const all = [a.aircraftTypeText, a.aircraftModel, a.aircraftType, a.type].map((v) => this.clean(v).toUpperCase()).join(" ").replace(/[\s_-]/g, "");
|
|
559
|
+
return /A380|A388|A38/.test(all);
|
|
560
|
+
}
|
|
561
|
+
isB747(a) {
|
|
562
|
+
const all = [a.aircraftTypeText, a.aircraftModel, a.aircraftType, a.type].map((v) => this.clean(v).toUpperCase()).join(" ").replace(/[\s_-]/g, "");
|
|
563
|
+
return /B747|B741|B742|B743|B744|B748|747/.test(all);
|
|
564
|
+
}
|
|
565
|
+
isHeavyAircraft(a) {
|
|
566
|
+
const all = [
|
|
567
|
+
a.aircraftTypeText,
|
|
568
|
+
a.aircraftModel,
|
|
569
|
+
a.aircraftType,
|
|
570
|
+
a.type,
|
|
571
|
+
a.aircraftSize
|
|
572
|
+
].map((v) => this.clean(v).toUpperCase()).join(" ").replace(/[\s_-]/g, "");
|
|
573
|
+
return /WIDEBODY|HEAVY|SUPERJUMBO|JUMBO|HEAVYCARGO/.test(all) || /A300|A310|A330|A332|A333|A338|A339|A340|A342|A343|A345|A346/.test(all) || /A350|A359|A35K|A351|A380|A388/.test(all) || /B747|B741|B742|B743|B744|B748/.test(all) || /B757|B752|B753/.test(all) || /B767|B762|B763|B764/.test(all) || /B777|B772|B773|B77W|B778|B779|B77L|B77F/.test(all) || /B787|B788|B789|B78X/.test(all) || /MD11|DC10|L1011|IL86|IL96/.test(all) || /AN124|AN225|C5M|GALAXY|C17|C17A|GLOBEMASTER|A400|A400M/.test(all);
|
|
574
|
+
}
|
|
575
|
+
isSpecialFlight(a) {
|
|
576
|
+
return !!(a.isSpecial || this.clean(a.specialText) || this.clean(a.specialLiveryTitle) || this.clean(a.specialLiveryDescription) || this.clean(a.specialLiveryFull) || this.clean(a.specialLiveryVisText));
|
|
577
|
+
}
|
|
578
|
+
mergeRanking(target, source) {
|
|
579
|
+
for (const [key, value] of Object.entries(source || {})) {
|
|
580
|
+
const cleanKey = this.clean(key);
|
|
581
|
+
if (!cleanKey) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const count = Number(value || 0);
|
|
585
|
+
if (!Number.isFinite(count) || count <= 0) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
target[cleanKey] = (target[cleanKey] || 0) + count;
|
|
589
|
+
}
|
|
590
|
+
return target;
|
|
591
|
+
}
|
|
592
|
+
bestHourFromHourly(hourly) {
|
|
593
|
+
const best = Object.entries(hourly || {}).filter(([, value]) => Number((value == null ? void 0 : value.total) || 0) > 0).sort((a, b) => Number(b[1].total || 0) - Number(a[1].total || 0) || Number(a[0]) - Number(b[0]))[0];
|
|
594
|
+
return best ? { hour: `${best[0]}:00`, total: Number(best[1].total || 0) } : { hour: "", total: 0 };
|
|
595
|
+
}
|
|
596
|
+
async archiveTodayStatistics(base, storedDate) {
|
|
597
|
+
const todayBase = `${base}.today`;
|
|
598
|
+
const yesterdayBase = `${base}.yesterday`;
|
|
599
|
+
const totalFlights = await this.readNumberState(`${todayBase}.totalFlights`);
|
|
600
|
+
if (!storedDate || totalFlights <= 0) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const landings = await this.readNumberState(`${todayBase}.landings`);
|
|
604
|
+
const departures = await this.readNumberState(`${todayBase}.departures`);
|
|
605
|
+
const overflights = await this.readNumberState(`${todayBase}.overflights`);
|
|
606
|
+
const a380Count = await this.readNumberState(`${todayBase}.a380Count`);
|
|
607
|
+
const b747Count = await this.readNumberState(`${todayBase}.b747Count`);
|
|
608
|
+
const heavyAircraftCount = await this.readNumberState(`${todayBase}.heavyAircraftCount`);
|
|
609
|
+
const specialLiveryCount = await this.readNumberState(`${todayBase}.specialLiveryCount`);
|
|
610
|
+
const hourly = await this.readJsonState(`${todayBase}.hourly`, this.emptyHourlyStats());
|
|
611
|
+
const airlineRanking = await this.readJsonState(`${todayBase}.airlineRanking`, {});
|
|
612
|
+
const routeRanking = await this.readJsonState(`${todayBase}.routeRanking`, {});
|
|
613
|
+
const airlineSorted = this.sortedRanking(airlineRanking, 20);
|
|
614
|
+
const routeSorted = this.sortedRanking(routeRanking, 20);
|
|
615
|
+
const bestHour = this.bestHourFromHourly(hourly);
|
|
616
|
+
const topAirline = airlineSorted.length ? `${airlineSorted[0][0]} \xB7 ${airlineSorted[0][1]}` : "";
|
|
617
|
+
const topRoute = routeSorted.length ? `${routeSorted[0][0]} \xB7 ${routeSorted[0][1]}` : "";
|
|
618
|
+
await this.setForeignStateAsync(`${yesterdayBase}.date`, storedDate, true);
|
|
619
|
+
await this.setForeignStateAsync(`${yesterdayBase}.totalFlights`, totalFlights, true);
|
|
620
|
+
await this.setForeignStateAsync(`${yesterdayBase}.landings`, landings, true);
|
|
621
|
+
await this.setForeignStateAsync(`${yesterdayBase}.departures`, departures, true);
|
|
622
|
+
await this.setForeignStateAsync(`${yesterdayBase}.overflights`, overflights, true);
|
|
623
|
+
await this.setForeignStateAsync(
|
|
624
|
+
`${yesterdayBase}.bestSpotterHour`,
|
|
625
|
+
bestHour.hour ? `${bestHour.hour} \xB7 ${bestHour.total} Fl\xFCge` : "",
|
|
626
|
+
true
|
|
627
|
+
);
|
|
628
|
+
await this.setForeignStateAsync(`${yesterdayBase}.bestHourFlights`, bestHour.total, true);
|
|
629
|
+
await this.setForeignStateAsync(`${yesterdayBase}.topAirline`, topAirline, true);
|
|
630
|
+
await this.setForeignStateAsync(`${yesterdayBase}.topRoute`, topRoute, true);
|
|
631
|
+
await this.setForeignStateAsync(`${yesterdayBase}.a380Count`, a380Count, true);
|
|
632
|
+
await this.setForeignStateAsync(`${yesterdayBase}.b747Count`, b747Count, true);
|
|
633
|
+
await this.setForeignStateAsync(`${yesterdayBase}.heavyAircraftCount`, heavyAircraftCount, true);
|
|
634
|
+
await this.setForeignStateAsync(`${yesterdayBase}.specialLiveryCount`, specialLiveryCount, true);
|
|
635
|
+
await this.setForeignStateAsync(`${yesterdayBase}.hourly`, JSON.stringify(hourly), true);
|
|
636
|
+
await this.setForeignStateAsync(
|
|
637
|
+
`${yesterdayBase}.airlineRankingText`,
|
|
638
|
+
this.topRankingText(airlineSorted, 5),
|
|
639
|
+
true
|
|
640
|
+
);
|
|
641
|
+
await this.setForeignStateAsync(`${yesterdayBase}.routeRankingText`, this.topRankingText(routeSorted, 5), true);
|
|
642
|
+
const historyId = `${base}.history.daily`;
|
|
643
|
+
const history = await this.readJsonState(historyId, []);
|
|
644
|
+
const entry = {
|
|
645
|
+
date: storedDate,
|
|
646
|
+
totalFlights,
|
|
647
|
+
landings,
|
|
648
|
+
departures,
|
|
649
|
+
overflights,
|
|
650
|
+
bestHour: bestHour.hour,
|
|
651
|
+
bestHourFlights: bestHour.total,
|
|
652
|
+
topAirline,
|
|
653
|
+
topRoute,
|
|
654
|
+
a380Count,
|
|
655
|
+
specialLiveryCount
|
|
656
|
+
};
|
|
657
|
+
const limitedHistory = [entry, ...history.filter((item) => this.clean(item == null ? void 0 : item.date) !== storedDate)].slice(0, 365);
|
|
658
|
+
const historyText = limitedHistory.slice(0, 14).map((item) => {
|
|
659
|
+
const best = item.bestHour ? ` \xB7 beste Zeit ${item.bestHour}` : "";
|
|
660
|
+
const special = Number(item.specialLiveryCount || 0) > 0 ? ` \xB7 \u2B50 ${item.specialLiveryCount}` : "";
|
|
661
|
+
const heavy = Number(item.heavyAircraftCount || 0) > 0 ? ` \xB7 Heavy ${item.heavyAircraftCount}` : "";
|
|
662
|
+
const a380 = Number(item.a380Count || 0) > 0 ? ` \xB7 A380 ${item.a380Count}` : "";
|
|
663
|
+
const b747 = Number(item.b747Count || 0) > 0 ? ` \xB7 B747 ${item.b747Count}` : "";
|
|
664
|
+
return `${item.date}: ${item.totalFlights} Fl\xFCge${best}${heavy}${a380}${b747}${special}`;
|
|
665
|
+
}).join("\n");
|
|
666
|
+
await this.setForeignStateAsync(historyId, JSON.stringify(limitedHistory), true);
|
|
667
|
+
await this.setForeignStateAsync(`${base}.history.dailyText`, historyText, true);
|
|
668
|
+
const currentBestDayFlights = await this.readNumberState(`${base}.alltime.bestDayFlights`);
|
|
669
|
+
if (totalFlights > currentBestDayFlights) {
|
|
670
|
+
await this.setForeignStateAsync(`${base}.alltime.bestDayDate`, storedDate, true);
|
|
671
|
+
await this.setForeignStateAsync(`${base}.alltime.bestDayFlights`, totalFlights, true);
|
|
672
|
+
}
|
|
673
|
+
const currentBestSpecial = await this.readNumberState(`${base}.alltime.bestSpecialDayCount`);
|
|
674
|
+
if (specialLiveryCount > currentBestSpecial) {
|
|
675
|
+
await this.setForeignStateAsync(`${base}.alltime.bestSpecialDayDate`, storedDate, true);
|
|
676
|
+
await this.setForeignStateAsync(`${base}.alltime.bestSpecialDayCount`, specialLiveryCount, true);
|
|
677
|
+
}
|
|
678
|
+
await this.setForeignStateAsync(
|
|
679
|
+
`${base}.alltime.a380Count`,
|
|
680
|
+
await this.readNumberState(`${base}.alltime.a380Count`) + a380Count,
|
|
681
|
+
true
|
|
682
|
+
);
|
|
683
|
+
await this.setForeignStateAsync(
|
|
684
|
+
`${base}.alltime.specialLiveryCount`,
|
|
685
|
+
await this.readNumberState(`${base}.alltime.specialLiveryCount`) + specialLiveryCount,
|
|
686
|
+
true
|
|
687
|
+
);
|
|
688
|
+
const alltimeHourly = await this.readJsonState(`${base}.alltime.hourly`, {});
|
|
689
|
+
for (const [hour, value] of Object.entries(hourly || {})) {
|
|
690
|
+
alltimeHourly[hour] = (alltimeHourly[hour] || 0) + Number((value == null ? void 0 : value.total) || 0);
|
|
691
|
+
}
|
|
692
|
+
const bestAlltimeHour = Object.entries(alltimeHourly).filter(([, count]) => Number(count || 0) > 0).sort((a, b) => Number(b[1] || 0) - Number(a[1] || 0) || Number(a[0]) - Number(b[0]))[0];
|
|
693
|
+
await this.setForeignStateAsync(`${base}.alltime.hourly`, JSON.stringify(alltimeHourly), true);
|
|
694
|
+
await this.setForeignStateAsync(
|
|
695
|
+
`${base}.alltime.bestHour`,
|
|
696
|
+
bestAlltimeHour ? `${bestAlltimeHour[0]}:00` : "",
|
|
697
|
+
true
|
|
698
|
+
);
|
|
699
|
+
await this.setForeignStateAsync(
|
|
700
|
+
`${base}.alltime.bestHourFlights`,
|
|
701
|
+
bestAlltimeHour ? Number(bestAlltimeHour[1] || 0) : 0,
|
|
702
|
+
true
|
|
703
|
+
);
|
|
704
|
+
const alltimeAirlines = this.mergeRanking(
|
|
705
|
+
await this.readJsonState(`${base}.alltime.airlineRanking`, {}),
|
|
706
|
+
airlineRanking
|
|
707
|
+
);
|
|
708
|
+
const alltimeRoutes = this.mergeRanking(
|
|
709
|
+
await this.readJsonState(`${base}.alltime.routeRanking`, {}),
|
|
710
|
+
routeRanking
|
|
711
|
+
);
|
|
712
|
+
const alltimeAirlineSorted = this.sortedRanking(alltimeAirlines, 50);
|
|
713
|
+
const alltimeRouteSorted = this.sortedRanking(alltimeRoutes, 50);
|
|
714
|
+
await this.setForeignStateAsync(
|
|
715
|
+
`${base}.alltime.airlineRanking`,
|
|
716
|
+
JSON.stringify(Object.fromEntries(alltimeAirlineSorted)),
|
|
717
|
+
true
|
|
718
|
+
);
|
|
719
|
+
await this.setForeignStateAsync(
|
|
720
|
+
`${base}.alltime.airlineRankingText`,
|
|
721
|
+
this.topRankingText(alltimeAirlineSorted, 8),
|
|
722
|
+
true
|
|
723
|
+
);
|
|
724
|
+
await this.setForeignStateAsync(
|
|
725
|
+
`${base}.alltime.routeRanking`,
|
|
726
|
+
JSON.stringify(Object.fromEntries(alltimeRouteSorted)),
|
|
727
|
+
true
|
|
728
|
+
);
|
|
729
|
+
await this.setForeignStateAsync(
|
|
730
|
+
`${base}.alltime.routeRankingText`,
|
|
731
|
+
this.topRankingText(alltimeRouteSorted, 8),
|
|
732
|
+
true
|
|
733
|
+
);
|
|
734
|
+
await this.setForeignStateAsync(
|
|
735
|
+
`${base}.alltime.bestAirline`,
|
|
736
|
+
alltimeAirlineSorted.length ? `${alltimeAirlineSorted[0][0]} \xB7 ${alltimeAirlineSorted[0][1]}` : "",
|
|
737
|
+
true
|
|
738
|
+
);
|
|
739
|
+
await this.setForeignStateAsync(
|
|
740
|
+
`${base}.alltime.bestRoute`,
|
|
741
|
+
alltimeRouteSorted.length ? `${alltimeRouteSorted[0][0]} \xB7 ${alltimeRouteSorted[0][1]}` : "",
|
|
742
|
+
true
|
|
743
|
+
);
|
|
744
|
+
const aircraftTypeRanking = await this.readJsonState(`${base}.aircraftTypeRanking`, {});
|
|
745
|
+
const aircraftTypeSorted = this.sortedRanking(aircraftTypeRanking, 50);
|
|
746
|
+
await this.setForeignStateAsync(
|
|
747
|
+
`${base}.alltime.bestAircraftType`,
|
|
748
|
+
aircraftTypeSorted.length ? `${aircraftTypeSorted[0][0]} \xB7 ${aircraftTypeSorted[0][1]}` : "",
|
|
749
|
+
true
|
|
750
|
+
);
|
|
751
|
+
this.log.info(`[JetFrame] Tagesstatistik archiviert: ${storedDate} \xB7 ${totalFlights} Fl\xFCge`);
|
|
752
|
+
}
|
|
753
|
+
todayDateKey() {
|
|
754
|
+
const d = /* @__PURE__ */ new Date();
|
|
755
|
+
const yyyy = d.getFullYear();
|
|
756
|
+
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
757
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
758
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
759
|
+
}
|
|
760
|
+
async resetTodayStatisticsIfNeeded(base) {
|
|
761
|
+
const todayBase = `${base}.today`;
|
|
762
|
+
const today = this.todayDateKey();
|
|
763
|
+
let storedDate = "";
|
|
764
|
+
try {
|
|
765
|
+
const st = await this.getForeignStateAsync(`${todayBase}.date`);
|
|
766
|
+
storedDate = this.clean(st == null ? void 0 : st.val);
|
|
767
|
+
} catch {
|
|
768
|
+
storedDate = "";
|
|
769
|
+
}
|
|
770
|
+
if (storedDate === today) {
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
await this.archiveTodayStatistics(base, storedDate);
|
|
774
|
+
await this.setForeignStateAsync(`${todayBase}.date`, today, true);
|
|
775
|
+
await this.setForeignStateAsync(`${todayBase}.totalFlights`, 0, true);
|
|
776
|
+
await this.setForeignStateAsync(`${todayBase}.landings`, 0, true);
|
|
777
|
+
await this.setForeignStateAsync(`${todayBase}.departures`, 0, true);
|
|
778
|
+
await this.setForeignStateAsync(`${todayBase}.overflights`, 0, true);
|
|
779
|
+
await this.setForeignStateAsync(`${todayBase}.firstSeen`, "", true);
|
|
780
|
+
await this.setForeignStateAsync(`${todayBase}.lastSeen`, "", true);
|
|
781
|
+
await this.setForeignStateAsync(`${todayBase}.lastFlight`, "", true);
|
|
782
|
+
await this.setForeignStateAsync(`${todayBase}.lastAirline`, "", true);
|
|
783
|
+
await this.setForeignStateAsync(`${todayBase}.lastRoute`, "", true);
|
|
784
|
+
await this.setForeignStateAsync(`${todayBase}.lastRegistration`, "", true);
|
|
785
|
+
await this.setForeignStateAsync(`${todayBase}.topAirline`, "", true);
|
|
786
|
+
await this.setForeignStateAsync(`${todayBase}.topRoute`, "", true);
|
|
787
|
+
await this.setForeignStateAsync(`${todayBase}.airlineRanking`, "{}", true);
|
|
788
|
+
await this.setForeignStateAsync(`${todayBase}.airlineRankingText`, "", true);
|
|
789
|
+
await this.setForeignStateAsync(`${todayBase}.routeRanking`, "{}", true);
|
|
790
|
+
await this.setForeignStateAsync(`${todayBase}.routeRankingText`, "", true);
|
|
791
|
+
await this.setForeignStateAsync(`${todayBase}.hourly`, JSON.stringify(this.emptyHourlyStats()), true);
|
|
792
|
+
await this.setForeignStateAsync(`${todayBase}.hourlyText`, "", true);
|
|
793
|
+
await this.setForeignStateAsync(`${todayBase}.bestSpotterHour`, "", true);
|
|
794
|
+
await this.setForeignStateAsync(`${todayBase}.currentHourFlights`, 0, true);
|
|
795
|
+
await this.setForeignStateAsync(`${todayBase}.rushHourNow`, false, true);
|
|
796
|
+
await this.setForeignStateAsync(`${todayBase}.rushHourText`, "", true);
|
|
797
|
+
await this.setForeignStateAsync(`${todayBase}.a380Count`, 0, true);
|
|
798
|
+
await this.setForeignStateAsync(`${todayBase}.b747Count`, 0, true);
|
|
799
|
+
await this.setForeignStateAsync(`${todayBase}.heavyAircraftCount`, 0, true);
|
|
800
|
+
await this.setForeignStateAsync(`${todayBase}.specialLiveryCount`, 0, true);
|
|
801
|
+
}
|
|
802
|
+
emptyHourlyStats() {
|
|
803
|
+
const result = {};
|
|
804
|
+
for (let h = 0; h < 24; h++) {
|
|
805
|
+
const key = String(h).padStart(2, "0");
|
|
806
|
+
result[key] = {
|
|
807
|
+
total: 0,
|
|
808
|
+
landings: 0,
|
|
809
|
+
departures: 0,
|
|
810
|
+
overflights: 0
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
return result;
|
|
814
|
+
}
|
|
815
|
+
async updateTodayHourlyStatistics(todayBase, mode) {
|
|
816
|
+
var _a;
|
|
817
|
+
const now = /* @__PURE__ */ new Date();
|
|
818
|
+
const hour = String(now.getHours()).padStart(2, "0");
|
|
819
|
+
const hourly = await this.readJsonState(`${todayBase}.hourly`, this.emptyHourlyStats());
|
|
820
|
+
if (!hourly[hour]) {
|
|
821
|
+
hourly[hour] = {
|
|
822
|
+
total: 0,
|
|
823
|
+
landings: 0,
|
|
824
|
+
departures: 0,
|
|
825
|
+
overflights: 0
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
hourly[hour].total += 1;
|
|
829
|
+
if (mode === "LANDING") {
|
|
830
|
+
hourly[hour].landings += 1;
|
|
831
|
+
} else if (mode === "TAKEOFF") {
|
|
832
|
+
hourly[hour].departures += 1;
|
|
833
|
+
} else if (mode === "OVERFLIGHT") {
|
|
834
|
+
hourly[hour].overflights += 1;
|
|
835
|
+
}
|
|
836
|
+
const entries = Object.entries(hourly).sort((a, b) => Number(a[0]) - Number(b[0]));
|
|
837
|
+
const activeEntries = entries.filter(([, v]) => v.total > 0);
|
|
838
|
+
const hourlyText = activeEntries.map(([h, v]) => `${h}:00 \xB7 ${v.total} (${v.landings}\u{1F6EC} ${v.departures}\u{1F6EB} ${v.overflights}\u{1F6E9}\uFE0F)`).join("\n");
|
|
839
|
+
const best = activeEntries.slice().sort((a, b) => b[1].total - a[1].total || Number(a[0]) - Number(b[0]))[0];
|
|
840
|
+
const currentHourTotal = ((_a = hourly[hour]) == null ? void 0 : _a.total) || 0;
|
|
841
|
+
const avgActive = activeEntries.length > 0 ? activeEntries.reduce((sum, [, v]) => sum + v.total, 0) / activeEntries.length : 0;
|
|
842
|
+
const rushHourNow = currentHourTotal >= 5 && currentHourTotal >= Math.max(3, Math.ceil(avgActive * 1.35));
|
|
843
|
+
const rushHourText = rushHourNow ? `\u{1F525} Rushhour: ${currentHourTotal} Fl\xFCge seit ${hour}:00` : currentHourTotal > 0 ? `Aktuelle Stunde: ${currentHourTotal} Fl\xFCge` : "";
|
|
844
|
+
await this.setForeignStateAsync(`${todayBase}.hourly`, JSON.stringify(hourly), true);
|
|
845
|
+
await this.setForeignStateAsync(`${todayBase}.hourlyText`, hourlyText, true);
|
|
846
|
+
await this.setForeignStateAsync(
|
|
847
|
+
`${todayBase}.bestSpotterHour`,
|
|
848
|
+
best ? `${best[0]}:00 \xB7 ${best[1].total} Fl\xFCge` : "",
|
|
849
|
+
true
|
|
850
|
+
);
|
|
851
|
+
await this.setForeignStateAsync(`${todayBase}.currentHourFlights`, currentHourTotal, true);
|
|
852
|
+
await this.setForeignStateAsync(`${todayBase}.rushHourNow`, rushHourNow, true);
|
|
853
|
+
await this.setForeignStateAsync(`${todayBase}.rushHourText`, rushHourText, true);
|
|
854
|
+
}
|
|
855
|
+
async updateTodayStatistics(base, a, info) {
|
|
856
|
+
var _a;
|
|
857
|
+
await this.resetTodayStatisticsIfNeeded(base);
|
|
858
|
+
const todayBase = `${base}.today`;
|
|
859
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
860
|
+
const total = await this.readNumberState(`${todayBase}.totalFlights`) + 1;
|
|
861
|
+
await this.setForeignStateAsync(`${todayBase}.totalFlights`, total, true);
|
|
862
|
+
if (info.mode === "LANDING") {
|
|
863
|
+
await this.setForeignStateAsync(
|
|
864
|
+
`${todayBase}.landings`,
|
|
865
|
+
await this.readNumberState(`${todayBase}.landings`) + 1,
|
|
866
|
+
true
|
|
867
|
+
);
|
|
868
|
+
} else if (info.mode === "TAKEOFF") {
|
|
869
|
+
await this.setForeignStateAsync(
|
|
870
|
+
`${todayBase}.departures`,
|
|
871
|
+
await this.readNumberState(`${todayBase}.departures`) + 1,
|
|
872
|
+
true
|
|
873
|
+
);
|
|
874
|
+
} else if (info.mode === "OVERFLIGHT") {
|
|
875
|
+
await this.setForeignStateAsync(
|
|
876
|
+
`${todayBase}.overflights`,
|
|
877
|
+
await this.readNumberState(`${todayBase}.overflights`) + 1,
|
|
878
|
+
true
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
const firstSeen = this.clean((_a = await this.getForeignStateAsync(`${todayBase}.firstSeen`)) == null ? void 0 : _a.val);
|
|
883
|
+
if (!firstSeen) {
|
|
884
|
+
await this.setForeignStateAsync(`${todayBase}.firstSeen`, nowIso, true);
|
|
885
|
+
}
|
|
886
|
+
} catch {
|
|
887
|
+
await this.setForeignStateAsync(`${todayBase}.firstSeen`, nowIso, true);
|
|
888
|
+
}
|
|
889
|
+
await this.setForeignStateAsync(`${todayBase}.lastSeen`, nowIso, true);
|
|
890
|
+
await this.setForeignStateAsync(`${todayBase}.lastFlight`, info.callsign, true);
|
|
891
|
+
await this.setForeignStateAsync(`${todayBase}.lastAirline`, info.airline, true);
|
|
892
|
+
await this.setForeignStateAsync(`${todayBase}.lastRoute`, info.route, true);
|
|
893
|
+
await this.setForeignStateAsync(`${todayBase}.lastRegistration`, info.registration, true);
|
|
894
|
+
const airlineRanking = await this.readJsonState(`${todayBase}.airlineRanking`, {});
|
|
895
|
+
airlineRanking[info.airline] = (airlineRanking[info.airline] || 0) + 1;
|
|
896
|
+
const airlineSorted = Object.entries(airlineRanking).sort((aEntry, bEntry) => bEntry[1] - aEntry[1] || aEntry[0].localeCompare(bEntry[0])).slice(0, 20);
|
|
897
|
+
const airlineText = airlineSorted.slice(0, 5).map(([name, count], index) => `${index + 1}. ${name} \xB7 ${count}`).join("\n");
|
|
898
|
+
await this.setForeignStateAsync(
|
|
899
|
+
`${todayBase}.airlineRanking`,
|
|
900
|
+
JSON.stringify(Object.fromEntries(airlineSorted)),
|
|
901
|
+
true
|
|
902
|
+
);
|
|
903
|
+
await this.setForeignStateAsync(`${todayBase}.airlineRankingText`, airlineText, true);
|
|
904
|
+
await this.setForeignStateAsync(
|
|
905
|
+
`${todayBase}.topAirline`,
|
|
906
|
+
airlineSorted.length ? `${airlineSorted[0][0]} \xB7 ${airlineSorted[0][1]}` : "",
|
|
907
|
+
true
|
|
908
|
+
);
|
|
909
|
+
if (info.route) {
|
|
910
|
+
const routeRanking = await this.readJsonState(`${todayBase}.routeRanking`, {});
|
|
911
|
+
routeRanking[info.route] = (routeRanking[info.route] || 0) + 1;
|
|
912
|
+
const routeSorted = Object.entries(routeRanking).filter(([routeName]) => {
|
|
913
|
+
const r = this.clean(routeName);
|
|
914
|
+
return r && !r.includes("?") && r.includes("\u2192");
|
|
915
|
+
}).sort((aEntry, bEntry) => bEntry[1] - aEntry[1] || aEntry[0].localeCompare(bEntry[0])).slice(0, 20);
|
|
916
|
+
const routeText = routeSorted.slice(0, 5).map(([name, count], index) => `${index + 1}. ${name} \xB7 ${count}`).join("\n");
|
|
917
|
+
await this.setForeignStateAsync(
|
|
918
|
+
`${todayBase}.routeRanking`,
|
|
919
|
+
JSON.stringify(Object.fromEntries(routeSorted)),
|
|
920
|
+
true
|
|
921
|
+
);
|
|
922
|
+
await this.setForeignStateAsync(`${todayBase}.routeRankingText`, routeText, true);
|
|
923
|
+
await this.setForeignStateAsync(
|
|
924
|
+
`${todayBase}.topRoute`,
|
|
925
|
+
routeSorted.length ? `${routeSorted[0][0]} \xB7 ${routeSorted[0][1]}` : "",
|
|
926
|
+
true
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
if (this.isA380(a)) {
|
|
930
|
+
await this.setForeignStateAsync(
|
|
931
|
+
`${todayBase}.a380Count`,
|
|
932
|
+
await this.readNumberState(`${todayBase}.a380Count`) + 1,
|
|
933
|
+
true
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
if (this.isB747(a)) {
|
|
937
|
+
await this.setForeignStateAsync(
|
|
938
|
+
`${todayBase}.b747Count`,
|
|
939
|
+
await this.readNumberState(`${todayBase}.b747Count`) + 1,
|
|
940
|
+
true
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
if (this.isHeavyAircraft(a)) {
|
|
944
|
+
await this.setForeignStateAsync(
|
|
945
|
+
`${todayBase}.heavyAircraftCount`,
|
|
946
|
+
await this.readNumberState(`${todayBase}.heavyAircraftCount`) + 1,
|
|
947
|
+
true
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
if (this.isSpecialFlight(a)) {
|
|
951
|
+
await this.setForeignStateAsync(
|
|
952
|
+
`${todayBase}.specialLiveryCount`,
|
|
953
|
+
await this.readNumberState(`${todayBase}.specialLiveryCount`) + 1,
|
|
954
|
+
true
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
await this.updateTodayHourlyStatistics(todayBase, info.mode);
|
|
958
|
+
}
|
|
959
|
+
async updateStatistics(dpRoot, a) {
|
|
960
|
+
const base = `${dpRoot}.statistics`;
|
|
961
|
+
const mode = String(a.mode || "").toUpperCase();
|
|
962
|
+
const callsign = this.clean(a.routeCallsign || a.callsign || a.hex || "");
|
|
963
|
+
const airline = this.clean(a.airlineName || "Unbekannte Airline");
|
|
964
|
+
const registration = this.clean(a.registration || "");
|
|
965
|
+
const origin = this.clean(a.originIata || "");
|
|
966
|
+
const dest = this.clean(a.destIata || "");
|
|
967
|
+
const route = origin && dest ? `${origin} \u2192 ${dest}` : "";
|
|
968
|
+
await this.setForeignStateAsync(
|
|
969
|
+
`${base}.totalFlightsSeen`,
|
|
970
|
+
await this.readNumberState(`${base}.totalFlightsSeen`) + 1,
|
|
971
|
+
true
|
|
972
|
+
);
|
|
973
|
+
if (mode === "LANDING") {
|
|
974
|
+
await this.setForeignStateAsync(
|
|
975
|
+
`${base}.landings`,
|
|
976
|
+
await this.readNumberState(`${base}.landings`) + 1,
|
|
977
|
+
true
|
|
978
|
+
);
|
|
979
|
+
} else if (mode === "TAKEOFF") {
|
|
980
|
+
await this.setForeignStateAsync(
|
|
981
|
+
`${base}.departures`,
|
|
982
|
+
await this.readNumberState(`${base}.departures`) + 1,
|
|
983
|
+
true
|
|
984
|
+
);
|
|
985
|
+
} else if (mode === "OVERFLIGHT") {
|
|
986
|
+
await this.setForeignStateAsync(
|
|
987
|
+
`${base}.overflights`,
|
|
988
|
+
await this.readNumberState(`${base}.overflights`) + 1,
|
|
989
|
+
true
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
await this.setForeignStateAsync(`${base}.lastFlight`, callsign, true);
|
|
993
|
+
await this.setForeignStateAsync(`${base}.lastAirline`, airline, true);
|
|
994
|
+
await this.setForeignStateAsync(`${base}.lastRoute`, route, true);
|
|
995
|
+
await this.setForeignStateAsync(`${base}.lastRegistration`, registration, true);
|
|
996
|
+
await this.setForeignStateAsync(`${base}.lastSeen`, (/* @__PURE__ */ new Date()).toISOString(), true);
|
|
997
|
+
const ranking = await this.readJsonState(`${base}.airlineRanking`, {});
|
|
998
|
+
ranking[airline] = (ranking[airline] || 0) + 1;
|
|
999
|
+
const sorted = Object.entries(ranking).sort((aEntry, bEntry) => bEntry[1] - aEntry[1] || aEntry[0].localeCompare(bEntry[0])).slice(0, 20);
|
|
1000
|
+
const rankingJson = Object.fromEntries(sorted);
|
|
1001
|
+
const rankingText = sorted.slice(0, 5).map(([name, count], index) => `${index + 1}. ${name} \xB7 ${count}`).join("\n");
|
|
1002
|
+
await this.setForeignStateAsync(`${base}.airlineRanking`, JSON.stringify(rankingJson), true);
|
|
1003
|
+
await this.setForeignStateAsync(`${base}.airlineRankingText`, rankingText, true);
|
|
1004
|
+
await this.updateGlobalDetailedStatistics(base, a, {
|
|
1005
|
+
mode,
|
|
1006
|
+
callsign,
|
|
1007
|
+
airline,
|
|
1008
|
+
registration,
|
|
1009
|
+
route
|
|
1010
|
+
});
|
|
1011
|
+
await this.updateTodayStatistics(base, a, {
|
|
1012
|
+
mode,
|
|
1013
|
+
callsign,
|
|
1014
|
+
airline,
|
|
1015
|
+
registration,
|
|
1016
|
+
route
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
async loop() {
|
|
1020
|
+
try {
|
|
1021
|
+
this.clearTimer();
|
|
1022
|
+
const config = (0, import_config.readConfig)(this);
|
|
1023
|
+
if (!config.enabled) {
|
|
1024
|
+
await this.setForeignStateAsync(`${config.dpRoot}.status`, "disabled", true);
|
|
1025
|
+
this.scheduleNext(config.searchPollSeconds);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (this.liveTarget) {
|
|
1029
|
+
await this.liveLoop();
|
|
1030
|
+
} else {
|
|
1031
|
+
await this.searchLoop();
|
|
1032
|
+
}
|
|
1033
|
+
} catch (e) {
|
|
1034
|
+
this.logError(`JetFrame Fehler: ${this.errorText(e)}`);
|
|
1035
|
+
const config = (0, import_config.readConfig)(this);
|
|
1036
|
+
this.scheduleNext(config.searchPollSeconds);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
async searchLoop() {
|
|
1040
|
+
const config = (0, import_config.readConfig)(this);
|
|
1041
|
+
this.log.debug("Search Loop gestartet");
|
|
1042
|
+
await this.setForeignStateAsync(`${config.dpRoot}.status`, "searching", true);
|
|
1043
|
+
const data = await (0, import_adsb.fetchAdsb)(
|
|
1044
|
+
config,
|
|
1045
|
+
this.httpJsonRaw.bind(this),
|
|
1046
|
+
this.logWarn.bind(this),
|
|
1047
|
+
this.logDebug.bind(this)
|
|
1048
|
+
);
|
|
1049
|
+
this.log.debug("[JetFrame] ADSB Fetch OK");
|
|
1050
|
+
const aircraft = (0, import_adsb.parseAircraft)(data);
|
|
1051
|
+
this.log.debug(`[JetFrame] ADSB parsed: ${aircraft.length}`);
|
|
1052
|
+
this.log.debug(`Aircraft parsed: ${aircraft.length}`);
|
|
1053
|
+
const matches = (0, import_classify.getMatches)(config, aircraft);
|
|
1054
|
+
if (matches.length) {
|
|
1055
|
+
await this.updateIdleRunway(matches[0], config);
|
|
1056
|
+
}
|
|
1057
|
+
this.log.debug(`Matches gefunden: ${matches.length}`);
|
|
1058
|
+
await this.setForeignStateAsync(`${config.dpRoot}.lastUpdate`, (/* @__PURE__ */ new Date()).toISOString(), true);
|
|
1059
|
+
await this.setForeignStateAsync(`${config.dpRoot}.allCount`, aircraft.length, true);
|
|
1060
|
+
await this.setForeignStateAsync(`${config.dpRoot}.matchCount`, matches.length, true);
|
|
1061
|
+
if (!matches.length) {
|
|
1062
|
+
await this.setForeignStateAsync(
|
|
1063
|
+
`${config.dpRoot}.current.text`,
|
|
1064
|
+
`Kein Start/Landung/\xDCberflug bei ${config.airport.iata}`,
|
|
1065
|
+
true
|
|
1066
|
+
);
|
|
1067
|
+
this.scheduleNext(config.searchPollSeconds);
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
this.log.debug(
|
|
1071
|
+
`Best Match: ${matches[0].callsign || matches[0].hex || "?"} | alt=${matches[0].altFt}ft | mode=${matches[0].mode}`
|
|
1072
|
+
);
|
|
1073
|
+
await this.startNewFlight(matches[0]);
|
|
1074
|
+
}
|
|
1075
|
+
async liveLoop() {
|
|
1076
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
1077
|
+
const config = (0, import_config.readConfig)(this);
|
|
1078
|
+
const elapsed = (Date.now() - this.liveStarted) / 1e3;
|
|
1079
|
+
const data = await (0, import_adsb.fetchAdsb)(
|
|
1080
|
+
config,
|
|
1081
|
+
this.httpJsonRaw.bind(this),
|
|
1082
|
+
this.logWarn.bind(this),
|
|
1083
|
+
this.logDebug.bind(this)
|
|
1084
|
+
);
|
|
1085
|
+
const aircraft = (0, import_adsb.parseAircraft)(data);
|
|
1086
|
+
const matches = (0, import_classify.getMatches)(config, aircraft);
|
|
1087
|
+
await this.setForeignStateAsync(`${config.dpRoot}.lastUpdate`, (/* @__PURE__ */ new Date()).toISOString(), true);
|
|
1088
|
+
await this.setForeignStateAsync(`${config.dpRoot}.allCount`, aircraft.length, true);
|
|
1089
|
+
await this.setForeignStateAsync(`${config.dpRoot}.matchCount`, matches.length, true);
|
|
1090
|
+
const live = this.findCurrentLive(matches, this.liveTarget);
|
|
1091
|
+
const bestNow = matches[0];
|
|
1092
|
+
if (bestNow && !live && this.isDifferentAircraft(bestNow, this.liveTarget)) {
|
|
1093
|
+
this.log.info(`Neues Flugzeug erkannt, schalte um: ${bestNow.callsign || bestNow.hex}`);
|
|
1094
|
+
await this.startNewFlight(bestNow);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (elapsed >= config.liveMaxSeconds) {
|
|
1098
|
+
this.liveTarget = null;
|
|
1099
|
+
this.liveStarted = 0;
|
|
1100
|
+
this.liveInfo = null;
|
|
1101
|
+
await (0, import_states.clearFlight)(this, `${config.dpRoot}.current`);
|
|
1102
|
+
await this.setForeignStateAsync(`${config.dpRoot}.status`, "cleared", true);
|
|
1103
|
+
this.scheduleNext(config.searchPollSeconds);
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
if (!live) {
|
|
1107
|
+
this.liveTarget = null;
|
|
1108
|
+
this.liveStarted = 0;
|
|
1109
|
+
this.log.info("Live Flug verloren");
|
|
1110
|
+
await (0, import_states.clearFlight)(this, `${config.dpRoot}.current`);
|
|
1111
|
+
await this.setForeignStateAsync(`${config.dpRoot}.status`, "lost", true);
|
|
1112
|
+
this.scheduleNext(config.searchPollSeconds);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const bases = [`${config.dpRoot}.current`];
|
|
1116
|
+
if (((_a = this.liveTarget) == null ? void 0 : _a.mode) === "OVERFLIGHT") {
|
|
1117
|
+
bases.push(`${config.dpRoot}.overflight`);
|
|
1118
|
+
} else {
|
|
1119
|
+
bases.push(`${config.dpRoot}.airport`);
|
|
1120
|
+
}
|
|
1121
|
+
const enrichedLive = {
|
|
1122
|
+
...this.liveInfo || {},
|
|
1123
|
+
...live,
|
|
1124
|
+
// Diese Werte kommen nur aus saveImages()/enrichFlightInfo
|
|
1125
|
+
// und dürfen vom Live-ADS-B-Update nicht wieder leer überschrieben werden.
|
|
1126
|
+
localLogoUrl: ((_b = this.liveInfo) == null ? void 0 : _b.localLogoUrl) || live.localLogoUrl || "",
|
|
1127
|
+
localImageUrl: ((_c = this.liveInfo) == null ? void 0 : _c.localImageUrl) || live.localImageUrl || "",
|
|
1128
|
+
finalImageUrl: ((_d = this.liveInfo) == null ? void 0 : _d.finalImageUrl) || live.finalImageUrl || "",
|
|
1129
|
+
logoUrl: ((_e = this.liveInfo) == null ? void 0 : _e.logoUrl) || live.logoUrl || "",
|
|
1130
|
+
routeCallsign: ((_f = this.liveInfo) == null ? void 0 : _f.routeCallsign) || live.routeCallsign || live.callsign || "",
|
|
1131
|
+
aircraftModel: ((_g = this.liveInfo) == null ? void 0 : _g.aircraftModel) || live.aircraftModel || live.aircraftType || live.type || "",
|
|
1132
|
+
isSpecial: ((_h = this.liveInfo) == null ? void 0 : _h.isSpecial) || live.isSpecial || false,
|
|
1133
|
+
specialText: ((_i = this.liveInfo) == null ? void 0 : _i.specialText) || live.specialText || "",
|
|
1134
|
+
specialLiveryTitle: ((_j = this.liveInfo) == null ? void 0 : _j.specialLiveryTitle) || live.specialLiveryTitle || "",
|
|
1135
|
+
specialLiveryDescription: ((_k = this.liveInfo) == null ? void 0 : _k.specialLiveryDescription) || live.specialLiveryDescription || "",
|
|
1136
|
+
specialLiveryFull: ((_l = this.liveInfo) == null ? void 0 : _l.specialLiveryFull) || live.specialLiveryFull || "",
|
|
1137
|
+
specialLiveryVisText: ((_m = this.liveInfo) == null ? void 0 : _m.specialLiveryVisText) || live.specialLiveryVisText || ""
|
|
1138
|
+
};
|
|
1139
|
+
await this.applyProbableRunway(enrichedLive, config);
|
|
1140
|
+
this.liveInfo = {
|
|
1141
|
+
...enrichedLive
|
|
1142
|
+
};
|
|
1143
|
+
for (const base of bases) {
|
|
1144
|
+
await (0, import_states.writeFlight)(this, base, enrichedLive);
|
|
1145
|
+
}
|
|
1146
|
+
await this.setForeignStateAsync(`${config.dpRoot}.status`, "live", true);
|
|
1147
|
+
this.scheduleNext(config.livePollSeconds);
|
|
1148
|
+
}
|
|
1149
|
+
async applySpecialLivery(a, dpRoot) {
|
|
1150
|
+
const reg = this.clean(a.registration).toUpperCase();
|
|
1151
|
+
if (!reg) {
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
try {
|
|
1155
|
+
const st = await this.getForeignStateAsync(`${dpRoot}.specialLiveries`);
|
|
1156
|
+
const raw = String((st == null ? void 0 : st.val) || "").trim();
|
|
1157
|
+
if (!raw || raw === "[]") {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const list = JSON.parse(raw);
|
|
1161
|
+
if (!Array.isArray(list)) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const hit = list.find((item) => {
|
|
1165
|
+
const itemReg = this.clean(item == null ? void 0 : item.registration).toUpperCase();
|
|
1166
|
+
return itemReg && itemReg === reg;
|
|
1167
|
+
});
|
|
1168
|
+
if (!hit) {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
const emoji = this.clean(hit.emoji) || "\u{1F3A8}";
|
|
1172
|
+
const title = this.clean(hit.title) || this.clean(hit.type) || "Special Livery";
|
|
1173
|
+
const description = this.clean(hit.description);
|
|
1174
|
+
const airline = this.clean(hit.airline);
|
|
1175
|
+
a.isSpecial = true;
|
|
1176
|
+
a.specialLiveryTitle = title;
|
|
1177
|
+
a.specialLiveryDescription = description;
|
|
1178
|
+
a.specialLiveryFull = description || title;
|
|
1179
|
+
a.specialLiveryVisText = `${emoji} ${title}`;
|
|
1180
|
+
a.specialText = `${emoji} ${title}${airline ? ` \xB7 ${airline}` : ""}`;
|
|
1181
|
+
this.log.info(`Special Livery erkannt: ${reg} \xB7 ${title}`);
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
this.logWarn(`Special-Livery Match Fehler: ${this.errorText(e)}`);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
async startNewFlight(rawMatch) {
|
|
1187
|
+
const config = (0, import_config.readConfig)(this);
|
|
1188
|
+
const startKey = this.flightKey(rawMatch);
|
|
1189
|
+
const now = Date.now();
|
|
1190
|
+
if (startKey && startKey === this.lastStartKey && now - this.lastStartTs < 15e3) {
|
|
1191
|
+
this.log.debug(`Gleicher Flug wurde gerade erst gestartet \u2192 ignoriere: ${startKey}`);
|
|
1192
|
+
this.scheduleNext(config.livePollSeconds);
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
this.lastStartKey = startKey;
|
|
1196
|
+
this.lastStartTs = now;
|
|
1197
|
+
this.log.debug(`Starte neuen Flug: ${rawMatch.callsign || rawMatch.hex}`);
|
|
1198
|
+
const best = await (0, import_flightInfo.enrichFlightInfo)(
|
|
1199
|
+
this,
|
|
1200
|
+
config,
|
|
1201
|
+
rawMatch,
|
|
1202
|
+
this.httpJson.bind(this),
|
|
1203
|
+
this.httpText.bind(this),
|
|
1204
|
+
this.logDebug.bind(this),
|
|
1205
|
+
this.logWarn.bind(this)
|
|
1206
|
+
);
|
|
1207
|
+
await this.applySpecialLivery(best, config.dpRoot);
|
|
1208
|
+
await this.applyProbableRunway(best, config);
|
|
1209
|
+
this.log.info(
|
|
1210
|
+
`Neuer Flug: callsign=${best.callsign || ""} route=${best.originIata || "?"} \u2192 ${best.destIata || "?"} | ${best.originName || "?"} \u2192 ${best.destName || "?"}${best.probableRunwayText ? ` | ${best.probableRunwayText}` : ""}`
|
|
1211
|
+
);
|
|
1212
|
+
await this.updateStatistics(config.dpRoot, best);
|
|
1213
|
+
this.liveTarget = {
|
|
1214
|
+
hex: best.hex,
|
|
1215
|
+
callsign: best.callsign,
|
|
1216
|
+
mode: best.mode
|
|
1217
|
+
};
|
|
1218
|
+
const imageSaveKey = this.flightKey(best);
|
|
1219
|
+
if (imageSaveKey && imageSaveKey !== this.lastImageSaveKey) {
|
|
1220
|
+
await (0, import_images.saveImages)(this, config, best, this.logDebug.bind(this), this.logWarn.bind(this));
|
|
1221
|
+
this.lastImageSaveKey = imageSaveKey;
|
|
1222
|
+
}
|
|
1223
|
+
this.liveInfo = {
|
|
1224
|
+
...best
|
|
1225
|
+
};
|
|
1226
|
+
this.liveStarted = Date.now();
|
|
1227
|
+
await (0, import_states.writeFlight)(this, `${config.dpRoot}.current`, best);
|
|
1228
|
+
if (best.mode === "OVERFLIGHT") {
|
|
1229
|
+
await (0, import_states.writeFlight)(this, `${config.dpRoot}.overflight`, best);
|
|
1230
|
+
} else {
|
|
1231
|
+
await (0, import_states.writeFlight)(this, `${config.dpRoot}.airport`, best);
|
|
1232
|
+
}
|
|
1233
|
+
await this.setForeignStateAsync(`${config.dpRoot}.status`, "live", true);
|
|
1234
|
+
this.scheduleNext(config.livePollSeconds);
|
|
1235
|
+
}
|
|
1236
|
+
findCurrentLive(matches, target) {
|
|
1237
|
+
if (!matches.length || !target) {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
return matches.find((a) => {
|
|
1241
|
+
const aHex = this.clean(a.hex).toLowerCase();
|
|
1242
|
+
const tHex = this.clean(target.hex).toLowerCase();
|
|
1243
|
+
const aCall = this.clean(a.callsign).toUpperCase();
|
|
1244
|
+
const tCall = this.clean(target.callsign).toUpperCase();
|
|
1245
|
+
if (aHex && tHex && aHex === tHex) {
|
|
1246
|
+
return true;
|
|
1247
|
+
}
|
|
1248
|
+
if (aCall && tCall && aCall === tCall) {
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
return false;
|
|
1252
|
+
}) || null;
|
|
1253
|
+
}
|
|
1254
|
+
isDifferentAircraft(a, target) {
|
|
1255
|
+
if (!a || !target) {
|
|
1256
|
+
return false;
|
|
1257
|
+
}
|
|
1258
|
+
const aHex = this.clean(a.hex).toLowerCase();
|
|
1259
|
+
const tHex = this.clean(target.hex).toLowerCase();
|
|
1260
|
+
const aCall = this.clean(a.callsign).toUpperCase();
|
|
1261
|
+
const tCall = this.clean(target.callsign).toUpperCase();
|
|
1262
|
+
if (aCall && tCall && aCall === tCall) {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
if (aHex && tHex && aHex === tHex) {
|
|
1266
|
+
return false;
|
|
1267
|
+
}
|
|
1268
|
+
if (aCall && tCall) {
|
|
1269
|
+
return aCall !== tCall;
|
|
1270
|
+
}
|
|
1271
|
+
if (aHex && tHex) {
|
|
1272
|
+
return aHex !== tHex;
|
|
1273
|
+
}
|
|
1274
|
+
return false;
|
|
1275
|
+
}
|
|
1276
|
+
flightKey(a) {
|
|
1277
|
+
const hex = this.clean(a.hex).toLowerCase();
|
|
1278
|
+
const cs = this.clean(a.callsign).toUpperCase();
|
|
1279
|
+
if (cs) {
|
|
1280
|
+
return `CS:${cs}`;
|
|
1281
|
+
}
|
|
1282
|
+
if (hex) {
|
|
1283
|
+
return `HEX:${hex}`;
|
|
1284
|
+
}
|
|
1285
|
+
return "";
|
|
1286
|
+
}
|
|
1287
|
+
scheduleStatisticsRotation(dpRoot) {
|
|
1288
|
+
if (this.statisticsTimer) {
|
|
1289
|
+
clearInterval(this.statisticsTimer);
|
|
1290
|
+
this.statisticsTimer = null;
|
|
1291
|
+
}
|
|
1292
|
+
this.statisticsTimer = setInterval(() => {
|
|
1293
|
+
this.resetTodayStatisticsIfNeeded(`${dpRoot}.statistics`).catch((e) => {
|
|
1294
|
+
this.logWarn(`Tagesstatistik-Rotation fehlgeschlagen: ${this.errorText(e)}`);
|
|
1295
|
+
});
|
|
1296
|
+
}, 6e4);
|
|
1297
|
+
}
|
|
1298
|
+
clearStatisticsTimer() {
|
|
1299
|
+
if (this.statisticsTimer) {
|
|
1300
|
+
clearInterval(this.statisticsTimer);
|
|
1301
|
+
this.statisticsTimer = null;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
scheduleNext(seconds) {
|
|
1305
|
+
this.timer = setTimeout(() => this.loop(), seconds * 1e3);
|
|
1306
|
+
}
|
|
1307
|
+
clearTimer() {
|
|
1308
|
+
if (this.timer) {
|
|
1309
|
+
clearTimeout(this.timer);
|
|
1310
|
+
this.timer = null;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
async httpJson(url) {
|
|
1314
|
+
const res = await this.httpRequest(url, {
|
|
1315
|
+
timeout: 12e3,
|
|
1316
|
+
headers: {
|
|
1317
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36",
|
|
1318
|
+
Accept: "application/json,text/plain,*/*",
|
|
1319
|
+
"Accept-Language": "de-DE,de;q=0.9,en;q=0.8",
|
|
1320
|
+
"Cache-Control": "no-cache",
|
|
1321
|
+
Pragma: "no-cache"
|
|
1322
|
+
}
|
|
1323
|
+
});
|
|
1324
|
+
if (typeof res === "string") {
|
|
1325
|
+
return JSON.parse(res);
|
|
1326
|
+
}
|
|
1327
|
+
return res;
|
|
1328
|
+
}
|
|
1329
|
+
async httpJsonRaw(url) {
|
|
1330
|
+
const res = await this.httpRequest(url, {
|
|
1331
|
+
timeout: 2e4,
|
|
1332
|
+
headers: {
|
|
1333
|
+
"User-Agent": "Mozilla/5.0",
|
|
1334
|
+
Accept: "application/json,text/plain,*/*"
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
if (typeof res === "string") {
|
|
1338
|
+
const text = res.trim();
|
|
1339
|
+
if (text.startsWith("<")) {
|
|
1340
|
+
throw new Error("HTML statt JSON erhalten");
|
|
1341
|
+
}
|
|
1342
|
+
return JSON.parse(text);
|
|
1343
|
+
}
|
|
1344
|
+
return res;
|
|
1345
|
+
}
|
|
1346
|
+
async httpText(url) {
|
|
1347
|
+
const res = await this.httpRequest(url, {
|
|
1348
|
+
timeout: 15e3,
|
|
1349
|
+
headers: {
|
|
1350
|
+
"User-Agent": "Mozilla/5.0 AppleWebKit/605.1.15 Safari/604.1",
|
|
1351
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
1352
|
+
"Accept-Language": "de-DE,de;q=0.9,en;q=0.8",
|
|
1353
|
+
Referer: "https://www.google.com/"
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
return String(res || "");
|
|
1357
|
+
}
|
|
1358
|
+
httpRequest(url, options = {}) {
|
|
1359
|
+
return new Promise((resolve, reject) => {
|
|
1360
|
+
const client = url.startsWith("https") ? https : http;
|
|
1361
|
+
const req = client.get(
|
|
1362
|
+
url,
|
|
1363
|
+
{
|
|
1364
|
+
headers: options.headers || {},
|
|
1365
|
+
timeout: options.timeout || 15e3
|
|
1366
|
+
},
|
|
1367
|
+
(res) => {
|
|
1368
|
+
let body = "";
|
|
1369
|
+
res.on("data", (chunk) => {
|
|
1370
|
+
body += chunk;
|
|
1371
|
+
});
|
|
1372
|
+
res.on("end", () => {
|
|
1373
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
1374
|
+
this.httpRequest(res.headers.location, options).then(resolve).catch(reject);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
1378
|
+
reject(new Error(`HTTP ${res.statusCode} bei ${url}`));
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
resolve(body);
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
);
|
|
1385
|
+
req.on("error", reject);
|
|
1386
|
+
req.setTimeout(options.timeout || 15e3, () => {
|
|
1387
|
+
req.destroy(new Error(`timeout of ${options.timeout || 15e3}ms exceeded`));
|
|
1388
|
+
});
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
async ensureMetaObject() {
|
|
1392
|
+
const id = `${this.namespace}.meta`;
|
|
1393
|
+
try {
|
|
1394
|
+
const obj = await this.getObjectAsync(id);
|
|
1395
|
+
if (!obj) {
|
|
1396
|
+
await this.setObjectAsync(id, {
|
|
1397
|
+
type: "meta",
|
|
1398
|
+
common: {
|
|
1399
|
+
name: "JetFrame Files",
|
|
1400
|
+
type: "meta.user"
|
|
1401
|
+
},
|
|
1402
|
+
native: {}
|
|
1403
|
+
});
|
|
1404
|
+
this.log.info("Meta-Objekt f\xFCr Dateien erstellt");
|
|
1405
|
+
}
|
|
1406
|
+
} catch (e) {
|
|
1407
|
+
this.log.error(`Meta-Objekt Fehler: ${this.errorText(e)}`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
logDebug(msg) {
|
|
1411
|
+
const config = (0, import_config.readConfig)(this);
|
|
1412
|
+
this.log.debug(`[JetFrame] ${msg}`);
|
|
1413
|
+
}
|
|
1414
|
+
logWarn(msg) {
|
|
1415
|
+
this.log.warn(`[JetFrame] \u26A0\uFE0F ${msg}`);
|
|
1416
|
+
}
|
|
1417
|
+
logError(msg) {
|
|
1418
|
+
this.log.error(`[JetFrame] \u274C ${msg}`);
|
|
1419
|
+
}
|
|
1420
|
+
clean(v) {
|
|
1421
|
+
return String(v || "").trim();
|
|
1422
|
+
}
|
|
1423
|
+
errorText(e) {
|
|
1424
|
+
if (!e) {
|
|
1425
|
+
return "unbekannter Fehler";
|
|
1426
|
+
}
|
|
1427
|
+
if (typeof e === "string") {
|
|
1428
|
+
return e;
|
|
1429
|
+
}
|
|
1430
|
+
if (e instanceof Error) {
|
|
1431
|
+
return e.message;
|
|
1432
|
+
}
|
|
1433
|
+
try {
|
|
1434
|
+
return JSON.stringify(e);
|
|
1435
|
+
} catch {
|
|
1436
|
+
return String(e);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
onUnload(callback) {
|
|
1440
|
+
try {
|
|
1441
|
+
this.clearTimer();
|
|
1442
|
+
this.clearStatisticsTimer();
|
|
1443
|
+
callback();
|
|
1444
|
+
} catch {
|
|
1445
|
+
callback();
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (require.main !== module) {
|
|
1450
|
+
module.exports = (options) => new Jetframe(options);
|
|
1451
|
+
} else {
|
|
1452
|
+
(() => new Jetframe())();
|
|
1453
|
+
}
|
|
1454
|
+
//# sourceMappingURL=main.js.map
|