iobroker.agent-dvr 0.1.0 → 0.2.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/README.de.md +289 -175
- package/README.md +226 -90
- package/admin/assets/index-ulvdM37k.js +685 -0
- package/admin/i18n/de.json +3 -7
- package/admin/i18n/en.json +131 -135
- package/admin/i18n/es.json +3 -7
- package/admin/i18n/fr.json +3 -7
- package/admin/i18n/it.json +3 -7
- package/admin/i18n/nl.json +3 -7
- package/admin/i18n/pl.json +3 -7
- package/admin/i18n/pt.json +3 -7
- package/admin/i18n/ru.json +3 -7
- package/admin/i18n/uk.json +3 -7
- package/admin/i18n/zh-cn.json +3 -7
- package/admin/index_m.html +27 -0
- package/build/main.js +152 -103
- package/build/main.js.map +2 -2
- package/io-package.json +16 -17
- package/lib/web.js +52 -5
- package/package.json +1 -1
- package/www/flv.min.js +7534 -0
- package/www/index.html +382 -99
- package/admin/custom/go2rtcMapping.js +0 -169
- package/admin/jsonConfig.json +0 -822
package/build/main.js
CHANGED
|
@@ -282,6 +282,7 @@ window.ADVscan=scan;scan();
|
|
|
282
282
|
`.trim();
|
|
283
283
|
class AgentDvr extends utils.Adapter {
|
|
284
284
|
pollTimer = void 0;
|
|
285
|
+
pollBusy = false;
|
|
285
286
|
refreshTimer = void 0;
|
|
286
287
|
authHeader = null;
|
|
287
288
|
baseUrl = "";
|
|
@@ -291,6 +292,7 @@ class AgentDvr extends utils.Adapter {
|
|
|
291
292
|
ptzActive = /* @__PURE__ */ new Map();
|
|
292
293
|
widgetSig = {};
|
|
293
294
|
profileSig = "";
|
|
295
|
+
lastCamNames = [];
|
|
294
296
|
lastEventFn = {};
|
|
295
297
|
camAspect = {};
|
|
296
298
|
devById = /* @__PURE__ */ new Map();
|
|
@@ -352,47 +354,84 @@ class AgentDvr extends utils.Adapter {
|
|
|
352
354
|
callback();
|
|
353
355
|
}
|
|
354
356
|
onMessage(obj) {
|
|
355
|
-
if (!obj
|
|
357
|
+
if (!obj) {
|
|
356
358
|
return;
|
|
357
359
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
360
|
+
if (obj.command === "getGo2rtcStreams") {
|
|
361
|
+
const overrideUrl = typeof obj.message === "object" && obj.message !== null ? obj.message.url : void 0;
|
|
362
|
+
void this.fetchGo2rtcStreams(overrideUrl).then((result) => {
|
|
363
|
+
this.sendTo(obj.from, obj.command, result, obj.callback);
|
|
364
|
+
});
|
|
365
|
+
} else if (obj.command === "getAgentDvrCameras") {
|
|
366
|
+
this.sendTo(obj.from, obj.command, { cameras: this.lastCamNames }, obj.callback);
|
|
367
|
+
} else if (obj.command === "getAvailableSources") {
|
|
368
|
+
void this.fetchAvailableSources().then((result) => {
|
|
369
|
+
this.sendTo(obj.from, obj.command, result, obj.callback);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
361
372
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
373
|
+
async fetchAvailableSources() {
|
|
374
|
+
const deadline = new Promise(
|
|
375
|
+
(res) => this.setTimeout(() => res({ result: [{ source: "!", name: "Timeout" }] }), 5e3)
|
|
376
|
+
);
|
|
377
|
+
return Promise.race([this._doFetchAvailableSources(), deadline]);
|
|
378
|
+
}
|
|
379
|
+
async _doFetchAvailableSources() {
|
|
380
|
+
const rows = [];
|
|
381
|
+
for (const c of this.lastCamNames) {
|
|
382
|
+
rows.push({ source: "AgentDVR", name: `${c.name} (${c.key})` });
|
|
383
|
+
}
|
|
384
|
+
const { streams } = await this.fetchGo2rtcStreams();
|
|
385
|
+
for (const s of streams) {
|
|
386
|
+
rows.push({ source: "go2rtc", name: s });
|
|
387
|
+
}
|
|
388
|
+
if (!rows.length) {
|
|
389
|
+
rows.push({ source: "\u2014", name: "Keine Quellen gefunden (Adapter hat noch nicht gepollt)" });
|
|
390
|
+
}
|
|
391
|
+
return { result: rows };
|
|
392
|
+
}
|
|
393
|
+
fetchGo2rtcStreams(overrideUrl) {
|
|
394
|
+
const url = (overrideUrl || this.config.go2rtcUrl || "").replace(/^https:\/\//i, "http://");
|
|
395
|
+
if (!url) {
|
|
396
|
+
return Promise.resolve({ streams: [], error: "go2rtcUrl nicht konfiguriert" });
|
|
397
|
+
}
|
|
398
|
+
let target;
|
|
399
|
+
try {
|
|
400
|
+
target = new URL("/api/streams", url);
|
|
401
|
+
} catch {
|
|
402
|
+
return Promise.resolve({ streams: [], error: `Ung\xFCltige URL: ${url}` });
|
|
403
|
+
}
|
|
404
|
+
let httpReq;
|
|
405
|
+
const fetch = new Promise((resolve) => {
|
|
376
406
|
const mod = target.protocol === "https:" ? https : http;
|
|
377
|
-
|
|
407
|
+
httpReq = mod.get(target.toString(), (res) => {
|
|
378
408
|
let body = "";
|
|
379
409
|
res.on("data", (c) => {
|
|
380
410
|
body += c.toString();
|
|
381
411
|
});
|
|
382
412
|
res.on("end", () => {
|
|
383
413
|
try {
|
|
384
|
-
|
|
414
|
+
const parsed = JSON.parse(body);
|
|
415
|
+
const streams = Object.keys(parsed || {});
|
|
416
|
+
resolve({
|
|
417
|
+
streams,
|
|
418
|
+
error: streams.length === 0 ? "go2rtc antwortet, aber keine Streams konfiguriert" : void 0
|
|
419
|
+
});
|
|
385
420
|
} catch {
|
|
386
|
-
resolve([]);
|
|
421
|
+
resolve({ streams: [], error: `Ung\xFCltige JSON-Antwort von go2rtc (HTTP ${res.statusCode})` });
|
|
387
422
|
}
|
|
388
423
|
});
|
|
389
|
-
res.on("error", () => resolve([]));
|
|
390
|
-
});
|
|
391
|
-
req.setTimeout(4e3, () => {
|
|
392
|
-
req.destroy();
|
|
424
|
+
res.on("error", (e) => resolve({ streams: [], error: `Stream-Fehler: ${e.message}` }));
|
|
393
425
|
});
|
|
394
|
-
|
|
426
|
+
httpReq.on("error", (e) => resolve({ streams: [], error: `Verbindungsfehler: ${e.message}` }));
|
|
395
427
|
});
|
|
428
|
+
const timeout = new Promise(
|
|
429
|
+
(resolve) => this.setTimeout(() => {
|
|
430
|
+
httpReq == null ? void 0 : httpReq.destroy();
|
|
431
|
+
resolve({ streams: [], error: `Timeout beim Abrufen von ${target.toString()}` });
|
|
432
|
+
}, 3e3)
|
|
433
|
+
);
|
|
434
|
+
return Promise.race([fetch, timeout]);
|
|
396
435
|
}
|
|
397
436
|
onStateChange(id, state) {
|
|
398
437
|
if (!state || state.ack !== false) {
|
|
@@ -1119,93 +1158,103 @@ class AgentDvr extends utils.Adapter {
|
|
|
1119
1158
|
// ---- main poll ----
|
|
1120
1159
|
async poll() {
|
|
1121
1160
|
var _a, _b;
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
if (!res.ok || !json) {
|
|
1125
|
-
await this.setStateAsync("info.connection", false, true);
|
|
1126
|
-
await this.setStateAsync("system.online", { val: false, ack: true });
|
|
1127
|
-
this.log.warn(`AgentDVR unreachable: ${res.error || "invalid response"}`);
|
|
1161
|
+
if (this.pollBusy) {
|
|
1162
|
+
this.log.debug("poll() skipped \u2014 previous run still in progress");
|
|
1128
1163
|
return;
|
|
1129
1164
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1140
|
-
const devices = findDevices(json);
|
|
1141
|
-
await this.setStateAsync("system.cameraCount", { val: devices.filter((d) => d.ot === 2).length, ack: true });
|
|
1142
|
-
const activeFolders = new Set(devices.map((d) => this.deviceFolder(d)));
|
|
1143
|
-
for (const d of devices) {
|
|
1144
|
-
await this.buildDevice(d);
|
|
1145
|
-
}
|
|
1146
|
-
await this.pruneStaleDevices(activeFolders);
|
|
1147
|
-
const stats = asJson((await this.apiGet("/command/getSystemStats")).data);
|
|
1148
|
-
if (stats) {
|
|
1149
|
-
await this.flattenWrite(stats, "system.stats", 0);
|
|
1150
|
-
const gb = parseSizeGb(stats.disk_free);
|
|
1151
|
-
if (gb !== null) {
|
|
1152
|
-
await this.writeLeaf("system.disk_free_gb", gb);
|
|
1165
|
+
this.pollBusy = true;
|
|
1166
|
+
try {
|
|
1167
|
+
const res = await this.apiGet("/command/getObjects");
|
|
1168
|
+
const json = asJson(res.data);
|
|
1169
|
+
if (!res.ok || !json) {
|
|
1170
|
+
await this.setStateAsync("info.connection", false, true);
|
|
1171
|
+
await this.setStateAsync("system.online", { val: false, ack: true });
|
|
1172
|
+
this.log.warn(`AgentDVR unreachable: ${res.error || "invalid response"}`);
|
|
1173
|
+
return;
|
|
1153
1174
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1175
|
+
await this.setStateAsync("info.connection", true, true);
|
|
1176
|
+
await this.setStateAsync("system.online", { val: true, ack: true });
|
|
1177
|
+
if (json.settings) {
|
|
1178
|
+
await this.flattenWrite(json.settings, "system.settings", 0);
|
|
1179
|
+
}
|
|
1180
|
+
if (this.config.storeRawJson) {
|
|
1181
|
+
const clean = { ...json };
|
|
1182
|
+
delete clean.icons;
|
|
1183
|
+
await this.writeLeaf("system.raw_getObjects", JSON.stringify(clean).slice(0, 6e4));
|
|
1184
|
+
}
|
|
1185
|
+
const devices = findDevices(json);
|
|
1186
|
+
this.lastCamNames = devices.filter((d) => d.ot === 2).map((d) => ({ key: this.deviceFolder(d), name: d.name }));
|
|
1187
|
+
await this.setStateAsync("system.cameraCount", { val: devices.filter((d) => d.ot === 2).length, ack: true });
|
|
1188
|
+
const activeFolders = new Set(devices.map((d) => this.deviceFolder(d)));
|
|
1189
|
+
for (const d of devices) {
|
|
1190
|
+
await this.buildDevice(d);
|
|
1191
|
+
}
|
|
1192
|
+
await this.pruneStaleDevices(activeFolders);
|
|
1193
|
+
const stats = asJson((await this.apiGet("/command/getSystemStats")).data);
|
|
1194
|
+
if (stats) {
|
|
1195
|
+
await this.flattenWrite(stats, "system.stats", 0);
|
|
1196
|
+
const gb = parseSizeGb(stats.disk_free);
|
|
1197
|
+
if (gb !== null) {
|
|
1198
|
+
await this.writeLeaf("system.disk_free_gb", gb);
|
|
1173
1199
|
}
|
|
1174
1200
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1201
|
+
const status = asJson((await this.apiGet("/command/getStatus")).data);
|
|
1202
|
+
if (status) {
|
|
1203
|
+
await this.flattenWrite(status, "system.status", 0);
|
|
1204
|
+
}
|
|
1205
|
+
if (this.config.enableSystemControls && Array.isArray(json.profiles)) {
|
|
1206
|
+
const states = {};
|
|
1207
|
+
let activeInd = null;
|
|
1208
|
+
for (const p of json.profiles) {
|
|
1209
|
+
if (p && typeof p === "object") {
|
|
1210
|
+
const po = p;
|
|
1211
|
+
const ind = (_b = (_a = po.id) != null ? _a : po.ind) != null ? _b : po.index;
|
|
1212
|
+
const pname = po.name;
|
|
1213
|
+
if (typeof ind === "number" && (typeof pname === "string" || typeof pname === "number")) {
|
|
1214
|
+
states[ind] = String(pname);
|
|
1215
|
+
if (po.active === true) {
|
|
1216
|
+
activeInd = ind;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1181
1220
|
}
|
|
1182
|
-
if (
|
|
1183
|
-
|
|
1221
|
+
if (Object.keys(states).length > 0) {
|
|
1222
|
+
const sig = JSON.stringify(states);
|
|
1223
|
+
if (sig !== this.profileSig) {
|
|
1224
|
+
this.profileSig = sig;
|
|
1225
|
+
await this.extendObjectAsync("system.profile.selector", { common: { states } });
|
|
1226
|
+
await this.setStateAsync("system.profile.list", { val: sig, ack: true });
|
|
1227
|
+
}
|
|
1228
|
+
if (activeInd !== null) {
|
|
1229
|
+
await this.setStateAsync("system.profile.selector", { val: activeInd, ack: true });
|
|
1230
|
+
}
|
|
1184
1231
|
}
|
|
1185
1232
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1233
|
+
if (this.config.enableOverview) {
|
|
1234
|
+
const cams = devices.filter((d) => d.ot === 2);
|
|
1235
|
+
const ovId = "overview";
|
|
1236
|
+
if (!this.ensuredFolders.has(ovId)) {
|
|
1237
|
+
await this.setObjectNotExistsAsync(ovId, {
|
|
1238
|
+
type: "state",
|
|
1239
|
+
common: {
|
|
1240
|
+
name: "Overview (all cameras)",
|
|
1241
|
+
type: "string",
|
|
1242
|
+
role: "html",
|
|
1243
|
+
read: true,
|
|
1244
|
+
write: false,
|
|
1245
|
+
def: ""
|
|
1246
|
+
},
|
|
1247
|
+
native: {}
|
|
1248
|
+
});
|
|
1249
|
+
this.ensuredFolders.add(ovId);
|
|
1250
|
+
}
|
|
1251
|
+
await this.setStateAsync(ovId, { val: this.buildOverviewHtml(cams), ack: true });
|
|
1204
1252
|
}
|
|
1205
|
-
await this.setStateAsync(
|
|
1253
|
+
await this.setStateAsync("system.lastUpdate", { val: (/* @__PURE__ */ new Date()).toISOString(), ack: true });
|
|
1254
|
+
await this.setStateAsync("system.lastPoll", { val: Date.now(), ack: true });
|
|
1255
|
+
} finally {
|
|
1256
|
+
this.pollBusy = false;
|
|
1206
1257
|
}
|
|
1207
|
-
await this.setStateAsync("system.lastUpdate", { val: (/* @__PURE__ */ new Date()).toISOString(), ack: true });
|
|
1208
|
-
await this.setStateAsync("system.lastPoll", { val: Date.now(), ack: true });
|
|
1209
1258
|
}
|
|
1210
1259
|
scheduleRefresh() {
|
|
1211
1260
|
if (this.refreshTimer !== void 0) {
|