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/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 || obj.command !== "getGo2rtcStreams") {
357
+ if (!obj) {
356
358
  return;
357
359
  }
358
- void this.fetchGo2rtcStreams().then((streams) => {
359
- this.sendTo(obj.from, obj.command, { streams }, obj.callback);
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
- fetchGo2rtcStreams() {
363
- return new Promise((resolve) => {
364
- const url = this.config.go2rtcUrl;
365
- if (!url) {
366
- resolve([]);
367
- return;
368
- }
369
- let target;
370
- try {
371
- target = new URL("/api/streams", url);
372
- } catch {
373
- resolve([]);
374
- return;
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
- const req = mod.get(target.toString(), (res) => {
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
- resolve(Object.keys(JSON.parse(body) || {}));
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
- req.on("error", () => resolve([]));
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
- const res = await this.apiGet("/command/getObjects");
1123
- const json = asJson(res.data);
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
- await this.setStateAsync("info.connection", true, true);
1131
- await this.setStateAsync("system.online", { val: true, ack: true });
1132
- if (json.settings) {
1133
- await this.flattenWrite(json.settings, "system.settings", 0);
1134
- }
1135
- if (this.config.storeRawJson) {
1136
- const clean = { ...json };
1137
- delete clean.icons;
1138
- await this.writeLeaf("system.raw_getObjects", JSON.stringify(clean).slice(0, 6e4));
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
- const status = asJson((await this.apiGet("/command/getStatus")).data);
1156
- if (status) {
1157
- await this.flattenWrite(status, "system.status", 0);
1158
- }
1159
- if (this.config.enableSystemControls && Array.isArray(json.profiles)) {
1160
- const states = {};
1161
- let activeInd = null;
1162
- for (const p of json.profiles) {
1163
- if (p && typeof p === "object") {
1164
- const po = p;
1165
- const ind = (_b = (_a = po.id) != null ? _a : po.ind) != null ? _b : po.index;
1166
- const pname = po.name;
1167
- if (typeof ind === "number" && (typeof pname === "string" || typeof pname === "number")) {
1168
- states[ind] = String(pname);
1169
- if (po.active === true) {
1170
- activeInd = ind;
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
- if (Object.keys(states).length > 0) {
1176
- const sig = JSON.stringify(states);
1177
- if (sig !== this.profileSig) {
1178
- this.profileSig = sig;
1179
- await this.extendObjectAsync("system.profile.selector", { common: { states } });
1180
- await this.setStateAsync("system.profile.list", { val: sig, ack: true });
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 (activeInd !== null) {
1183
- await this.setStateAsync("system.profile.selector", { val: activeInd, ack: true });
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
- if (this.config.enableOverview) {
1188
- const cams = devices.filter((d) => d.ot === 2);
1189
- const ovId = "overview";
1190
- if (!this.ensuredFolders.has(ovId)) {
1191
- await this.setObjectNotExistsAsync(ovId, {
1192
- type: "state",
1193
- common: {
1194
- name: "Overview (all cameras)",
1195
- type: "string",
1196
- role: "html",
1197
- read: true,
1198
- write: false,
1199
- def: ""
1200
- },
1201
- native: {}
1202
- });
1203
- this.ensuredFolders.add(ovId);
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(ovId, { val: this.buildOverviewHtml(cams), ack: true });
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) {