@camstack/addon-post-analysis 1.2.233 → 1.2.235
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/dist/{dist-yyQ1H5yE.mjs → dist-BDpucx_W.mjs} +220 -11
- package/dist/{dist-Dbf6ceXh.js → dist-IoI_ufBo.js} +220 -11
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BvU7JaBW.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DIQIycqg.mjs} +3 -3
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C5YuStaN.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dl-N-7EJ.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-Dg-LBlut.mjs → hostInit-B5V7K95U.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +281 -5
- package/dist/pipeline-analytics/index.mjs +281 -5
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.189",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.105",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.2.
|
|
84
|
+
version: "1.2.154",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-IoI_ufBo.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -7445,6 +7445,16 @@ var NcAlertStore = class {
|
|
|
7445
7445
|
};
|
|
7446
7446
|
}
|
|
7447
7447
|
};
|
|
7448
|
+
function deriveConsumableBand(input) {
|
|
7449
|
+
const { level, status } = input.reading;
|
|
7450
|
+
const measured = level !== null && Number.isFinite(level);
|
|
7451
|
+
if (!measured && status === null) return null;
|
|
7452
|
+
if (status === "replace") return "low";
|
|
7453
|
+
if (!measured) return "normal";
|
|
7454
|
+
const value = level;
|
|
7455
|
+
if (input.previous === "low") return value >= input.thresholdPercent + 5 ? "normal" : "low";
|
|
7456
|
+
return value <= input.thresholdPercent ? "low" : "normal";
|
|
7457
|
+
}
|
|
7448
7458
|
//#endregion
|
|
7449
7459
|
//#region src/notification-center/device-directory.ts
|
|
7450
7460
|
/**
|
|
@@ -9845,6 +9855,40 @@ function incomingFromBatteryBand(input) {
|
|
|
9845
9855
|
});
|
|
9846
9856
|
}
|
|
9847
9857
|
/**
|
|
9858
|
+
* A consumable BAND, as a system-event subject.
|
|
9859
|
+
*
|
|
9860
|
+
* Built exactly like {@link incomingFromBatteryBand} — the operator asked for
|
|
9861
|
+
* these to be treated as the battery notifications are — with two differences
|
|
9862
|
+
* that come from the subject and not from taste:
|
|
9863
|
+
*
|
|
9864
|
+
* - **the subject carries the ITEM.** `consumable:<deviceId>:<key>`, because a
|
|
9865
|
+
* vacuum has a brush AND a filter AND a mop pad, and one subject for all
|
|
9866
|
+
* three would let the brush's alert cancel the filter's.
|
|
9867
|
+
* - **the body variant follows what is KNOWN.** An item with no percentage
|
|
9868
|
+
* gets a finished sentence rather than a template with a stand-in number in
|
|
9869
|
+
* it, for the same reason a binary battery does.
|
|
9870
|
+
*
|
|
9871
|
+
* The recovery keeps the SAME subject: it is the same episode ending.
|
|
9872
|
+
*/
|
|
9873
|
+
function incomingFromConsumableBand(input) {
|
|
9874
|
+
const kind = input.band === "low" ? "device-consumable-low" : "device-consumable-normal";
|
|
9875
|
+
const measured = input.item.level !== null && Number.isFinite(input.item.level);
|
|
9876
|
+
return systemSubject({
|
|
9877
|
+
facts: input.facts,
|
|
9878
|
+
kind,
|
|
9879
|
+
subject: `consumable:${input.deviceId}:${input.item.key}`,
|
|
9880
|
+
titleKey: `system.${kind}.title`,
|
|
9881
|
+
bodyKey: measured ? `system.${kind}.body` : `system.${kind}.body.unmeasured`,
|
|
9882
|
+
textParams: {
|
|
9883
|
+
...deviceTextParams(input.deviceId, input.identity),
|
|
9884
|
+
item: input.item.label,
|
|
9885
|
+
...measured ? { percentage: String(Math.round(input.item.level)) } : {}
|
|
9886
|
+
},
|
|
9887
|
+
deviceId: input.deviceId,
|
|
9888
|
+
...input.identity !== void 0 ? { deviceType: input.identity.type } : {}
|
|
9889
|
+
});
|
|
9890
|
+
}
|
|
9891
|
+
/**
|
|
9848
9892
|
* Normalize the infrastructure categories consumed by Notification Center.
|
|
9849
9893
|
* Malformed payloads are rejected instead of guessing an identity.
|
|
9850
9894
|
*
|
|
@@ -9996,6 +10040,7 @@ function incomingFromSystemEvent(event, lookup) {
|
|
|
9996
10040
|
}
|
|
9997
10041
|
case "system.restart-completed": return incomingFromServerUpdated(facts, data);
|
|
9998
10042
|
case "recording.export.completed": return incomingFromExportCompleted(facts, data);
|
|
10043
|
+
case "backup.run-completed": return incomingFromBackupCompleted(facts, data);
|
|
9999
10044
|
default: return null;
|
|
10000
10045
|
}
|
|
10001
10046
|
}
|
|
@@ -10159,6 +10204,37 @@ function incomingFromServerUpdated(facts, data) {
|
|
|
10159
10204
|
nodeIds: [nodeId]
|
|
10160
10205
|
});
|
|
10161
10206
|
}
|
|
10207
|
+
/**
|
|
10208
|
+
* A finished backup RUN.
|
|
10209
|
+
*
|
|
10210
|
+
* Subject is the run, not the destination: `backup.completed` fires once per
|
|
10211
|
+
* destination and an operator with three of them would be told three times.
|
|
10212
|
+
* The counts travel in the text because a PARTIAL run must not read as a clean
|
|
10213
|
+
* one — two destinations of three is news, and a bare "backup completed" would
|
|
10214
|
+
* hide it.
|
|
10215
|
+
*/
|
|
10216
|
+
function incomingFromBackupCompleted(facts, data) {
|
|
10217
|
+
const ok = numberField(data, "destinationsOk");
|
|
10218
|
+
const total = numberField(data, "destinationsTotal");
|
|
10219
|
+
if (ok === void 0 || total === void 0) return null;
|
|
10220
|
+
const failed = numberField(data, "destinationsFailed") ?? Math.max(0, total - ok);
|
|
10221
|
+
const sizeMB = numberField(data, "sizeMB");
|
|
10222
|
+
const backupId = stringField(data, "backupId");
|
|
10223
|
+
return systemSubject({
|
|
10224
|
+
facts,
|
|
10225
|
+
kind: "backup-completed",
|
|
10226
|
+
subject: `backup:${backupId ?? facts.timestamp}`,
|
|
10227
|
+
titleKey: "system.backup-completed.title",
|
|
10228
|
+
bodyKey: failed > 0 ? "system.backup-completed.body.partial" : "system.backup-completed.body",
|
|
10229
|
+
textParams: {
|
|
10230
|
+
destinationsOk: String(ok),
|
|
10231
|
+
destinationsTotal: String(total),
|
|
10232
|
+
destinationsFailed: String(failed),
|
|
10233
|
+
...sizeMB !== void 0 ? { sizeMB: String(sizeMB) } : {},
|
|
10234
|
+
...backupId !== void 0 ? { backupId } : {}
|
|
10235
|
+
}
|
|
10236
|
+
});
|
|
10237
|
+
}
|
|
10162
10238
|
function incomingFromExportCompleted(facts, data) {
|
|
10163
10239
|
const exportId = stringField(data, "exportId");
|
|
10164
10240
|
if (exportId === void 0) return null;
|
|
@@ -10496,6 +10572,8 @@ var NcDeviceAlerts = class {
|
|
|
10496
10572
|
* durable.
|
|
10497
10573
|
*/
|
|
10498
10574
|
lastReading = /* @__PURE__ */ new Map();
|
|
10575
|
+
/** The last reading per consumable SUBJECT, for the reminder sweep. */
|
|
10576
|
+
lastConsumable = /* @__PURE__ */ new Map();
|
|
10499
10577
|
constructor(deps) {
|
|
10500
10578
|
this.deps = deps;
|
|
10501
10579
|
this.now = deps.now ?? Date.now;
|
|
@@ -10533,6 +10611,44 @@ var NcDeviceAlerts = class {
|
|
|
10533
10611
|
}));
|
|
10534
10612
|
}
|
|
10535
10613
|
/**
|
|
10614
|
+
* ONE consumable item, from a device's `consumables` slice.
|
|
10615
|
+
*
|
|
10616
|
+
* Per ITEM, never per device: a vacuum has a brush, a filter and a mop pad,
|
|
10617
|
+
* and folding them into one subject would make the brush's alert cancel the
|
|
10618
|
+
* filter's — the same collision D141 split `device-switch:<id>` off liveness
|
|
10619
|
+
* to avoid. Each item is its own episode, its own cooldown, its own recovery.
|
|
10620
|
+
*
|
|
10621
|
+
* An item that has told us nothing leaves EVERYTHING untouched, for the
|
|
10622
|
+
* reason `observeBattery` does: seeding it healthy is a claim, and the first
|
|
10623
|
+
* genuine `replace` would then read as a transition out of a state nobody
|
|
10624
|
+
* observed.
|
|
10625
|
+
*/
|
|
10626
|
+
observeConsumable(deviceId, item) {
|
|
10627
|
+
const subject = `consumable:${deviceId}:${item.key}`;
|
|
10628
|
+
const previous = this.deps.rows.get(subject);
|
|
10629
|
+
const settings = this.deps.settings();
|
|
10630
|
+
const band = deriveConsumableBand({
|
|
10631
|
+
reading: {
|
|
10632
|
+
level: item.level,
|
|
10633
|
+
status: item.status
|
|
10634
|
+
},
|
|
10635
|
+
thresholdPercent: settings.consumableLowPercent,
|
|
10636
|
+
previous: previous?.alerting === true ? "low" : previous === void 0 ? void 0 : "normal"
|
|
10637
|
+
});
|
|
10638
|
+
if (band === null) return;
|
|
10639
|
+
this.lastConsumable.set(subject, {
|
|
10640
|
+
deviceId,
|
|
10641
|
+
item
|
|
10642
|
+
});
|
|
10643
|
+
this.apply(subject, band === "low", settings, (facts, alerting) => incomingFromConsumableBand({
|
|
10644
|
+
facts,
|
|
10645
|
+
deviceId,
|
|
10646
|
+
band: alerting ? "low" : "normal",
|
|
10647
|
+
item,
|
|
10648
|
+
identity: this.deps.lookup(deviceId)
|
|
10649
|
+
}));
|
|
10650
|
+
}
|
|
10651
|
+
/**
|
|
10536
10652
|
* The reminder sweep. Called from an existing periodic tick; it allocates
|
|
10537
10653
|
* nothing on the healthy path.
|
|
10538
10654
|
*
|
|
@@ -10546,6 +10662,17 @@ var NcDeviceAlerts = class {
|
|
|
10546
10662
|
if (settings.renotifyMinutes <= 0) return;
|
|
10547
10663
|
for (const row of this.deps.rows.all()) {
|
|
10548
10664
|
if (!row.alerting) continue;
|
|
10665
|
+
const consumable = this.lastConsumable.get(row.subject);
|
|
10666
|
+
if (consumable !== void 0) {
|
|
10667
|
+
this.apply(row.subject, true, settings, (facts) => incomingFromConsumableBand({
|
|
10668
|
+
facts,
|
|
10669
|
+
deviceId: consumable.deviceId,
|
|
10670
|
+
band: "low",
|
|
10671
|
+
item: consumable.item,
|
|
10672
|
+
identity: this.deps.lookup(consumable.deviceId)
|
|
10673
|
+
}));
|
|
10674
|
+
continue;
|
|
10675
|
+
}
|
|
10549
10676
|
const restatement = this.lastReading.get(row.subject);
|
|
10550
10677
|
if (restatement === void 0) continue;
|
|
10551
10678
|
this.apply(row.subject, true, settings, (facts) => incomingFromBatteryBand({
|
|
@@ -11654,6 +11781,15 @@ var en_default = {
|
|
|
11654
11781
|
"system.export-completed.title": "Export ready",
|
|
11655
11782
|
"system.export-completed.body": "The footage export is ready",
|
|
11656
11783
|
"system.export-completed.body.titled": "{{title}} is ready",
|
|
11784
|
+
"system.backup-completed.title": "Backup completed",
|
|
11785
|
+
"system.backup-completed.body": "Backup saved to {{destinationsOk}} of {{destinationsTotal}} destinations ({{sizeMB}} MB)",
|
|
11786
|
+
"system.backup-completed.body.partial": "Backup saved to {{destinationsOk}} of {{destinationsTotal}} destinations — {{destinationsFailed}} failed",
|
|
11787
|
+
"system.device-consumable-low.title": "Consumable low",
|
|
11788
|
+
"system.device-consumable-low.body": "{{item}} on {{deviceName}} is at {{percentage}}%",
|
|
11789
|
+
"system.device-consumable-low.body.unmeasured": "{{item}} on {{deviceName}} needs replacing",
|
|
11790
|
+
"system.device-consumable-normal.title": "Consumable replaced",
|
|
11791
|
+
"system.device-consumable-normal.body": "{{item}} on {{deviceName}} is back to {{percentage}}%",
|
|
11792
|
+
"system.device-consumable-normal.body.unmeasured": "{{item}} on {{deviceName}} is OK again",
|
|
11657
11793
|
"system.camera-online.title": "Device online",
|
|
11658
11794
|
"system.camera-online.body": "Device {{deviceName}} is online",
|
|
11659
11795
|
"system.camera-offline.title": "Device offline",
|
|
@@ -11914,6 +12050,15 @@ var it_default = {
|
|
|
11914
12050
|
"system.export-completed.title": "Export pronto",
|
|
11915
12051
|
"system.export-completed.body": "L'export del filmato è pronto",
|
|
11916
12052
|
"system.export-completed.body.titled": "{{title}} è pronto",
|
|
12053
|
+
"system.backup-completed.title": "Backup completato",
|
|
12054
|
+
"system.backup-completed.body": "Backup salvato su {{destinationsOk}} destinazioni su {{destinationsTotal}} ({{sizeMB}} MB)",
|
|
12055
|
+
"system.backup-completed.body.partial": "Backup salvato su {{destinationsOk}} destinazioni su {{destinationsTotal}} — {{destinationsFailed}} fallite",
|
|
12056
|
+
"system.device-consumable-low.title": "Consumabile quasi esaurito",
|
|
12057
|
+
"system.device-consumable-low.body": "{{item}} su {{deviceName}} è al {{percentage}}%",
|
|
12058
|
+
"system.device-consumable-low.body.unmeasured": "{{item}} su {{deviceName}} va sostituito",
|
|
12059
|
+
"system.device-consumable-normal.title": "Consumabile sostituito",
|
|
12060
|
+
"system.device-consumable-normal.body": "{{item}} su {{deviceName}} è tornato al {{percentage}}%",
|
|
12061
|
+
"system.device-consumable-normal.body.unmeasured": "{{item}} su {{deviceName}} è di nuovo a posto",
|
|
11917
12062
|
"system.camera-online.title": "Dispositivo online",
|
|
11918
12063
|
"system.camera-online.body": "Il dispositivo {{deviceName}} è online",
|
|
11919
12064
|
"system.camera-offline.title": "Dispositivo offline",
|
|
@@ -22237,6 +22382,7 @@ function alarmButtonKind(kind) {
|
|
|
22237
22382
|
*/
|
|
22238
22383
|
var NC_DEVICE_ALERT_FALLBACK = {
|
|
22239
22384
|
batteryLowPercent: 20,
|
|
22385
|
+
consumableLowPercent: 15,
|
|
22240
22386
|
renotifyMinutes: 720,
|
|
22241
22387
|
notifyRecovery: true
|
|
22242
22388
|
};
|
|
@@ -22269,6 +22415,52 @@ function batterySliceOf(event) {
|
|
|
22269
22415
|
};
|
|
22270
22416
|
}
|
|
22271
22417
|
/**
|
|
22418
|
+
* A `device.state-changed` carrying a CONSUMABLES slice, reduced to the items
|
|
22419
|
+
* the band derivation reads.
|
|
22420
|
+
*
|
|
22421
|
+
* Same tap as the battery's: `DeviceStateChanged` is already subscribed, so
|
|
22422
|
+
* every `consumables` write already arrives here and was simply dropped. No
|
|
22423
|
+
* new bus category and no producer change.
|
|
22424
|
+
*
|
|
22425
|
+
* An item is kept only when it carries a usable `key` — the subject is built
|
|
22426
|
+
* from it, and an item with no key could not be told apart from its sibling.
|
|
22427
|
+
* `level` and `status` are each optional; an item with NEITHER is still
|
|
22428
|
+
* returned, because deciding that it has told us nothing belongs to
|
|
22429
|
+
* `deriveConsumableBand` and not to a parser (D393: absent is not zero, and
|
|
22430
|
+
* the parser is not the place that judges).
|
|
22431
|
+
*/
|
|
22432
|
+
function consumablesSliceOf(event) {
|
|
22433
|
+
if (event.category !== "device.state-changed") return null;
|
|
22434
|
+
const data = event.data;
|
|
22435
|
+
if (data["capName"] !== "consumables") return null;
|
|
22436
|
+
const deviceId = data["deviceId"];
|
|
22437
|
+
const slice = data["slice"];
|
|
22438
|
+
if (typeof deviceId !== "number") return null;
|
|
22439
|
+
if (typeof slice !== "object" || slice === null || Array.isArray(slice)) return null;
|
|
22440
|
+
const rawItems = slice["items"];
|
|
22441
|
+
if (!Array.isArray(rawItems)) return null;
|
|
22442
|
+
const items = [];
|
|
22443
|
+
for (const entry of rawItems) {
|
|
22444
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
22445
|
+
const row = entry;
|
|
22446
|
+
const key = row["key"];
|
|
22447
|
+
if (typeof key !== "string" || key.length === 0) continue;
|
|
22448
|
+
const level = row["level"];
|
|
22449
|
+
const status = row["status"];
|
|
22450
|
+
const label = row["label"];
|
|
22451
|
+
items.push({
|
|
22452
|
+
key,
|
|
22453
|
+
label: typeof label === "string" && label.length > 0 ? label : key,
|
|
22454
|
+
level: typeof level === "number" ? level : null,
|
|
22455
|
+
status: status === "ok" || status === "replace" ? status : null
|
|
22456
|
+
});
|
|
22457
|
+
}
|
|
22458
|
+
return items.length === 0 ? null : {
|
|
22459
|
+
deviceId,
|
|
22460
|
+
items
|
|
22461
|
+
};
|
|
22462
|
+
}
|
|
22463
|
+
/**
|
|
22272
22464
|
* What `getAlarmConfig` answers on a node with no panel.
|
|
22273
22465
|
*
|
|
22274
22466
|
* The DEFAULTS, not zeroes: paired with `deviceId: null` this tells a client
|
|
@@ -24317,6 +24509,11 @@ var NotificationCenter = class NotificationCenter {
|
|
|
24317
24509
|
this.deviceAlerts.observeBattery(battery.deviceId, battery.reading);
|
|
24318
24510
|
return;
|
|
24319
24511
|
}
|
|
24512
|
+
const consumables = consumablesSliceOf(event);
|
|
24513
|
+
if (consumables !== null) {
|
|
24514
|
+
for (const item of consumables.items) this.deviceAlerts.observeConsumable(consumables.deviceId, item);
|
|
24515
|
+
return;
|
|
24516
|
+
}
|
|
24320
24517
|
const lookup = this.deviceDirectory.lookup();
|
|
24321
24518
|
const incoming = incomingFromSystemEvent(event, lookup);
|
|
24322
24519
|
if (incoming === null) {
|
|
@@ -25929,6 +26126,7 @@ var NotificationCenter = class NotificationCenter {
|
|
|
25929
26126
|
deviceAlertSettings() {
|
|
25930
26127
|
return this.deps.readDeviceAlertSettings?.() ?? {
|
|
25931
26128
|
batteryLowPercent: NC_DEVICE_ALERT_FALLBACK.batteryLowPercent,
|
|
26129
|
+
consumableLowPercent: NC_DEVICE_ALERT_FALLBACK.consumableLowPercent,
|
|
25932
26130
|
renotifyMinutes: NC_DEVICE_ALERT_FALLBACK.renotifyMinutes,
|
|
25933
26131
|
notifyRecovery: NC_DEVICE_ALERT_FALLBACK.notifyRecovery
|
|
25934
26132
|
};
|
|
@@ -26544,6 +26742,7 @@ function systemEventSubject(kind, scope) {
|
|
|
26544
26742
|
if (kind === "addon-updated") return `packages:addon-updated:${scope.packageName ?? "pkg"}`;
|
|
26545
26743
|
if (kind === "server-updated") return `server-updated:${scope.nodeId ?? "hub"}`;
|
|
26546
26744
|
if (kind === "export-completed") return `export:${scope.deviceId ?? 0}`;
|
|
26745
|
+
if (kind === "backup-completed") return "backup:test";
|
|
26547
26746
|
return `alarm:${scope.deviceId ?? 0}`;
|
|
26548
26747
|
}
|
|
26549
26748
|
/**
|
|
@@ -49947,6 +50146,19 @@ var DeviceAlertSettingsSchema = require_dist.object({
|
|
|
49947
50146
|
*/
|
|
49948
50147
|
batteryLowPercent: require_dist.number().int().min(1).max(99).default(20),
|
|
49949
50148
|
/**
|
|
50149
|
+
* What counts as a nearly-exhausted CONSUMABLE — a filter, a brush, a mop pad.
|
|
50150
|
+
*
|
|
50151
|
+
* Its own number and not the battery's, because the two are different chores
|
|
50152
|
+
* on different clocks: a battery at 20 % is hours from flat, a brush at 20 %
|
|
50153
|
+
* is weeks from worn, and an operator who wants to be told early about one
|
|
50154
|
+
* does not necessarily want to be told early about the other.
|
|
50155
|
+
*
|
|
50156
|
+
* Applies to the MEASURED side only. An item whose provider states a
|
|
50157
|
+
* `replace` verdict is low whatever this says — the provider decided, and a
|
|
50158
|
+
* threshold cannot talk it out of it (`alerts/consumable-band.ts`).
|
|
50159
|
+
*/
|
|
50160
|
+
consumableLowPercent: require_dist.number().int().min(1).max(99).default(15),
|
|
50161
|
+
/**
|
|
49950
50162
|
* How long a still-true alert waits before it says itself again.
|
|
49951
50163
|
*
|
|
49952
50164
|
* **0 = say it once.** That is the behaviour of every other notification this
|
|
@@ -58592,7 +58804,9 @@ function stationaryEvidenceKey(deviceId, className, anchorId) {
|
|
|
58592
58804
|
* refused is the FIRST thing it failed, not the last.
|
|
58593
58805
|
*/
|
|
58594
58806
|
function evaluateStationaryEvidence(row, policy, classEnabled) {
|
|
58595
|
-
const
|
|
58807
|
+
const windowSeen = row.windowSeen + row.prevWindowSeen;
|
|
58808
|
+
const windowAgreed = row.windowAgreed + row.prevWindowAgreed;
|
|
58809
|
+
const ratio = windowSeen > 0 ? windowAgreed / windowSeen : 0;
|
|
58596
58810
|
const spanMs = row.lastAgreedAt - row.firstAgreedAt;
|
|
58597
58811
|
const base = {
|
|
58598
58812
|
agreed: row.agreed,
|
|
@@ -58626,6 +58840,36 @@ function evaluateStationaryEvidence(row, policy, classEnabled) {
|
|
|
58626
58840
|
};
|
|
58627
58841
|
}
|
|
58628
58842
|
/**
|
|
58843
|
+
* Slide the window forward to `now`.
|
|
58844
|
+
*
|
|
58845
|
+
* One bucket width past: the current bucket becomes the previous one and a
|
|
58846
|
+
* fresh one opens. TWO or more past: the candidate was not observed for the
|
|
58847
|
+
* whole window, so both buckets are empty — and an empty window answers `0`,
|
|
58848
|
+
* which is an honest "nothing recent to judge by" rather than a stale verdict.
|
|
58849
|
+
* The lifetime counters are untouched, so nothing a candidate has earned is
|
|
58850
|
+
* lost — only its claim about the present expires.
|
|
58851
|
+
*/
|
|
58852
|
+
function rotateWindow(row, now) {
|
|
58853
|
+
const elapsed = now - row.windowStartedAt;
|
|
58854
|
+
if (elapsed < 9e5) return row;
|
|
58855
|
+
if (elapsed < 2 * 9e5) return {
|
|
58856
|
+
...row,
|
|
58857
|
+
prevWindowAgreed: row.windowAgreed,
|
|
58858
|
+
prevWindowSeen: row.windowSeen,
|
|
58859
|
+
windowAgreed: 0,
|
|
58860
|
+
windowSeen: 0,
|
|
58861
|
+
windowStartedAt: now
|
|
58862
|
+
};
|
|
58863
|
+
return {
|
|
58864
|
+
...row,
|
|
58865
|
+
prevWindowAgreed: 0,
|
|
58866
|
+
prevWindowSeen: 0,
|
|
58867
|
+
windowAgreed: 0,
|
|
58868
|
+
windowSeen: 0,
|
|
58869
|
+
windowStartedAt: now
|
|
58870
|
+
};
|
|
58871
|
+
}
|
|
58872
|
+
/**
|
|
58629
58873
|
* The candidate this observation belongs to: the same-class row whose anchor it
|
|
58630
58874
|
* overlaps most, provided the overlap reaches `agreeIou`. Overlap is the ONLY
|
|
58631
58875
|
* notion of sameness here, and it is the same {@link iou} the suppression gate
|
|
@@ -58659,15 +58903,19 @@ function matchEvidenceCandidate(rows, className, bbox, agreeIou) {
|
|
|
58659
58903
|
*/
|
|
58660
58904
|
function foldObservation(row, observation, agrees, overlap) {
|
|
58661
58905
|
const seen = row.seen + 1;
|
|
58906
|
+
const rotated = rotateWindow(row, observation.timestamp);
|
|
58662
58907
|
if (!agrees) return {
|
|
58663
|
-
...
|
|
58908
|
+
...rotated,
|
|
58664
58909
|
seen,
|
|
58910
|
+
windowSeen: rotated.windowSeen + 1,
|
|
58665
58911
|
updatedAt: observation.timestamp
|
|
58666
58912
|
};
|
|
58667
58913
|
const improves = overlap >= iou(row.anchor, row.bestBbox);
|
|
58668
58914
|
return {
|
|
58669
|
-
...
|
|
58915
|
+
...rotated,
|
|
58670
58916
|
seen,
|
|
58917
|
+
windowSeen: rotated.windowSeen + 1,
|
|
58918
|
+
windowAgreed: rotated.windowAgreed + 1,
|
|
58671
58919
|
agreed: row.agreed + 1,
|
|
58672
58920
|
firstAgreedAt: Math.min(row.firstAgreedAt, observation.timestamp),
|
|
58673
58921
|
lastAgreedAt: Math.max(row.lastAgreedAt, observation.timestamp),
|
|
@@ -58691,6 +58939,11 @@ function newEvidenceRow(observation, anchorId) {
|
|
|
58691
58939
|
anchor: { ...observation.bbox },
|
|
58692
58940
|
agreed: 1,
|
|
58693
58941
|
seen: 1,
|
|
58942
|
+
windowAgreed: 1,
|
|
58943
|
+
windowSeen: 1,
|
|
58944
|
+
prevWindowAgreed: 0,
|
|
58945
|
+
prevWindowSeen: 0,
|
|
58946
|
+
windowStartedAt: observation.timestamp,
|
|
58694
58947
|
firstAgreedAt: observation.timestamp,
|
|
58695
58948
|
lastAgreedAt: observation.timestamp,
|
|
58696
58949
|
bestBbox: { ...observation.bbox },
|
|
@@ -58829,6 +59082,11 @@ var StationaryEvidenceLedger = class {
|
|
|
58829
59082
|
anchor: { ...row.anchor },
|
|
58830
59083
|
agreed: row.agreed,
|
|
58831
59084
|
seen: row.seen,
|
|
59085
|
+
windowAgreed: row.windowAgreed,
|
|
59086
|
+
windowSeen: row.windowSeen,
|
|
59087
|
+
prevWindowAgreed: row.prevWindowAgreed,
|
|
59088
|
+
prevWindowSeen: row.prevWindowSeen,
|
|
59089
|
+
windowStartedAt: row.windowStartedAt,
|
|
58832
59090
|
firstAgreedAt: row.firstAgreedAt,
|
|
58833
59091
|
lastAgreedAt: row.lastAgreedAt,
|
|
58834
59092
|
bestBbox: { ...row.bestBbox },
|
|
@@ -58857,6 +59115,11 @@ var StationaryEvidenceLedger = class {
|
|
|
58857
59115
|
anchor,
|
|
58858
59116
|
agreed: numberOr(data.agreed, 0),
|
|
58859
59117
|
seen: numberOr(data.seen, 0),
|
|
59118
|
+
windowAgreed: numberOr(data.windowAgreed, 0),
|
|
59119
|
+
windowSeen: numberOr(data.windowSeen, 0),
|
|
59120
|
+
prevWindowAgreed: numberOr(data.prevWindowAgreed, 0),
|
|
59121
|
+
prevWindowSeen: numberOr(data.prevWindowSeen, 0),
|
|
59122
|
+
windowStartedAt: numberOr(data.windowStartedAt, updatedAt),
|
|
58860
59123
|
firstAgreedAt: numberOr(data.firstAgreedAt, updatedAt),
|
|
58861
59124
|
lastAgreedAt: numberOr(data.lastAgreedAt, updatedAt),
|
|
58862
59125
|
bestBbox,
|
|
@@ -67561,7 +67824,8 @@ var NC_SYSTEM_EVENT_CATEGORIES = [
|
|
|
67561
67824
|
require_dist.EventCategory.UpdateAvailable,
|
|
67562
67825
|
require_dist.EventCategory.AddonUpdated,
|
|
67563
67826
|
require_dist.EventCategory.SystemRestartCompleted,
|
|
67564
|
-
require_dist.EventCategory.RecordingExportCompleted
|
|
67827
|
+
require_dist.EventCategory.RecordingExportCompleted,
|
|
67828
|
+
require_dist.EventCategory.BackupRunCompleted
|
|
67565
67829
|
];
|
|
67566
67830
|
/**
|
|
67567
67831
|
* Install the addon's bus subscriptions. Identical set + relative order to the
|
|
@@ -70644,6 +70908,17 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
70644
70908
|
default: DEVICE_ALERT_DEFAULTS.batteryLowPercent,
|
|
70645
70909
|
unit: "%"
|
|
70646
70910
|
},
|
|
70911
|
+
{
|
|
70912
|
+
type: "number",
|
|
70913
|
+
key: "consumableLowPercent",
|
|
70914
|
+
label: "Low consumable at",
|
|
70915
|
+
description: "At or below this, a measured consumable — a filter, a brush, a mop pad — is nearly exhausted. Its own number rather than the battery’s: a battery at 20% is hours from flat, a brush at 20% is weeks from worn. An item whose device reports “replace” outright is announced whatever this says.",
|
|
70916
|
+
min: 1,
|
|
70917
|
+
max: 99,
|
|
70918
|
+
step: 1,
|
|
70919
|
+
default: DEVICE_ALERT_DEFAULTS.consumableLowPercent,
|
|
70920
|
+
unit: "%"
|
|
70921
|
+
},
|
|
70647
70922
|
{
|
|
70648
70923
|
type: "number",
|
|
70649
70924
|
key: "alertRenotifyMinutes",
|
|
@@ -76237,6 +76512,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
76237
76512
|
},
|
|
76238
76513
|
readDeviceAlertSettings: () => ({
|
|
76239
76514
|
batteryLowPercent: this.deviceAlertSettingsMirror.batteryLowPercent,
|
|
76515
|
+
consumableLowPercent: this.deviceAlertSettingsMirror.consumableLowPercent,
|
|
76240
76516
|
renotifyMinutes: this.deviceAlertSettingsMirror.alertRenotifyMinutes,
|
|
76241
76517
|
notifyRecovery: this.deviceAlertSettingsMirror.alertNotifyRecovery
|
|
76242
76518
|
}),
|