iobroker.agent-dvr 0.1.0 → 0.2.1
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 +294 -175
- package/README.md +231 -94
- package/admin/assets/index-DD1mpQy7.js +685 -0
- package/admin/i18n/de.json +5 -7
- package/admin/i18n/en.json +133 -135
- package/admin/i18n/es.json +5 -7
- package/admin/i18n/fr.json +5 -7
- package/admin/i18n/it.json +5 -7
- package/admin/i18n/nl.json +5 -7
- package/admin/i18n/pl.json +5 -7
- package/admin/i18n/pt.json +5 -7
- package/admin/i18n/ru.json +5 -7
- package/admin/i18n/uk.json +5 -7
- package/admin/i18n/zh-cn.json +5 -7
- package/admin/index_m.html +27 -0
- package/build/main.js +155 -103
- package/build/main.js.map +2 -2
- package/io-package.json +29 -30
- 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,87 @@ 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: "No sources found (adapter has not polled yet)" });
|
|
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 not configured" });
|
|
397
|
+
}
|
|
398
|
+
let target;
|
|
399
|
+
try {
|
|
400
|
+
target = new URL("/api/streams", url);
|
|
401
|
+
} catch {
|
|
402
|
+
return Promise.resolve({ streams: [], error: `Invalid 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 reachable but no streams configured" : void 0
|
|
419
|
+
});
|
|
385
420
|
} catch {
|
|
386
|
-
resolve(
|
|
421
|
+
resolve({
|
|
422
|
+
streams: [],
|
|
423
|
+
error: `Invalid JSON response from go2rtc (HTTP ${res.statusCode})`
|
|
424
|
+
});
|
|
387
425
|
}
|
|
388
426
|
});
|
|
389
|
-
res.on("error", () => resolve([]));
|
|
390
|
-
});
|
|
391
|
-
req.setTimeout(4e3, () => {
|
|
392
|
-
req.destroy();
|
|
427
|
+
res.on("error", (e) => resolve({ streams: [], error: `Stream error: ${e.message}` }));
|
|
393
428
|
});
|
|
394
|
-
|
|
429
|
+
httpReq.on("error", (e) => resolve({ streams: [], error: `Connection error: ${e.message}` }));
|
|
395
430
|
});
|
|
431
|
+
const timeout = new Promise(
|
|
432
|
+
(resolve) => this.setTimeout(() => {
|
|
433
|
+
httpReq == null ? void 0 : httpReq.destroy();
|
|
434
|
+
resolve({ streams: [], error: `Timeout fetching ${target.toString()}` });
|
|
435
|
+
}, 3e3)
|
|
436
|
+
);
|
|
437
|
+
return Promise.race([fetch, timeout]);
|
|
396
438
|
}
|
|
397
439
|
onStateChange(id, state) {
|
|
398
440
|
if (!state || state.ack !== false) {
|
|
@@ -1119,93 +1161,103 @@ class AgentDvr extends utils.Adapter {
|
|
|
1119
1161
|
// ---- main poll ----
|
|
1120
1162
|
async poll() {
|
|
1121
1163
|
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"}`);
|
|
1164
|
+
if (this.pollBusy) {
|
|
1165
|
+
this.log.debug("poll() skipped \u2014 previous run still in progress");
|
|
1128
1166
|
return;
|
|
1129
1167
|
}
|
|
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);
|
|
1168
|
+
this.pollBusy = true;
|
|
1169
|
+
try {
|
|
1170
|
+
const res = await this.apiGet("/command/getObjects");
|
|
1171
|
+
const json = asJson(res.data);
|
|
1172
|
+
if (!res.ok || !json) {
|
|
1173
|
+
await this.setStateAsync("info.connection", false, true);
|
|
1174
|
+
await this.setStateAsync("system.online", { val: false, ack: true });
|
|
1175
|
+
this.log.warn(`AgentDVR unreachable: ${res.error || "invalid response"}`);
|
|
1176
|
+
return;
|
|
1153
1177
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1178
|
+
await this.setStateAsync("info.connection", true, true);
|
|
1179
|
+
await this.setStateAsync("system.online", { val: true, ack: true });
|
|
1180
|
+
if (json.settings) {
|
|
1181
|
+
await this.flattenWrite(json.settings, "system.settings", 0);
|
|
1182
|
+
}
|
|
1183
|
+
if (this.config.storeRawJson) {
|
|
1184
|
+
const clean = { ...json };
|
|
1185
|
+
delete clean.icons;
|
|
1186
|
+
await this.writeLeaf("system.raw_getObjects", JSON.stringify(clean).slice(0, 6e4));
|
|
1187
|
+
}
|
|
1188
|
+
const devices = findDevices(json);
|
|
1189
|
+
this.lastCamNames = devices.filter((d) => d.ot === 2).map((d) => ({ key: this.deviceFolder(d), name: d.name }));
|
|
1190
|
+
await this.setStateAsync("system.cameraCount", { val: devices.filter((d) => d.ot === 2).length, ack: true });
|
|
1191
|
+
const activeFolders = new Set(devices.map((d) => this.deviceFolder(d)));
|
|
1192
|
+
for (const d of devices) {
|
|
1193
|
+
await this.buildDevice(d);
|
|
1194
|
+
}
|
|
1195
|
+
await this.pruneStaleDevices(activeFolders);
|
|
1196
|
+
const stats = asJson((await this.apiGet("/command/getSystemStats")).data);
|
|
1197
|
+
if (stats) {
|
|
1198
|
+
await this.flattenWrite(stats, "system.stats", 0);
|
|
1199
|
+
const gb = parseSizeGb(stats.disk_free);
|
|
1200
|
+
if (gb !== null) {
|
|
1201
|
+
await this.writeLeaf("system.disk_free_gb", gb);
|
|
1173
1202
|
}
|
|
1174
1203
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1204
|
+
const status = asJson((await this.apiGet("/command/getStatus")).data);
|
|
1205
|
+
if (status) {
|
|
1206
|
+
await this.flattenWrite(status, "system.status", 0);
|
|
1207
|
+
}
|
|
1208
|
+
if (this.config.enableSystemControls && Array.isArray(json.profiles)) {
|
|
1209
|
+
const states = {};
|
|
1210
|
+
let activeInd = null;
|
|
1211
|
+
for (const p of json.profiles) {
|
|
1212
|
+
if (p && typeof p === "object") {
|
|
1213
|
+
const po = p;
|
|
1214
|
+
const ind = (_b = (_a = po.id) != null ? _a : po.ind) != null ? _b : po.index;
|
|
1215
|
+
const pname = po.name;
|
|
1216
|
+
if (typeof ind === "number" && (typeof pname === "string" || typeof pname === "number")) {
|
|
1217
|
+
states[ind] = String(pname);
|
|
1218
|
+
if (po.active === true) {
|
|
1219
|
+
activeInd = ind;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1181
1223
|
}
|
|
1182
|
-
if (
|
|
1183
|
-
|
|
1224
|
+
if (Object.keys(states).length > 0) {
|
|
1225
|
+
const sig = JSON.stringify(states);
|
|
1226
|
+
if (sig !== this.profileSig) {
|
|
1227
|
+
this.profileSig = sig;
|
|
1228
|
+
await this.extendObjectAsync("system.profile.selector", { common: { states } });
|
|
1229
|
+
await this.setStateAsync("system.profile.list", { val: sig, ack: true });
|
|
1230
|
+
}
|
|
1231
|
+
if (activeInd !== null) {
|
|
1232
|
+
await this.setStateAsync("system.profile.selector", { val: activeInd, ack: true });
|
|
1233
|
+
}
|
|
1184
1234
|
}
|
|
1185
1235
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1236
|
+
if (this.config.enableOverview) {
|
|
1237
|
+
const cams = devices.filter((d) => d.ot === 2);
|
|
1238
|
+
const ovId = "overview";
|
|
1239
|
+
if (!this.ensuredFolders.has(ovId)) {
|
|
1240
|
+
await this.setObjectNotExistsAsync(ovId, {
|
|
1241
|
+
type: "state",
|
|
1242
|
+
common: {
|
|
1243
|
+
name: "Overview (all cameras)",
|
|
1244
|
+
type: "string",
|
|
1245
|
+
role: "html",
|
|
1246
|
+
read: true,
|
|
1247
|
+
write: false,
|
|
1248
|
+
def: ""
|
|
1249
|
+
},
|
|
1250
|
+
native: {}
|
|
1251
|
+
});
|
|
1252
|
+
this.ensuredFolders.add(ovId);
|
|
1253
|
+
}
|
|
1254
|
+
await this.setStateAsync(ovId, { val: this.buildOverviewHtml(cams), ack: true });
|
|
1204
1255
|
}
|
|
1205
|
-
await this.setStateAsync(
|
|
1256
|
+
await this.setStateAsync("system.lastUpdate", { val: (/* @__PURE__ */ new Date()).toISOString(), ack: true });
|
|
1257
|
+
await this.setStateAsync("system.lastPoll", { val: Date.now(), ack: true });
|
|
1258
|
+
} finally {
|
|
1259
|
+
this.pollBusy = false;
|
|
1206
1260
|
}
|
|
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
1261
|
}
|
|
1210
1262
|
scheduleRefresh() {
|
|
1211
1263
|
if (this.refreshTimer !== void 0) {
|