iobroker.agent-dvr 0.0.4 → 0.0.6
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 +51 -0
- package/README.md +51 -0
- package/admin/custom/go2rtcMapping.js +169 -0
- package/admin/i18n/de.json +97 -1
- package/admin/i18n/en.json +97 -1
- package/admin/i18n/es.json +97 -1
- package/admin/i18n/fr.json +97 -1
- package/admin/i18n/it.json +97 -1
- package/admin/i18n/nl.json +97 -1
- package/admin/i18n/pl.json +97 -1
- package/admin/i18n/pt.json +97 -1
- package/admin/i18n/ru.json +97 -1
- package/admin/i18n/uk.json +97 -1
- package/admin/i18n/zh-cn.json +97 -1
- package/admin/jsonConfig.json +643 -35
- package/build/main.js +194 -12
- package/build/main.js.map +2 -2
- package/io-package.json +77 -7
- package/lib/web.js +296 -0
- package/package.json +2 -1
- package/www/index.html +1262 -0
package/build/main.js
CHANGED
|
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
var utils = __toESM(require("@iobroker/adapter-core"));
|
|
25
25
|
var http = __toESM(require("node:http"));
|
|
26
|
+
var https = __toESM(require("node:https"));
|
|
26
27
|
var import_widget_i18n = require("./lib/widget-i18n");
|
|
27
28
|
const CAM_COMMANDS = [
|
|
28
29
|
{ id: "record", path: "/command/record", params: ["oid", "ot"], name: "Start recording" },
|
|
@@ -181,11 +182,22 @@ function findDevices(json) {
|
|
|
181
182
|
}
|
|
182
183
|
return out;
|
|
183
184
|
}
|
|
184
|
-
function
|
|
185
|
-
|
|
185
|
+
function advCssVars(c) {
|
|
186
|
+
const cardBg = (c == null ? void 0 : c.cardBg) || "rgba(127,127,127,.15)";
|
|
187
|
+
const tagBg = (c == null ? void 0 : c.tagBg) || "rgba(0,0,0,.55)";
|
|
188
|
+
const tagText = (c == null ? void 0 : c.tagText) || "#fff";
|
|
189
|
+
const accent = (c == null ? void 0 : c.accent) || "#8ab4f8";
|
|
190
|
+
const modalBg = (c == null ? void 0 : c.modalBg) || "#1c1c1e";
|
|
191
|
+
const radius = (c == null ? void 0 : c.radius) != null ? `${c.radius}px` : "8px";
|
|
192
|
+
return `--adv-card-bg:${cardBg};--adv-tag-bg:${tagBg};--adv-tag-color:${tagText};--adv-accent:${accent};--adv-modal-bg:${modalBg};--adv-radius:${radius};`;
|
|
186
193
|
}
|
|
187
|
-
function
|
|
188
|
-
|
|
194
|
+
function galleryCss(minCol, maxW, compact = false, colors) {
|
|
195
|
+
const gap = compact ? 6 : 12;
|
|
196
|
+
return `.advgrid{${advCssVars(colors)}display:grid;grid-template-columns:repeat(auto-fill,minmax(${minCol}px,1fr));gap:${gap}px;max-width:100%}.advgrid .advlb{display:none}.advcell{display:flex;flex-direction:column;cursor:pointer;text-decoration:none;color:inherit;font-family:inherit}.advimg{position:relative;display:block;border-radius:var(--adv-radius);overflow:hidden;background:var(--adv-card-bg)}.advimg img{width:100%;height:auto;display:block}.advimgfix img{height:100%;object-fit:cover}.advtag{position:absolute;background:var(--adv-tag-bg);color:var(--adv-tag-color);font-size:.6rem;padding:2px 4px;border-radius:3px;line-height:1;max-width:85%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.advplay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .15s;background:rgba(0,0,0,.2)}.advplay::before{content:"";border-style:solid;border-width:11px 0 11px 18px;border-color:transparent transparent transparent #fff;filter:drop-shadow(0 1px 2px rgba(0,0,0,.6))}.advcell:hover .advplay{opacity:1}.advcap{font-size:${compact ? ".65rem" : ".72rem"};margin-top:${compact ? 2 : 4}px;line-height:1.25;opacity:.85}.advmodal{display:none;position:fixed;inset:0;z-index:99999;align-items:center;justify-content:center}.advgrid .advlb:checked + .advthumb + .advmodal{display:flex}.advbackdrop{position:absolute;inset:0;background:transparent;cursor:pointer}.advbox{position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;max-width:min(92vw,${maxW}px);background:var(--adv-modal-bg);padding:12px;border-radius:14px;box-shadow:0 16px 48px rgba(0,0,0,.55),0 0 0 1px rgba(255,255,255,.08)}.advvideo{width:100%;max-height:80vh;border-radius:8px;background:#000;display:block}.advclose{position:absolute;top:18px;right:18px;width:32px;height:32px;border-radius:50%;background:rgba(0,0,0,.65);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:15px;z-index:2;border:2px solid rgba(255,255,255,.35)}.advinfo{color:#fff;font-size:.8rem;text-align:center}.advinfo a{color:var(--adv-accent)}.advempty{padding:24px;text-align:center;opacity:.7;font-family:inherit}`;
|
|
197
|
+
}
|
|
198
|
+
function galleryCssJs(minCol, compact = false, colors) {
|
|
199
|
+
const gap = compact ? 6 : 12;
|
|
200
|
+
return `.advroot{${advCssVars(colors)}font-family:inherit;color:inherit}.advbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:10px}.advsearchjs{flex:1 1 160px;min-width:120px;padding:6px 10px;border-radius:8px;border:1px solid rgba(127,127,127,.4);background:rgba(127,127,127,.08);color:inherit;font:inherit}.advtagsjs{display:flex;flex-wrap:wrap;gap:6px}.advtagbtn{padding:4px 10px;border-radius:999px;border:1px solid rgba(127,127,127,.4);background:transparent;color:inherit;cursor:pointer;font:inherit;font-size:.8rem}.advtagbtn.on{background:var(--adv-accent);color:#fff;border-color:var(--adv-accent)}.advgridjs{display:grid;grid-template-columns:repeat(auto-fill,minmax(${minCol}px,1fr));gap:${gap}px}.advcelljs{display:flex;flex-direction:column;cursor:pointer}.advimgjs{position:relative;display:block;border-radius:var(--adv-radius);overflow:hidden;background:var(--adv-card-bg)}.advimgjs img{width:100%;height:auto;display:block}.advimgjs.advimgfix img{height:100%;object-fit:cover}.advtagjs{position:absolute;background:var(--adv-tag-bg);color:var(--adv-tag-color);font-size:.6rem;padding:2px 4px;border-radius:3px;line-height:1;max-width:85%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.advplayjs{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .15s;background:rgba(0,0,0,.2)}.advplayjs::before{content:"";border-style:solid;border-width:11px 0 11px 18px;border-color:transparent transparent transparent #fff;filter:drop-shadow(0 1px 2px rgba(0,0,0,.6))}.advcelljs:hover .advplayjs{opacity:1}.advcapjs{font-size:${compact ? ".65rem" : ".72rem"};margin-top:${compact ? 2 : 4}px;line-height:1.25;opacity:.85}.advemptyjs{padding:24px;text-align:center;opacity:.7}`;
|
|
189
201
|
}
|
|
190
202
|
const ADV_CLIENT_CODE = `
|
|
191
203
|
(function(){
|
|
@@ -226,7 +238,7 @@ function initRoot(root){
|
|
|
226
238
|
var D;try{D=JSON.parse(dataEl.textContent);}catch(e){return;}
|
|
227
239
|
var cfg=D.cfg||{},items=D.items||[],live=D.live||null;
|
|
228
240
|
var grid=root.querySelector('.advgridjs'),search=root.querySelector('.advsearchjs'),tagsBox=root.querySelector('.advtagsjs');
|
|
229
|
-
var state={q:'',tag:''};
|
|
241
|
+
var state={q:'',tag:cfg.default_tag||''};
|
|
230
242
|
var tagset={};
|
|
231
243
|
items.forEach(function(it){String(it.tag||'').split(/[,\\s]+/).forEach(function(t){if(t)tagset[t]=1;});});
|
|
232
244
|
var tags=Object.keys(tagset);
|
|
@@ -287,12 +299,23 @@ class AgentDvr extends utils.Adapter {
|
|
|
287
299
|
super({ ...options, name: "agent-dvr" });
|
|
288
300
|
this.on("ready", this.onReady.bind(this));
|
|
289
301
|
this.on("stateChange", this.onStateChange.bind(this));
|
|
302
|
+
this.on("message", this.onMessage.bind(this));
|
|
290
303
|
this.on("unload", this.onUnload.bind(this));
|
|
291
304
|
}
|
|
292
305
|
// ---- lifecycle ----
|
|
306
|
+
async ensureWebInstance() {
|
|
307
|
+
var _a;
|
|
308
|
+
const instanceId = `system.adapter.${this.namespace}`;
|
|
309
|
+
const obj = await this.getForeignObjectAsync(instanceId);
|
|
310
|
+
if (obj && !((_a = obj.native) == null ? void 0 : _a.webInstance)) {
|
|
311
|
+
await this.extendForeignObjectAsync(instanceId, { native: { webInstance: "*" } });
|
|
312
|
+
this.log.info("webInstance set \u2014 web adapter will reload the extension");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
293
315
|
async onReady() {
|
|
294
316
|
var _a;
|
|
295
317
|
void this.setState("info.connection", false, true);
|
|
318
|
+
await this.ensureWebInstance();
|
|
296
319
|
const sysConfig = await this.getForeignObjectAsync("system.config");
|
|
297
320
|
const rawLang = (_a = sysConfig == null ? void 0 : sysConfig.common) == null ? void 0 : _a.language;
|
|
298
321
|
this.wt = (0, import_widget_i18n.getWidgetLabels)(rawLang != null ? rawLang : "en");
|
|
@@ -328,6 +351,49 @@ class AgentDvr extends utils.Adapter {
|
|
|
328
351
|
}
|
|
329
352
|
callback();
|
|
330
353
|
}
|
|
354
|
+
onMessage(obj) {
|
|
355
|
+
if (!obj || obj.command !== "getGo2rtcStreams") {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
void this.fetchGo2rtcStreams().then((streams) => {
|
|
359
|
+
this.sendTo(obj.from, obj.command, { streams }, obj.callback);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
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
|
+
}
|
|
376
|
+
const mod = target.protocol === "https:" ? https : http;
|
|
377
|
+
const req = mod.get(target.toString(), (res) => {
|
|
378
|
+
let body = "";
|
|
379
|
+
res.on("data", (c) => {
|
|
380
|
+
body += c.toString();
|
|
381
|
+
});
|
|
382
|
+
res.on("end", () => {
|
|
383
|
+
try {
|
|
384
|
+
resolve(Object.keys(JSON.parse(body) || {}));
|
|
385
|
+
} catch {
|
|
386
|
+
resolve([]);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
res.on("error", () => resolve([]));
|
|
390
|
+
});
|
|
391
|
+
req.setTimeout(4e3, () => {
|
|
392
|
+
req.destroy();
|
|
393
|
+
});
|
|
394
|
+
req.on("error", () => resolve([]));
|
|
395
|
+
});
|
|
396
|
+
}
|
|
331
397
|
onStateChange(id, state) {
|
|
332
398
|
if (!state || state.ack !== false) {
|
|
333
399
|
return;
|
|
@@ -607,6 +673,36 @@ class AgentDvr extends utils.Adapter {
|
|
|
607
673
|
deviceFolder(d) {
|
|
608
674
|
return `${d.ot === 1 ? "mic" : "cam"}_${sanitize(d.oid)}_${sanitize(d.name)}`;
|
|
609
675
|
}
|
|
676
|
+
// ---- device status data points ----
|
|
677
|
+
STATUS_DPS = [
|
|
678
|
+
{ key: "recording", role: "indicator.working", name: "Active recording" },
|
|
679
|
+
{ key: "online", role: "indicator.reachable", name: "Online" },
|
|
680
|
+
{ key: "connected", role: "indicator.connected", name: "Stream connected" },
|
|
681
|
+
{ key: "detected", role: "indicator", name: "Motion detected" },
|
|
682
|
+
{ key: "alerted", role: "indicator.alarm", name: "Alert active" }
|
|
683
|
+
];
|
|
684
|
+
async writeStatusDps(d, fid) {
|
|
685
|
+
const data = d.raw.data;
|
|
686
|
+
if (!data || typeof data !== "object") {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const sfid = `${fid}.status`;
|
|
690
|
+
if (!this.ensuredFolders.has(sfid)) {
|
|
691
|
+
await this.ensureFolder(sfid, "Status", "channel");
|
|
692
|
+
}
|
|
693
|
+
for (const dp of this.STATUS_DPS) {
|
|
694
|
+
if (!(dp.key in data)) {
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
const id = `${sfid}.${dp.key}`;
|
|
698
|
+
await this.setObjectNotExistsAsync(id, {
|
|
699
|
+
type: "state",
|
|
700
|
+
common: { name: dp.name, type: "boolean", role: dp.role, read: true, write: false, def: false },
|
|
701
|
+
native: {}
|
|
702
|
+
});
|
|
703
|
+
await this.setStateAsync(id, { val: !!data[dp.key], ack: true });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
610
706
|
// ---- build device data points ----
|
|
611
707
|
async buildDevice(d) {
|
|
612
708
|
const fid = this.deviceFolder(d);
|
|
@@ -616,6 +712,7 @@ class AgentDvr extends utils.Adapter {
|
|
|
616
712
|
await this.detectAspect(d);
|
|
617
713
|
}
|
|
618
714
|
await this.flattenWrite(d.raw, fid, 0);
|
|
715
|
+
await this.writeStatusDps(d, fid);
|
|
619
716
|
await this.ensureFolder(`${fid}.control`, "Control", "channel");
|
|
620
717
|
for (const c of CAM_COMMANDS) {
|
|
621
718
|
if (c.id === "snapshot" && d.ot !== 2) {
|
|
@@ -701,19 +798,52 @@ class AgentDvr extends utils.Adapter {
|
|
|
701
798
|
return { fn, sizeMB, dur, tag, date, time, thumb, video };
|
|
702
799
|
}
|
|
703
800
|
// ---- gallery HTML builders ----
|
|
801
|
+
effectiveMinCol() {
|
|
802
|
+
const sizeMap = { small: 120, medium: 180, large: 240 };
|
|
803
|
+
const ts = this.config.widgetThumbSize;
|
|
804
|
+
return ts && sizeMap[ts] ? sizeMap[ts] : this.config.widgetMinCol || 150;
|
|
805
|
+
}
|
|
806
|
+
async pruneStaleDevices(activeFolders) {
|
|
807
|
+
const allObjs = await this.getAdapterObjectsAsync();
|
|
808
|
+
const stale = [];
|
|
809
|
+
for (const fullId of Object.keys(allObjs)) {
|
|
810
|
+
const obj = allObjs[fullId];
|
|
811
|
+
if (obj.type !== "device") {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const rel = fullId.slice(this.namespace.length + 1);
|
|
815
|
+
if (/^(cam|mic)_/.test(rel) && !activeFolders.has(rel)) {
|
|
816
|
+
stale.push(rel);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
for (const rel of stale) {
|
|
820
|
+
this.log.info(`[agent-dvr] removing stale device: ${rel}`);
|
|
821
|
+
await this.delObjectAsync(rel, { recursive: true });
|
|
822
|
+
for (const [oid, d] of this.devById) {
|
|
823
|
+
if (this.deviceFolder(d) === rel) {
|
|
824
|
+
this.devById.delete(oid);
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
sortedEvents(events) {
|
|
831
|
+
return this.config.widgetSortNewest === false ? [...events].reverse() : events;
|
|
832
|
+
}
|
|
704
833
|
buildGalleryHtml(d, events) {
|
|
705
834
|
return (this.config.widgetMode || "nojs") === "js" ? this.buildGalleryHtmlJs(d, events) : this.buildGalleryHtmlNojs(d, events);
|
|
706
835
|
}
|
|
707
836
|
buildGalleryHtmlNojs(d, events) {
|
|
708
837
|
const oid = d.oid;
|
|
709
|
-
const minCol = this.
|
|
838
|
+
const minCol = this.effectiveMinCol();
|
|
710
839
|
const maxW = this.config.widgetMaxModalWidth || 900;
|
|
711
840
|
const showTags = this.config.widgetShowTags;
|
|
712
841
|
const pos = (this.config.widgetTagPosition || "bottom-left").split("-");
|
|
713
842
|
const tagStyle = `${pos[0]}:5px;${pos[1]}:5px`;
|
|
714
843
|
const playerUrl = (this.config.widgetPlayerUrl || "").trim();
|
|
844
|
+
const compact = !!this.config.widgetCompact;
|
|
715
845
|
const PAUSE_ATTR = ` onchange="if(!this.checked){var m=this.nextElementSibling.nextElementSibling,v=m&&m.querySelector('video');if(v){v.pause();}}"`;
|
|
716
|
-
const items = events.map((ev, i) => {
|
|
846
|
+
const items = this.sortedEvents(events).map((ev, i) => {
|
|
717
847
|
const p = this.fmtEvent(ev, oid);
|
|
718
848
|
const id = `adv${sanitize(oid)}_${i}`;
|
|
719
849
|
const badge = showTags && p.tag ? `<span class="advtag" style="${tagStyle}">${escHtml(p.tag)}</span>` : "";
|
|
@@ -724,14 +854,26 @@ class AgentDvr extends utils.Adapter {
|
|
|
724
854
|
}
|
|
725
855
|
return `<input class="advlb" type="checkbox" id="${id}"${PAUSE_ATTR}><label class="advcell advthumb" for="${id}">${inner}</label><div class="advmodal"><label class="advbackdrop" for="${id}"></label><div class="advbox"><label class="advclose" for="${id}">✕</label><video class="advvideo" controls preload="none" playsinline src="${p.video}"></video><div class="advinfo">${escHtml(p.date)} ${escHtml(p.time)} · ${escHtml(p.dur)}s · ${escHtml(p.sizeMB)} MB${p.tag ? ` · ${escHtml(p.tag)}` : ""} · <a href="${p.video}" download target="_blank">${this.wt.download}</a></div></div></div>`;
|
|
726
856
|
}).join("");
|
|
727
|
-
const
|
|
728
|
-
|
|
857
|
+
const gridClass = compact ? "advgrid advcompact" : "advgrid";
|
|
858
|
+
const grid = items ? `<div class="${gridClass}">${items}</div>` : `<div class="advempty">${this.wt.noRecordings}</div>`;
|
|
859
|
+
const colors = {
|
|
860
|
+
cardBg: this.config.widgetColorCardBg || void 0,
|
|
861
|
+
tagBg: this.config.widgetColorTagBg || void 0,
|
|
862
|
+
tagText: this.config.widgetColorTagText || void 0,
|
|
863
|
+
accent: this.config.widgetColorAccent || void 0,
|
|
864
|
+
modalBg: this.config.widgetColorModalBg || void 0,
|
|
865
|
+
radius: this.config.widgetBorderRadius
|
|
866
|
+
};
|
|
867
|
+
return `<style>${galleryCss(minCol, maxW, compact, colors)}</style>${grid}`;
|
|
729
868
|
}
|
|
730
869
|
buildGalleryHtmlJs(d, events) {
|
|
731
870
|
const oid = d.oid;
|
|
732
|
-
const minCol = this.
|
|
871
|
+
const minCol = this.effectiveMinCol();
|
|
733
872
|
const maxW = this.config.widgetMaxModalWidth || 900;
|
|
734
|
-
const
|
|
873
|
+
const showSearch = this.config.widgetShowSearch !== false;
|
|
874
|
+
const compact = !!this.config.widgetCompact;
|
|
875
|
+
const defaultTag = (this.config.widgetDefaultTag || "").trim();
|
|
876
|
+
const items = this.sortedEvents(events).map((ev) => {
|
|
735
877
|
const p = this.fmtEvent(ev, oid);
|
|
736
878
|
return {
|
|
737
879
|
fn: p.fn,
|
|
@@ -751,11 +893,22 @@ class AgentDvr extends utils.Adapter {
|
|
|
751
893
|
live_aspect: this.camAspect[oid] || this.config.widgetLiveAspect || "",
|
|
752
894
|
player_url: (this.config.widgetPlayerUrl || "").trim(),
|
|
753
895
|
tag_position: this.config.widgetTagPosition || "bottom-left",
|
|
896
|
+
compact,
|
|
897
|
+
default_tag: defaultTag,
|
|
754
898
|
labels: { noRecordings: this.wt.noRecordings, all: this.wt.all, live: this.wt.live }
|
|
755
899
|
};
|
|
756
900
|
const data = JSON.stringify({ items, live: null, cfg }).replace(/</g, "\\u003c");
|
|
757
901
|
const boot = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
758
|
-
|
|
902
|
+
const searchHtml = showSearch ? `<input class="advsearchjs" type="text" placeholder="${escHtml(this.wt.search)}">` : "";
|
|
903
|
+
const colors = {
|
|
904
|
+
cardBg: this.config.widgetColorCardBg || void 0,
|
|
905
|
+
tagBg: this.config.widgetColorTagBg || void 0,
|
|
906
|
+
tagText: this.config.widgetColorTagText || void 0,
|
|
907
|
+
accent: this.config.widgetColorAccent || void 0,
|
|
908
|
+
modalBg: this.config.widgetColorModalBg || void 0,
|
|
909
|
+
radius: this.config.widgetBorderRadius
|
|
910
|
+
};
|
|
911
|
+
return `<style>${galleryCssJs(minCol, compact, colors)}</style><div class="advroot"><script type="application/json" class="advdata">${data}</script><div class="advbar">${searchHtml}<div class="advtagsjs"></div></div><div class="advgridjs"></div></div><script type="text/plain" class="advcode">${ADV_CLIENT_CODE}</script><img alt="" src="${boot}" style="display:none" onload="(function(){if(window.ADVscan){window.ADVscan();return;}var c=document.querySelector('script.advcode');if(!c)return;var s=document.createElement('script');s.textContent=c.textContent;document.body.appendChild(s);})()">`;
|
|
759
912
|
}
|
|
760
913
|
buildOverviewHtml(cams) {
|
|
761
914
|
const ts = Date.now();
|
|
@@ -801,6 +954,33 @@ class AgentDvr extends utils.Adapter {
|
|
|
801
954
|
await this.writeLeaf(`${fid}.events.last.thumb`, p.thumb);
|
|
802
955
|
await this.writeLeaf(`${fid}.events.last.video`, p.video);
|
|
803
956
|
}
|
|
957
|
+
const jsonId = `${fid}.events.json`;
|
|
958
|
+
if (!this.ensuredFolders.has(jsonId)) {
|
|
959
|
+
await this.ensurePath(jsonId);
|
|
960
|
+
await this.setObjectNotExistsAsync(jsonId, {
|
|
961
|
+
type: "state",
|
|
962
|
+
common: { name: "Recordings list (web UI)", type: "string", role: "json", read: true, write: false },
|
|
963
|
+
native: {}
|
|
964
|
+
});
|
|
965
|
+
this.ensuredFolders.add(jsonId);
|
|
966
|
+
}
|
|
967
|
+
await this.setStateAsync(jsonId, {
|
|
968
|
+
val: JSON.stringify(
|
|
969
|
+
events.slice(0, Math.max(1, this.config.widgetAnzahl || 50)).map((ev) => {
|
|
970
|
+
const p = this.fmtEvent(ev, oid);
|
|
971
|
+
return {
|
|
972
|
+
date: p.date,
|
|
973
|
+
time: p.time,
|
|
974
|
+
dur: p.dur,
|
|
975
|
+
size: p.sizeMB,
|
|
976
|
+
tag: p.tag,
|
|
977
|
+
thumb: p.thumb,
|
|
978
|
+
video: p.video
|
|
979
|
+
};
|
|
980
|
+
})
|
|
981
|
+
),
|
|
982
|
+
ack: true
|
|
983
|
+
});
|
|
804
984
|
await this.ensureFlag(`${fid}.events.new`, "New event");
|
|
805
985
|
if (this.config.enablePush) {
|
|
806
986
|
const pid = `${fid}.events.pushTrigger`;
|
|
@@ -959,9 +1139,11 @@ class AgentDvr extends utils.Adapter {
|
|
|
959
1139
|
}
|
|
960
1140
|
const devices = findDevices(json);
|
|
961
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)));
|
|
962
1143
|
for (const d of devices) {
|
|
963
1144
|
await this.buildDevice(d);
|
|
964
1145
|
}
|
|
1146
|
+
await this.pruneStaleDevices(activeFolders);
|
|
965
1147
|
const stats = asJson((await this.apiGet("/command/getSystemStats")).data);
|
|
966
1148
|
if (stats) {
|
|
967
1149
|
await this.flattenWrite(stats, "system.stats", 0);
|