@camstack/addon-post-analysis 1.2.219 → 1.2.220
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-Cnlk6Tyz.js → dist-Bw4aTXFP.js} +26 -27
- package/dist/{dist-D0l3xZqa.mjs → dist-DWXR9KNe.mjs} +21 -22
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-C8d8YCvw.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Bfm6wOeY.mjs} +3 -3
- package/dist/pipeline-analytics/{hostInit-BsYVmiZ9.mjs → hostInit-CbC3Ttww.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +210 -57
- package/dist/pipeline-analytics/index.mjs +210 -57
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -16219,24 +16219,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
16219
16219
|
targetId: string(),
|
|
16220
16220
|
enabled: boolean()
|
|
16221
16221
|
}), _void(), { kind: "mutation" });
|
|
16222
|
-
var MACRO_LABELS = [
|
|
16223
|
-
{
|
|
16224
|
-
id: "person",
|
|
16225
|
-
name: "Person"
|
|
16226
|
-
},
|
|
16227
|
-
{
|
|
16228
|
-
id: "vehicle",
|
|
16229
|
-
name: "Vehicle"
|
|
16230
|
-
},
|
|
16231
|
-
{
|
|
16232
|
-
id: "animal",
|
|
16233
|
-
name: "Animal"
|
|
16234
|
-
},
|
|
16235
|
-
{
|
|
16236
|
-
id: "package",
|
|
16237
|
-
name: "Package"
|
|
16238
|
-
}
|
|
16239
|
-
];
|
|
16240
16222
|
/**
|
|
16241
16223
|
* The class names a per-class COUNT can ever be keyed on — the macro ids, and
|
|
16242
16224
|
* only those.
|
|
@@ -16254,7 +16236,24 @@ var MACRO_LABELS = [
|
|
|
16254
16236
|
* does not go through the engine's macro-expanding `expandClassSelector`), two
|
|
16255
16237
|
* live rules were authored on `car`, and both counters read 0 forever.
|
|
16256
16238
|
*/
|
|
16257
|
-
var DETECTION_MACRO_CLASSES = new Set(
|
|
16239
|
+
var DETECTION_MACRO_CLASSES = new Set([
|
|
16240
|
+
{
|
|
16241
|
+
id: "person",
|
|
16242
|
+
name: "Person"
|
|
16243
|
+
},
|
|
16244
|
+
{
|
|
16245
|
+
id: "vehicle",
|
|
16246
|
+
name: "Vehicle"
|
|
16247
|
+
},
|
|
16248
|
+
{
|
|
16249
|
+
id: "animal",
|
|
16250
|
+
name: "Animal"
|
|
16251
|
+
},
|
|
16252
|
+
{
|
|
16253
|
+
id: "package",
|
|
16254
|
+
name: "Package"
|
|
16255
|
+
}
|
|
16256
|
+
].map((l) => l.id));
|
|
16258
16257
|
/** True when `className` is a macro the detector can actually emit (and hence a
|
|
16259
16258
|
* class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
|
|
16260
16259
|
function isDetectionMacroClass(className) {
|
|
@@ -27767,8 +27766,8 @@ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly
|
|
|
27767
27766
|
*
|
|
27768
27767
|
* An **empty array reads NOTHING** — `[]` is an empty page, never
|
|
27769
27768
|
* "every camera". A request for no devices is a request, not an
|
|
27770
|
-
* omission; same contract as `deviceManager.
|
|
27771
|
-
* `pipelineAnalytics.listRecentTracks`.
|
|
27769
|
+
* omission; same contract as `deviceManager.listAll`'s `deviceIds`
|
|
27770
|
+
* and `pipelineAnalytics.listRecentTracks`.
|
|
27772
27771
|
*
|
|
27773
27772
|
* Absent (`undefined`) is the omission, and keeps the cluster-wide view.
|
|
27774
27773
|
*/
|
|
@@ -45570,6 +45569,12 @@ Object.defineProperty(exports, "EventCategory", {
|
|
|
45570
45569
|
return EventCategory;
|
|
45571
45570
|
}
|
|
45572
45571
|
});
|
|
45572
|
+
Object.defineProperty(exports, "FIRST_LEVEL_MACRO_CLASSES", {
|
|
45573
|
+
enumerable: true,
|
|
45574
|
+
get: function() {
|
|
45575
|
+
return FIRST_LEVEL_MACRO_CLASSES;
|
|
45576
|
+
}
|
|
45577
|
+
});
|
|
45573
45578
|
Object.defineProperty(exports, "FULL_IMAGE_BBOX", {
|
|
45574
45579
|
enumerable: true,
|
|
45575
45580
|
get: function() {
|
|
@@ -45588,12 +45593,6 @@ Object.defineProperty(exports, "LabelAttributionSchema", {
|
|
|
45588
45593
|
return LabelAttributionSchema;
|
|
45589
45594
|
}
|
|
45590
45595
|
});
|
|
45591
|
-
Object.defineProperty(exports, "MACRO_LABELS", {
|
|
45592
|
-
enumerable: true,
|
|
45593
|
-
get: function() {
|
|
45594
|
-
return MACRO_LABELS;
|
|
45595
|
-
}
|
|
45596
|
-
});
|
|
45597
45596
|
Object.defineProperty(exports, "MAX_ARCHIVED_DEBUG_NOTES", {
|
|
45598
45597
|
enumerable: true,
|
|
45599
45598
|
get: function() {
|
|
@@ -16188,24 +16188,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
16188
16188
|
targetId: string(),
|
|
16189
16189
|
enabled: boolean()
|
|
16190
16190
|
}), _void(), { kind: "mutation" });
|
|
16191
|
-
var MACRO_LABELS = [
|
|
16192
|
-
{
|
|
16193
|
-
id: "person",
|
|
16194
|
-
name: "Person"
|
|
16195
|
-
},
|
|
16196
|
-
{
|
|
16197
|
-
id: "vehicle",
|
|
16198
|
-
name: "Vehicle"
|
|
16199
|
-
},
|
|
16200
|
-
{
|
|
16201
|
-
id: "animal",
|
|
16202
|
-
name: "Animal"
|
|
16203
|
-
},
|
|
16204
|
-
{
|
|
16205
|
-
id: "package",
|
|
16206
|
-
name: "Package"
|
|
16207
|
-
}
|
|
16208
|
-
];
|
|
16209
16191
|
/**
|
|
16210
16192
|
* The class names a per-class COUNT can ever be keyed on — the macro ids, and
|
|
16211
16193
|
* only those.
|
|
@@ -16223,7 +16205,24 @@ var MACRO_LABELS = [
|
|
|
16223
16205
|
* does not go through the engine's macro-expanding `expandClassSelector`), two
|
|
16224
16206
|
* live rules were authored on `car`, and both counters read 0 forever.
|
|
16225
16207
|
*/
|
|
16226
|
-
var DETECTION_MACRO_CLASSES = new Set(
|
|
16208
|
+
var DETECTION_MACRO_CLASSES = new Set([
|
|
16209
|
+
{
|
|
16210
|
+
id: "person",
|
|
16211
|
+
name: "Person"
|
|
16212
|
+
},
|
|
16213
|
+
{
|
|
16214
|
+
id: "vehicle",
|
|
16215
|
+
name: "Vehicle"
|
|
16216
|
+
},
|
|
16217
|
+
{
|
|
16218
|
+
id: "animal",
|
|
16219
|
+
name: "Animal"
|
|
16220
|
+
},
|
|
16221
|
+
{
|
|
16222
|
+
id: "package",
|
|
16223
|
+
name: "Package"
|
|
16224
|
+
}
|
|
16225
|
+
].map((l) => l.id));
|
|
16227
16226
|
/** True when `className` is a macro the detector can actually emit (and hence a
|
|
16228
16227
|
* class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
|
|
16229
16228
|
function isDetectionMacroClass(className) {
|
|
@@ -27736,8 +27735,8 @@ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly
|
|
|
27736
27735
|
*
|
|
27737
27736
|
* An **empty array reads NOTHING** — `[]` is an empty page, never
|
|
27738
27737
|
* "every camera". A request for no devices is a request, not an
|
|
27739
|
-
* omission; same contract as `deviceManager.
|
|
27740
|
-
* `pipelineAnalytics.listRecentTracks`.
|
|
27738
|
+
* omission; same contract as `deviceManager.listAll`'s `deviceIds`
|
|
27739
|
+
* and `pipelineAnalytics.listRecentTracks`.
|
|
27741
27740
|
*
|
|
27742
27741
|
* Absent (`undefined`) is the omission, and keeps the cluster-wide view.
|
|
27743
27742
|
*/
|
|
@@ -45431,4 +45430,4 @@ function vectorDimFromBase64(encoded) {
|
|
|
45431
45430
|
return Math.floor(Buffer.from(encoded, "base64").byteLength / 4);
|
|
45432
45431
|
}
|
|
45433
45432
|
//#endregion
|
|
45434
|
-
export { VISIT_MERGE_GAP_MS as $, object as $t, NcRulePatchSchema as A, resolvePoolMemoryPolicy as At, PoolMemoryWatchdog as B, CamProfileSchema as Bt, NC_ALARM_SYSTEM_EVENT_KINDS as C, parseProcStatus as Ct, NC_TAXONOMY as D, readDeviceStateFrom as Dt, NC_SNOOZE_MAX_MINUTES as E, plateGalleryCapability as Et, NcSnoozeSchema as F, vectorDimFromBase64 as Ft, SCENE_DEFAULT_UNCOVERED_POLICY as G, nodePin as Gt, RetrainStatusSchema as H, createEvent as Ht, NcSnoozeSuppressedSchema as I, videoclipsCapability as It, TIMELAPSE_DENSE_FLOOR_SEC as J, array as Jt, SCENE_DIVERGED as K, sleep as Kt, NcSystemEventKindSchema as L, zoneAnalyticsCapability as Lt, NcRuleTargetSchema as M, storageOccupancyCapability as Mt, NcScheduleSchema as N, subKindsOf as Nt, NcConditionDescriptorSchema as O, readTimelapseGeneratedAt as Ot, NcSnoozeInputSchema as P, systemEventFilterApplies as Pt, TrackSourceSchema as Q, number as Qt, NcTaxonomySchema as R, errMsg as Rt, MediaFileKindEnum as S, notificationRulesCapability as St, NC_DEFAULT_SNOOZE_MINUTES as T, pipelineAnalyticsCapability as Tt, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as U, hydrateSchema as Ut, RECORDING_EXPORT_MAX_READ_BYTES as V, DeviceType as Vt, SCENE_DEFAULT_ANCHOR_THRESHOLD as W, isDeviceScopedCap as Wt, TimelapseRulePatchSchema as X, discriminatedUnion as Xt, TimelapseRuleInputSchema as Y, boolean as Yt, TimelapseRuleSchema as Z, literal as Zt,
|
|
45433
|
+
export { VISIT_MERGE_GAP_MS as $, object as $t, NcRulePatchSchema as A, resolvePoolMemoryPolicy as At, PoolMemoryWatchdog as B, CamProfileSchema as Bt, NC_ALARM_SYSTEM_EVENT_KINDS as C, parseProcStatus as Ct, NC_TAXONOMY as D, readDeviceStateFrom as Dt, NC_SNOOZE_MAX_MINUTES as E, plateGalleryCapability as Et, NcSnoozeSchema as F, vectorDimFromBase64 as Ft, SCENE_DEFAULT_UNCOVERED_POLICY as G, nodePin as Gt, RetrainStatusSchema as H, createEvent as Ht, NcSnoozeSuppressedSchema as I, videoclipsCapability as It, TIMELAPSE_DENSE_FLOOR_SEC as J, array as Jt, SCENE_DIVERGED as K, sleep as Kt, NcSystemEventKindSchema as L, zoneAnalyticsCapability as Lt, NcRuleTargetSchema as M, storageOccupancyCapability as Mt, NcScheduleSchema as N, subKindsOf as Nt, NcConditionDescriptorSchema as O, readTimelapseGeneratedAt as Ot, NcSnoozeInputSchema as P, systemEventFilterApplies as Pt, TrackSourceSchema as Q, number as Qt, NcTaxonomySchema as R, errMsg as Rt, MediaFileKindEnum as S, notificationRulesCapability as St, NC_DEFAULT_SNOOZE_MINUTES as T, pipelineAnalyticsCapability as Tt, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as U, hydrateSchema as Ut, RECORDING_EXPORT_MAX_READ_BYTES as V, DeviceType as Vt, SCENE_DEFAULT_ANCHOR_THRESHOLD as W, isDeviceScopedCap as Wt, TimelapseRulePatchSchema as X, discriminatedUnion as Xt, TimelapseRuleInputSchema as Y, boolean as Yt, TimelapseRuleSchema as Z, literal as Zt, FailureCounters as _, isDetectionMacroClass as _t, CLUSTER_MODEL_SCOPED_STEPS as a, buildEventKindDescriptor as at, MAX_BIRTH_DECISION_RECORDS as b, kebabToCamel as bt, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as c, defineCustomActions as ct, DETECTION_PIPELINE_CAP_NAME as d, encodeVectorBase64 as dt, partialRecord as en, addonWidgetsSourceCapability as et, DeclaredDevices as f, evaluateSensorEdge as ft, FULL_IMAGE_BBOX as g, hfModelUrl as gt, FIRST_LEVEL_MACRO_CLASSES as h, failureContributionCapability as ht, BirthDecisionRecordSchema as i, EventCategory as in, audioModeOf as it, NcRuleSchema as j, sceneMonitorCapability as jt, NcRuleInputSchema as k, resolveLocationMode as kt, DEFAULT_TIMELAPSE_PREVIEW_TEXT as l, deriveRecordingMode as lt, EVENT_PAD_MS as m, faceGalleryCapability as mt, ArchivedDebugNoteSchema as n, string as nn, assertTimelapseCadences as nt, COCO_TO_MACRO as o, cosineSimilarity as ot, EVENT_KIND_BY_CAP as p, evictionPolicyOfLocation as pt, SceneMonitorSchema as q, _enum as qt, BaseDevice as r, unknown as rn, audioMetricsCapability as rt, DEFAULT_EVENT_COLOR as s, customAction as st, AUDIO_MACRO_LABELS as t, record as tn, alarmPanelCapability as tt, DETECTION_MACRO_CLASSES as u, embeddingEncoderCapability as ut, LabelAttributionSchema as v, isScheduleActive as vt, NC_CONDITION_CATALOG as w, pickClusterStepModels as wt, MAX_BIRTH_LATENCY_PROXY_MS as x, mayWriteToLocation as xt, MAX_ARCHIVED_DEBUG_NOTES as y, isSourceCap as yt, OpsLogEntrySchema as z, BaseAddon as zt };
|
|
@@ -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-Bw4aTXFP.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as resolvePoolMemoryPolicy, B as PoolMemoryWatchdog, Ct as parseProcStatus, gt as hfModelUrl, ut as embeddingEncoderCapability, zt as BaseAddon } from "../dist-
|
|
1
|
+
import { At as resolvePoolMemoryPolicy, B as PoolMemoryWatchdog, Ct as parseProcStatus, gt as hfModelUrl, ut as embeddingEncoderCapability, zt as BaseAddon } from "../dist-DWXR9KNe.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.98",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.178",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.146",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.178",
|
|
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.98",
|
|
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.146",
|
|
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-Bw4aTXFP.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");
|
|
@@ -20002,6 +20002,14 @@ function checkTimelapsePacing(input) {
|
|
|
20002
20002
|
unservedWindows: []
|
|
20003
20003
|
};
|
|
20004
20004
|
}
|
|
20005
|
+
//#endregion
|
|
20006
|
+
//#region src/notification-center/timelapse/timelapse-renderer.ts
|
|
20007
|
+
/** `part / whole` to two decimals — the shape a log line is read in. An empty
|
|
20008
|
+
* whole is 0, never NaN: a ratio nobody can compare is not a measurement. */
|
|
20009
|
+
function ratio(part, whole) {
|
|
20010
|
+
if (whole <= 0) return 0;
|
|
20011
|
+
return Math.round(part / whole * 100) / 100;
|
|
20012
|
+
}
|
|
20005
20013
|
/** Bound on the post-render pacing check. The check is best-effort by charter:
|
|
20006
20014
|
* a byte read that hangs must delay delivery by at most this, never sink it. */
|
|
20007
20015
|
var PACING_CHECK_TIMEOUT_MS = 2e4;
|
|
@@ -20025,25 +20033,6 @@ function profileCost(profile) {
|
|
|
20025
20033
|
const i = PROFILE_BY_COST.indexOf(profile);
|
|
20026
20034
|
return i === -1 ? PROFILE_BY_COST.length : i;
|
|
20027
20035
|
}
|
|
20028
|
-
/**
|
|
20029
|
-
* The CHEAPEST profile that has footage over the window.
|
|
20030
|
-
*
|
|
20031
|
-
* It used to be the profile with the MOST coverage, ties broken by name — and
|
|
20032
|
-
* that is a rule which picks `high` almost every time, because the high band is
|
|
20033
|
-
* the one that records continuously. On 2026-09-03 it did: a test render of a
|
|
20034
|
-
* ten-hour window chose `profile=high` at 97% coverage, which on this hub means
|
|
20035
|
-
* 4K read from a USB spinning disk. Decoding it made the machine stop answering
|
|
20036
|
-
* SSH and its own web UI for half an hour, and it had to be power-cycled.
|
|
20037
|
-
*
|
|
20038
|
-
* A timelapse is watched accelerated. Resolution buys nothing at 12 fps over a
|
|
20039
|
-
* night, and the cost difference between the bands is one to two orders of
|
|
20040
|
-
* magnitude in both bytes read and frames decoded. So there is no trade to
|
|
20041
|
-
* weigh: take the cheapest band that has anything, every time.
|
|
20042
|
-
*
|
|
20043
|
-
* Coverage no longer decides WHICH — only whether a band qualifies at all. A
|
|
20044
|
-
* band with less footage is still the right source; the gaps show as a shorter
|
|
20045
|
-
* video, not as a reason to read twenty times the bytes.
|
|
20046
|
-
*/
|
|
20047
20036
|
function pickProfile(ranges, window) {
|
|
20048
20037
|
const byProfile = /* @__PURE__ */ new Map();
|
|
20049
20038
|
for (const r of ranges) {
|
|
@@ -20290,18 +20279,20 @@ var TimelapseRenderer = class {
|
|
|
20290
20279
|
const finished = await this.awaitReady(queued, tags);
|
|
20291
20280
|
const published = await this.publish(finished.id, request, tags);
|
|
20292
20281
|
const downloadUrl = await this.ports.resolveDownloadUrl?.({ exportId: finished.id }).catch(() => null);
|
|
20282
|
+
const archive = await this.measureArchive(finished.id, request, chosen, tags);
|
|
20283
|
+
const deliveredRows = archive?.rows ?? availability.filter((r) => r.profile === chosen.profile);
|
|
20293
20284
|
await this.verifyPacing({
|
|
20294
20285
|
exportId: finished.id,
|
|
20295
20286
|
request,
|
|
20296
20287
|
dense,
|
|
20297
20288
|
subjects,
|
|
20298
|
-
availability:
|
|
20289
|
+
availability: deliveredRows,
|
|
20299
20290
|
tags
|
|
20300
20291
|
});
|
|
20301
20292
|
return {
|
|
20302
20293
|
exportId: finished.id,
|
|
20303
20294
|
profile: chosen.profile,
|
|
20304
|
-
coverage: chosen.coverage,
|
|
20295
|
+
coverage: archive?.coverage ?? chosen.coverage,
|
|
20305
20296
|
plan,
|
|
20306
20297
|
detections,
|
|
20307
20298
|
artifact: published.video,
|
|
@@ -20409,6 +20400,96 @@ var TimelapseRenderer = class {
|
|
|
20409
20400
|
throw new TimelapseRenderError("export-timeout", `export ${queued.id} timed out after ${this.timeoutMs}ms` + (pollFailures > 0 ? ` (${pollFailures} poll failures, last: ${lastPollError})` : ""));
|
|
20410
20401
|
}
|
|
20411
20402
|
/**
|
|
20403
|
+
* The window against the archive, measured AFTER the export and always said.
|
|
20404
|
+
*
|
|
20405
|
+
* Three figures, one line: the window's length, what the PLAN was cut from
|
|
20406
|
+
* (the availability read before the export) and what the ARCHIVE holds (the
|
|
20407
|
+
* same read after it — the export's verified walk has repaired the index the
|
|
20408
|
+
* calendar serves from by then). `plannedOfArchive` is the ratio D445 found
|
|
20409
|
+
* at 0.20 on camera 4374 and nothing reported; a healthy night reads 1.00
|
|
20410
|
+
* because both reads return the same rows.
|
|
20411
|
+
*
|
|
20412
|
+
* WHY THE THRESHOLD IS A SLACK AND NOT A RATIO. The two reads are the SAME
|
|
20413
|
+
* query against the SAME index, so on a healthy night they return the same
|
|
20414
|
+
* rows and the shortfall is exactly 0 — there is no distribution to pick a
|
|
20415
|
+
* percentile out of. It is non-zero only when something CHANGED the index
|
|
20416
|
+
* between them, and the export's verified walk is the only thing that does.
|
|
20417
|
+
* So the bar is not "how short is too short" (the robot's night was 0.20 and
|
|
20418
|
+
* a milder one would be just as wrong); it is only "is this a real change or
|
|
20419
|
+
* a boundary artefact". One artefact exists: the window is closed, but a
|
|
20420
|
+
* segment straddling its end can finalize while the export runs and extend
|
|
20421
|
+
* the last range by its own length. {@link COVERAGE_COMPLETE_SLACK_MS} (30 s)
|
|
20422
|
+
* is the slack the coverage figure already allows at exactly those edges,
|
|
20423
|
+
* and it is several segments wide. No second constant, and nothing between
|
|
20424
|
+
* "unchanged" and "the planner rendered from a short answer".
|
|
20425
|
+
*
|
|
20426
|
+
* Never throws. A re-read that fails is SAID, the plan's figure stands, and
|
|
20427
|
+
* every figure that needed the archive is `null` rather than a stand-in.
|
|
20428
|
+
*/
|
|
20429
|
+
async measureArchive(exportId, request, chosen, tags) {
|
|
20430
|
+
const { window } = request;
|
|
20431
|
+
const windowSec = Math.round((window.endMs - window.startMs) / 1e3);
|
|
20432
|
+
const plannedSec = Math.round(chosen.coverage.coveredMs / 1e3);
|
|
20433
|
+
let rows;
|
|
20434
|
+
try {
|
|
20435
|
+
rows = (await this.ports.getAvailability({
|
|
20436
|
+
deviceId: request.deviceId,
|
|
20437
|
+
fromMs: window.startMs,
|
|
20438
|
+
toMs: window.endMs,
|
|
20439
|
+
profile: chosen.profile
|
|
20440
|
+
})).ranges.filter((r) => r.profile === chosen.profile);
|
|
20441
|
+
} catch (err) {
|
|
20442
|
+
this.ports.logger.warn("timelapse coverage: archive re-read failed — the window is reported against the plan only", {
|
|
20443
|
+
tags,
|
|
20444
|
+
meta: {
|
|
20445
|
+
exportId,
|
|
20446
|
+
ruleId: request.ruleId,
|
|
20447
|
+
profile: chosen.profile,
|
|
20448
|
+
windowSec,
|
|
20449
|
+
plannedSec,
|
|
20450
|
+
plannedOfWindow: ratio(chosen.coverage.coveredMs, window.endMs - window.startMs),
|
|
20451
|
+
archiveSec: null,
|
|
20452
|
+
plannedOfArchive: null,
|
|
20453
|
+
archiveOfWindow: null,
|
|
20454
|
+
error: String(err)
|
|
20455
|
+
}
|
|
20456
|
+
});
|
|
20457
|
+
return null;
|
|
20458
|
+
}
|
|
20459
|
+
const coverage = footageCoverage(rows, window.startMs, window.endMs);
|
|
20460
|
+
const shortfallMs = coverage.coveredMs - chosen.coverage.coveredMs;
|
|
20461
|
+
const meta = {
|
|
20462
|
+
exportId,
|
|
20463
|
+
ruleId: request.ruleId,
|
|
20464
|
+
profile: chosen.profile,
|
|
20465
|
+
windowSec,
|
|
20466
|
+
plannedSec,
|
|
20467
|
+
archiveSec: Math.round(coverage.coveredMs / 1e3),
|
|
20468
|
+
/** What the operator ASKED for against what the file HOLDS — the ratio
|
|
20469
|
+
* the robot's night made someone file a report over: ten hours in,
|
|
20470
|
+
* 6.4 s of video out. Low is not by itself a defect (a lazy source is
|
|
20471
|
+
* legitimately idle most of a night), which is why it never gates. */
|
|
20472
|
+
plannedOfWindow: ratio(chosen.coverage.coveredMs, window.endMs - window.startMs),
|
|
20473
|
+
archiveOfWindow: ratio(coverage.coveredMs, window.endMs - window.startMs),
|
|
20474
|
+
/** The one that IS a defect: what the planner saw against what the
|
|
20475
|
+
* export's verified walk found. 0.20 on camera 4374, 1.00 when healthy. */
|
|
20476
|
+
plannedOfArchive: ratio(chosen.coverage.coveredMs, coverage.coveredMs),
|
|
20477
|
+
shortfallSec: Math.round(shortfallMs / 1e3)
|
|
20478
|
+
};
|
|
20479
|
+
if (shortfallMs > 3e4) this.ports.logger.warn("timelapse coverage: the plan was cut from less footage than the archive holds", {
|
|
20480
|
+
tags,
|
|
20481
|
+
meta
|
|
20482
|
+
});
|
|
20483
|
+
else this.ports.logger.info("timelapse coverage: the window against the archive", {
|
|
20484
|
+
tags,
|
|
20485
|
+
meta
|
|
20486
|
+
});
|
|
20487
|
+
return {
|
|
20488
|
+
rows,
|
|
20489
|
+
coverage
|
|
20490
|
+
};
|
|
20491
|
+
}
|
|
20492
|
+
/**
|
|
20412
20493
|
* Run the pacing check against the finished file, and SAY what it found.
|
|
20413
20494
|
*
|
|
20414
20495
|
* Never throws and never blocks past its bound: the check is telemetry about
|
|
@@ -28431,6 +28512,34 @@ var MEDIA_INDEXES = [
|
|
|
28431
28512
|
"deviceId",
|
|
28432
28513
|
"sizeBytes"
|
|
28433
28514
|
]
|
|
28515
|
+
}),
|
|
28516
|
+
(
|
|
28517
|
+
/**
|
|
28518
|
+
* The per-LOCATION footprint — `COUNT(*)`/`SUM(sizeBytes) WHERE locationId
|
|
28519
|
+
* = ?`, and the legacy `locationId IS NULL` half of the same question. See
|
|
28520
|
+
* {@link MediaStore.footprintByLocation} (D388) and D447.
|
|
28521
|
+
*
|
|
28522
|
+
* **Why not `idx_media_location`.** It leads with `locationId`, so it finds
|
|
28523
|
+
* the range — and then, exactly as the `idx_media_device_size` note above
|
|
28524
|
+
* describes for `deviceId`, fetches the table row behind every entry to read
|
|
28525
|
+
* `sizeBytes`. Every row of the store is on SOME location, so the range is
|
|
28526
|
+
* the table: measured 2026-09-10/11 on `hub/sqlite-settings`, 4 128–6 962 ms
|
|
28527
|
+
* per statement, every five minutes (the occupancy refresh), all night —
|
|
28528
|
+
* `plan=["SEARCH … USING INDEX idx_media_location (locationId=?)"]` on every
|
|
28529
|
+
* WARN. That thread is synchronous, so every configuration read in the
|
|
28530
|
+
* cluster queued behind it: the orchestrator's `configMs=6640` at 23:26:11
|
|
28531
|
+
* is this statement's 6 002 ms at 23:26:10.
|
|
28532
|
+
*
|
|
28533
|
+
* With `sizeBytes` in the index the planner never touches the table
|
|
28534
|
+
* (`USING COVERING INDEX idx_media_location_size`), and the seal gate keeps
|
|
28535
|
+
* its own narrower index. Same shape, same one-off caveat as its two
|
|
28536
|
+
* siblings: ~30 bytes a row, one more b-tree per insert, and the first
|
|
28537
|
+
* `declareCollection` after this ships builds it over every existing row —
|
|
28538
|
+
* do not deploy it beside a running drain or seal.
|
|
28539
|
+
*/
|
|
28540
|
+
{
|
|
28541
|
+
name: "idx_media_location_size",
|
|
28542
|
+
columns: ["locationId", "sizeBytes"]
|
|
28434
28543
|
})
|
|
28435
28544
|
];
|
|
28436
28545
|
/** The storage location of a media row/record: its stamped `locationId`, or
|
|
@@ -65283,6 +65392,44 @@ function buildAudioLabels(input) {
|
|
|
65283
65392
|
}];
|
|
65284
65393
|
}
|
|
65285
65394
|
//#endregion
|
|
65395
|
+
//#region src/pipeline-analytics/services/event-kinds-config.ts
|
|
65396
|
+
/** The honest answer for a pipeline that could not be read: nothing advertised. */
|
|
65397
|
+
var NO_EMITTABLE = {
|
|
65398
|
+
macroClasses: [],
|
|
65399
|
+
audioEnabled: false
|
|
65400
|
+
};
|
|
65401
|
+
/** Depth-first walk of the step tree; the detector is a root today, but the
|
|
65402
|
+
* question is "is it enabled anywhere", not "is it a root". */
|
|
65403
|
+
function* walkSteps(steps) {
|
|
65404
|
+
for (const step of steps) {
|
|
65405
|
+
yield step;
|
|
65406
|
+
if (step.children !== void 0) yield* walkSteps(step.children);
|
|
65407
|
+
}
|
|
65408
|
+
}
|
|
65409
|
+
/**
|
|
65410
|
+
* The macro classes an enabled `object-detection` step emits. Order follows
|
|
65411
|
+
* the authority, not the stored array, so two cameras narrowed to the same
|
|
65412
|
+
* set render the same lanes in the same order.
|
|
65413
|
+
*/
|
|
65414
|
+
function macroClassesOf(step) {
|
|
65415
|
+
const stored = step.settings?.["enabledMacroClasses"];
|
|
65416
|
+
if (!Array.isArray(stored) || stored.length === 0) return [...require_dist.FIRST_LEVEL_MACRO_CLASSES];
|
|
65417
|
+
const chosen = new Set(stored.filter((v) => typeof v === "string"));
|
|
65418
|
+
return require_dist.FIRST_LEVEL_MACRO_CLASSES.filter((macro) => chosen.has(macro));
|
|
65419
|
+
}
|
|
65420
|
+
function emittableFromPipelineConfig(config) {
|
|
65421
|
+
let macroClasses = [];
|
|
65422
|
+
for (const step of walkSteps(config.steps)) {
|
|
65423
|
+
if (step.addonId !== "object-detection" || step.enabled === false) continue;
|
|
65424
|
+
macroClasses = macroClassesOf(step);
|
|
65425
|
+
break;
|
|
65426
|
+
}
|
|
65427
|
+
return {
|
|
65428
|
+
macroClasses,
|
|
65429
|
+
audioEnabled: config.audio?.enabled === true
|
|
65430
|
+
};
|
|
65431
|
+
}
|
|
65432
|
+
//#endregion
|
|
65286
65433
|
//#region src/pipeline-analytics/services/event-kinds.ts
|
|
65287
65434
|
/**
|
|
65288
65435
|
* Extensible per-device event kinds (Part B).
|
|
@@ -78190,28 +78337,26 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78190
78337
|
};
|
|
78191
78338
|
}
|
|
78192
78339
|
/**
|
|
78193
|
-
* Every event kind the device can produce: built-ins (motion + audio),
|
|
78194
|
-
* detection classes
|
|
78195
|
-
*
|
|
78196
|
-
*
|
|
78340
|
+
* Every event kind the device can produce: built-ins (motion + audio), the
|
|
78341
|
+
* detection classes its CONFIGURED pipeline can emit, and sensor kinds from
|
|
78342
|
+
* LINKED devices (device-manager `getLinkedDevices`, binding-driven per
|
|
78343
|
+
* linked device). Degrades to the built-ins on error.
|
|
78344
|
+
*
|
|
78345
|
+
* The detection and audio facts are read from `resolvePipeline` — the
|
|
78346
|
+
* configuration — and never from `getCameraStatus`, which is RUNTIME: an
|
|
78347
|
+
* on-motion camera holds no session between motion events
|
|
78348
|
+
* (`reasons.detection: 'armed:on-motion'`, `detection: null`) and was being
|
|
78349
|
+
* advertised as detecting nothing while its tracks said person / vehicle /
|
|
78350
|
+
* animal ([D449](../../../../docs/decisions/adr-0449-the-timeline-taxonomy-is-the-configured-one-raw-forward-has-no-switch-and-a-one-row-write-waits-on-the-disk.md)).
|
|
78197
78351
|
*/
|
|
78198
78352
|
async listEventKinds(input) {
|
|
78199
78353
|
const api = this.ctx.api;
|
|
78200
|
-
const
|
|
78201
|
-
this.ctx.logger.warn("listEventKinds: getCameraStatus failed", {
|
|
78202
|
-
tags: { deviceId: input.deviceId },
|
|
78203
|
-
meta: { error: require_dist.errMsg(err) }
|
|
78204
|
-
});
|
|
78205
|
-
return null;
|
|
78206
|
-
});
|
|
78207
|
-
const detectionOn = status?.detection != null;
|
|
78208
|
-
const audioOn = status?.audio?.enabled === true;
|
|
78209
|
-
const detectionMacros = require_dist.MACRO_LABELS.map((l) => l.id).filter((id) => id !== "package");
|
|
78354
|
+
const emittable = await this.emittableFromConfig(input.deviceId, "listEventKinds");
|
|
78210
78355
|
return composeEventKinds({
|
|
78211
78356
|
linkedDevices: { getLinkedDevices: (i) => api.deviceManager.getLinkedDevices.query(i) },
|
|
78212
78357
|
bindings: { getBindings: (i) => api.deviceManager.getBindings.query(i) },
|
|
78213
|
-
enabledMacroClasses: async () =>
|
|
78214
|
-
audioEnabled: async () =>
|
|
78358
|
+
enabledMacroClasses: async () => emittable.macroClasses,
|
|
78359
|
+
audioEnabled: async () => emittable.audioEnabled,
|
|
78215
78360
|
packageZonesEnabled: async (deviceId) => {
|
|
78216
78361
|
return (await this.resolveDevicePackageRules(deviceId)).some((r) => r.enabled !== false);
|
|
78217
78362
|
},
|
|
@@ -78225,6 +78370,24 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78225
78370
|
}, input.deviceId);
|
|
78226
78371
|
}
|
|
78227
78372
|
/**
|
|
78373
|
+
* What one camera's configured pipeline can emit (`resolvePipeline`), or
|
|
78374
|
+
* the honest fallback when the orchestrator could not answer: nothing
|
|
78375
|
+
* advertised, and a WARN saying so. The cap's answer is a plain list, so it
|
|
78376
|
+
* has no room for "unknown" — that residual of D315 is named in D449; the
|
|
78377
|
+
* steady state (an armed camera with no session) is no longer in it.
|
|
78378
|
+
*/
|
|
78379
|
+
async emittableFromConfig(deviceId, scope) {
|
|
78380
|
+
try {
|
|
78381
|
+
return emittableFromPipelineConfig(await this.ctx.api.pipelineOrchestrator.resolvePipeline.query({ deviceId }));
|
|
78382
|
+
} catch (err) {
|
|
78383
|
+
this.ctx.logger.warn(`${scope}: resolvePipeline failed — no ML kinds advertised`, {
|
|
78384
|
+
tags: { deviceId },
|
|
78385
|
+
meta: { error: require_dist.errMsg(err) }
|
|
78386
|
+
});
|
|
78387
|
+
return NO_EMITTABLE;
|
|
78388
|
+
}
|
|
78389
|
+
}
|
|
78390
|
+
/**
|
|
78228
78391
|
* `listEventKinds` for many cameras, in one call.
|
|
78229
78392
|
*
|
|
78230
78393
|
* The saving is not the round-trips the CALLER avoids — it is the fan-out
|
|
@@ -78236,11 +78399,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78236
78399
|
* `getLinkedDevices` alone took 4110 ms — each one runs a full-fleet
|
|
78237
78400
|
* `listAll` inside device-manager and thirty of them QUEUE.
|
|
78238
78401
|
*
|
|
78239
|
-
* Now
|
|
78240
|
-
* `
|
|
78241
|
-
* `
|
|
78242
|
-
*
|
|
78243
|
-
* maps instead of RPC.
|
|
78402
|
+
* Now two calls for the whole topology, whatever N is —
|
|
78403
|
+
* `prefetchDeviceTopology`'s `getLinkedDevicesBatch` + `getBindingsBatch` —
|
|
78404
|
+
* plus one `resolvePipeline` per camera, all in flight at once (D449; there
|
|
78405
|
+
* is no batch form of it). `composeEventKinds` is unchanged — it is handed
|
|
78406
|
+
* the same per-device client interfaces, backed by maps instead of RPC.
|
|
78244
78407
|
*
|
|
78245
78408
|
* A camera whose composition fails yields an EMPTY list rather than dropping
|
|
78246
78409
|
* out of the response: a caller that asked for twelve and got eleven cannot
|
|
@@ -78249,15 +78412,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78249
78412
|
async listEventKindsBatch(input) {
|
|
78250
78413
|
const api = this.ctx.api;
|
|
78251
78414
|
const deviceIds = [...new Set(input.deviceIds)];
|
|
78252
|
-
const
|
|
78253
|
-
this.ctx.logger.warn("listEventKindsBatch: getCameraStatuses failed", { meta: {
|
|
78254
|
-
deviceCount: deviceIds.length,
|
|
78255
|
-
error: require_dist.errMsg(err)
|
|
78256
|
-
} });
|
|
78257
|
-
return [];
|
|
78258
|
-
});
|
|
78259
|
-
const statusByDevice = new Map(statuses.map((st) => [st.deviceId, st]));
|
|
78260
|
-
const detectionMacros = require_dist.MACRO_LABELS.map((l) => l.id).filter((id) => id !== "package");
|
|
78415
|
+
const emittableByDevice = new Map(await Promise.all(deviceIds.map(async (deviceId) => [deviceId, await this.emittableFromConfig(deviceId, "listEventKindsBatch")])));
|
|
78261
78416
|
const topology = await prefetchDeviceTopology({
|
|
78262
78417
|
getLinkedDevicesBatch: (i) => api.deviceManager.getLinkedDevicesBatch.query(i),
|
|
78263
78418
|
getBindingsBatch: (i) => api.deviceManager.getBindingsBatch.query(i)
|
|
@@ -78269,14 +78424,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78269
78424
|
return null;
|
|
78270
78425
|
});
|
|
78271
78426
|
return composeEventKindsForDevices(deviceIds, async (deviceId) => {
|
|
78272
|
-
const
|
|
78273
|
-
const detectionOn = status?.detection != null;
|
|
78274
|
-
const audioOn = status?.audio?.enabled === true;
|
|
78427
|
+
const emittable = emittableByDevice.get(deviceId) ?? NO_EMITTABLE;
|
|
78275
78428
|
return composeEventKinds({
|
|
78276
78429
|
linkedDevices: topology?.linkedDevices ?? { getLinkedDevices: (i) => api.deviceManager.getLinkedDevices.query(i) },
|
|
78277
78430
|
bindings: topology?.bindings ?? { getBindings: (i) => api.deviceManager.getBindings.query(i) },
|
|
78278
|
-
enabledMacroClasses: async () =>
|
|
78279
|
-
audioEnabled: async () =>
|
|
78431
|
+
enabledMacroClasses: async () => emittable.macroClasses,
|
|
78432
|
+
audioEnabled: async () => emittable.audioEnabled,
|
|
78280
78433
|
packageZonesEnabled: async (id) => {
|
|
78281
78434
|
return (await this.resolveDevicePackageRules(id)).some((r) => r.enabled !== false);
|
|
78282
78435
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as VISIT_MERGE_GAP_MS, $t as object, A as NcRulePatchSchema, Bt as CamProfileSchema, C as NC_ALARM_SYSTEM_EVENT_KINDS, D as NC_TAXONOMY, Dt as readDeviceStateFrom, Et as plateGalleryCapability, F as NcSnoozeSchema, Ft as vectorDimFromBase64, G as SCENE_DEFAULT_UNCOVERED_POLICY, Gt as nodePin, H as RetrainStatusSchema, Ht as createEvent, I as NcSnoozeSuppressedSchema, It as videoclipsCapability, J as TIMELAPSE_DENSE_FLOOR_SEC, Jt as array, K as SCENE_DIVERGED, Kt as sleep$1, L as NcSystemEventKindSchema, Lt as zoneAnalyticsCapability, M as NcRuleTargetSchema, Mt as storageOccupancyCapability, N as NcScheduleSchema, Nt as subKindsOf, O as NcConditionDescriptorSchema, Ot as readTimelapseGeneratedAt, P as NcSnoozeInputSchema, Pt as systemEventFilterApplies, Q as TrackSourceSchema, Qt as number, R as NcTaxonomySchema, Rt as errMsg$1, S as MediaFileKindEnum, St as notificationRulesCapability, T as NC_DEFAULT_SNOOZE_MINUTES, Tt as pipelineAnalyticsCapability, Ut as hydrateSchema, V as RECORDING_EXPORT_MAX_READ_BYTES, Vt as DeviceType, W as SCENE_DEFAULT_ANCHOR_THRESHOLD, Wt as isDeviceScopedCap, X as TimelapseRulePatchSchema, Xt as discriminatedUnion, Y as TimelapseRuleInputSchema, Yt as boolean, Z as TimelapseRuleSchema, Zt as literal, _ as
|
|
1
|
+
import { $ as VISIT_MERGE_GAP_MS, $t as object, A as NcRulePatchSchema, Bt as CamProfileSchema, C as NC_ALARM_SYSTEM_EVENT_KINDS, D as NC_TAXONOMY, Dt as readDeviceStateFrom, Et as plateGalleryCapability, F as NcSnoozeSchema, Ft as vectorDimFromBase64, G as SCENE_DEFAULT_UNCOVERED_POLICY, Gt as nodePin, H as RetrainStatusSchema, Ht as createEvent, I as NcSnoozeSuppressedSchema, It as videoclipsCapability, J as TIMELAPSE_DENSE_FLOOR_SEC, Jt as array, K as SCENE_DIVERGED, Kt as sleep$1, L as NcSystemEventKindSchema, Lt as zoneAnalyticsCapability, M as NcRuleTargetSchema, Mt as storageOccupancyCapability, N as NcScheduleSchema, Nt as subKindsOf, O as NcConditionDescriptorSchema, Ot as readTimelapseGeneratedAt, P as NcSnoozeInputSchema, Pt as systemEventFilterApplies, Q as TrackSourceSchema, Qt as number, R as NcTaxonomySchema, Rt as errMsg$1, S as MediaFileKindEnum, St as notificationRulesCapability, T as NC_DEFAULT_SNOOZE_MINUTES, Tt as pipelineAnalyticsCapability, Ut as hydrateSchema, V as RECORDING_EXPORT_MAX_READ_BYTES, Vt as DeviceType, W as SCENE_DEFAULT_ANCHOR_THRESHOLD, Wt as isDeviceScopedCap, X as TimelapseRulePatchSchema, Xt as discriminatedUnion, Y as TimelapseRuleInputSchema, Yt as boolean, Z as TimelapseRuleSchema, Zt as literal, _ as FailureCounters, _t as isDetectionMacroClass, a as CLUSTER_MODEL_SCOPED_STEPS, at as buildEventKindDescriptor, b as MAX_BIRTH_DECISION_RECORDS, bt as kebabToCamel, ct as defineCustomActions, dt as encodeVectorBase64, en as partialRecord, et as addonWidgetsSourceCapability, f as DeclaredDevices, ft as evaluateSensorEdge, g as FULL_IMAGE_BBOX, h as FIRST_LEVEL_MACRO_CLASSES, ht as failureContributionCapability, i as BirthDecisionRecordSchema, in as EventCategory, it as audioModeOf, j as NcRuleSchema, jt as sceneMonitorCapability, k as NcRuleInputSchema, kt as resolveLocationMode, lt as deriveRecordingMode, m as EVENT_PAD_MS, mt as faceGalleryCapability, n as ArchivedDebugNoteSchema, nn as string, nt as assertTimelapseCadences, o as COCO_TO_MACRO, ot as cosineSimilarity$1, p as EVENT_KIND_BY_CAP, pt as evictionPolicyOfLocation, q as SceneMonitorSchema, qt as _enum, r as BaseDevice, rn as unknown, rt as audioMetricsCapability, s as DEFAULT_EVENT_COLOR, st as customAction, t as AUDIO_MACRO_LABELS, tn as record, tt as alarmPanelCapability, u as DETECTION_MACRO_CLASSES, v as LabelAttributionSchema, vt as isScheduleActive, w as NC_CONDITION_CATALOG, wt as pickClusterStepModels, xt as mayWriteToLocation, y as MAX_ARCHIVED_DEBUG_NOTES, yt as isSourceCap, z as OpsLogEntrySchema, zt as BaseAddon } from "../dist-DWXR9KNe.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -19980,6 +19980,14 @@ function checkTimelapsePacing(input) {
|
|
|
19980
19980
|
unservedWindows: []
|
|
19981
19981
|
};
|
|
19982
19982
|
}
|
|
19983
|
+
//#endregion
|
|
19984
|
+
//#region src/notification-center/timelapse/timelapse-renderer.ts
|
|
19985
|
+
/** `part / whole` to two decimals — the shape a log line is read in. An empty
|
|
19986
|
+
* whole is 0, never NaN: a ratio nobody can compare is not a measurement. */
|
|
19987
|
+
function ratio(part, whole) {
|
|
19988
|
+
if (whole <= 0) return 0;
|
|
19989
|
+
return Math.round(part / whole * 100) / 100;
|
|
19990
|
+
}
|
|
19983
19991
|
/** Bound on the post-render pacing check. The check is best-effort by charter:
|
|
19984
19992
|
* a byte read that hangs must delay delivery by at most this, never sink it. */
|
|
19985
19993
|
var PACING_CHECK_TIMEOUT_MS = 2e4;
|
|
@@ -20003,25 +20011,6 @@ function profileCost(profile) {
|
|
|
20003
20011
|
const i = PROFILE_BY_COST.indexOf(profile);
|
|
20004
20012
|
return i === -1 ? PROFILE_BY_COST.length : i;
|
|
20005
20013
|
}
|
|
20006
|
-
/**
|
|
20007
|
-
* The CHEAPEST profile that has footage over the window.
|
|
20008
|
-
*
|
|
20009
|
-
* It used to be the profile with the MOST coverage, ties broken by name — and
|
|
20010
|
-
* that is a rule which picks `high` almost every time, because the high band is
|
|
20011
|
-
* the one that records continuously. On 2026-09-03 it did: a test render of a
|
|
20012
|
-
* ten-hour window chose `profile=high` at 97% coverage, which on this hub means
|
|
20013
|
-
* 4K read from a USB spinning disk. Decoding it made the machine stop answering
|
|
20014
|
-
* SSH and its own web UI for half an hour, and it had to be power-cycled.
|
|
20015
|
-
*
|
|
20016
|
-
* A timelapse is watched accelerated. Resolution buys nothing at 12 fps over a
|
|
20017
|
-
* night, and the cost difference between the bands is one to two orders of
|
|
20018
|
-
* magnitude in both bytes read and frames decoded. So there is no trade to
|
|
20019
|
-
* weigh: take the cheapest band that has anything, every time.
|
|
20020
|
-
*
|
|
20021
|
-
* Coverage no longer decides WHICH — only whether a band qualifies at all. A
|
|
20022
|
-
* band with less footage is still the right source; the gaps show as a shorter
|
|
20023
|
-
* video, not as a reason to read twenty times the bytes.
|
|
20024
|
-
*/
|
|
20025
20014
|
function pickProfile(ranges, window) {
|
|
20026
20015
|
const byProfile = /* @__PURE__ */ new Map();
|
|
20027
20016
|
for (const r of ranges) {
|
|
@@ -20268,18 +20257,20 @@ var TimelapseRenderer = class {
|
|
|
20268
20257
|
const finished = await this.awaitReady(queued, tags);
|
|
20269
20258
|
const published = await this.publish(finished.id, request, tags);
|
|
20270
20259
|
const downloadUrl = await this.ports.resolveDownloadUrl?.({ exportId: finished.id }).catch(() => null);
|
|
20260
|
+
const archive = await this.measureArchive(finished.id, request, chosen, tags);
|
|
20261
|
+
const deliveredRows = archive?.rows ?? availability.filter((r) => r.profile === chosen.profile);
|
|
20271
20262
|
await this.verifyPacing({
|
|
20272
20263
|
exportId: finished.id,
|
|
20273
20264
|
request,
|
|
20274
20265
|
dense,
|
|
20275
20266
|
subjects,
|
|
20276
|
-
availability:
|
|
20267
|
+
availability: deliveredRows,
|
|
20277
20268
|
tags
|
|
20278
20269
|
});
|
|
20279
20270
|
return {
|
|
20280
20271
|
exportId: finished.id,
|
|
20281
20272
|
profile: chosen.profile,
|
|
20282
|
-
coverage: chosen.coverage,
|
|
20273
|
+
coverage: archive?.coverage ?? chosen.coverage,
|
|
20283
20274
|
plan,
|
|
20284
20275
|
detections,
|
|
20285
20276
|
artifact: published.video,
|
|
@@ -20387,6 +20378,96 @@ var TimelapseRenderer = class {
|
|
|
20387
20378
|
throw new TimelapseRenderError("export-timeout", `export ${queued.id} timed out after ${this.timeoutMs}ms` + (pollFailures > 0 ? ` (${pollFailures} poll failures, last: ${lastPollError})` : ""));
|
|
20388
20379
|
}
|
|
20389
20380
|
/**
|
|
20381
|
+
* The window against the archive, measured AFTER the export and always said.
|
|
20382
|
+
*
|
|
20383
|
+
* Three figures, one line: the window's length, what the PLAN was cut from
|
|
20384
|
+
* (the availability read before the export) and what the ARCHIVE holds (the
|
|
20385
|
+
* same read after it — the export's verified walk has repaired the index the
|
|
20386
|
+
* calendar serves from by then). `plannedOfArchive` is the ratio D445 found
|
|
20387
|
+
* at 0.20 on camera 4374 and nothing reported; a healthy night reads 1.00
|
|
20388
|
+
* because both reads return the same rows.
|
|
20389
|
+
*
|
|
20390
|
+
* WHY THE THRESHOLD IS A SLACK AND NOT A RATIO. The two reads are the SAME
|
|
20391
|
+
* query against the SAME index, so on a healthy night they return the same
|
|
20392
|
+
* rows and the shortfall is exactly 0 — there is no distribution to pick a
|
|
20393
|
+
* percentile out of. It is non-zero only when something CHANGED the index
|
|
20394
|
+
* between them, and the export's verified walk is the only thing that does.
|
|
20395
|
+
* So the bar is not "how short is too short" (the robot's night was 0.20 and
|
|
20396
|
+
* a milder one would be just as wrong); it is only "is this a real change or
|
|
20397
|
+
* a boundary artefact". One artefact exists: the window is closed, but a
|
|
20398
|
+
* segment straddling its end can finalize while the export runs and extend
|
|
20399
|
+
* the last range by its own length. {@link COVERAGE_COMPLETE_SLACK_MS} (30 s)
|
|
20400
|
+
* is the slack the coverage figure already allows at exactly those edges,
|
|
20401
|
+
* and it is several segments wide. No second constant, and nothing between
|
|
20402
|
+
* "unchanged" and "the planner rendered from a short answer".
|
|
20403
|
+
*
|
|
20404
|
+
* Never throws. A re-read that fails is SAID, the plan's figure stands, and
|
|
20405
|
+
* every figure that needed the archive is `null` rather than a stand-in.
|
|
20406
|
+
*/
|
|
20407
|
+
async measureArchive(exportId, request, chosen, tags) {
|
|
20408
|
+
const { window } = request;
|
|
20409
|
+
const windowSec = Math.round((window.endMs - window.startMs) / 1e3);
|
|
20410
|
+
const plannedSec = Math.round(chosen.coverage.coveredMs / 1e3);
|
|
20411
|
+
let rows;
|
|
20412
|
+
try {
|
|
20413
|
+
rows = (await this.ports.getAvailability({
|
|
20414
|
+
deviceId: request.deviceId,
|
|
20415
|
+
fromMs: window.startMs,
|
|
20416
|
+
toMs: window.endMs,
|
|
20417
|
+
profile: chosen.profile
|
|
20418
|
+
})).ranges.filter((r) => r.profile === chosen.profile);
|
|
20419
|
+
} catch (err) {
|
|
20420
|
+
this.ports.logger.warn("timelapse coverage: archive re-read failed — the window is reported against the plan only", {
|
|
20421
|
+
tags,
|
|
20422
|
+
meta: {
|
|
20423
|
+
exportId,
|
|
20424
|
+
ruleId: request.ruleId,
|
|
20425
|
+
profile: chosen.profile,
|
|
20426
|
+
windowSec,
|
|
20427
|
+
plannedSec,
|
|
20428
|
+
plannedOfWindow: ratio(chosen.coverage.coveredMs, window.endMs - window.startMs),
|
|
20429
|
+
archiveSec: null,
|
|
20430
|
+
plannedOfArchive: null,
|
|
20431
|
+
archiveOfWindow: null,
|
|
20432
|
+
error: String(err)
|
|
20433
|
+
}
|
|
20434
|
+
});
|
|
20435
|
+
return null;
|
|
20436
|
+
}
|
|
20437
|
+
const coverage = footageCoverage(rows, window.startMs, window.endMs);
|
|
20438
|
+
const shortfallMs = coverage.coveredMs - chosen.coverage.coveredMs;
|
|
20439
|
+
const meta = {
|
|
20440
|
+
exportId,
|
|
20441
|
+
ruleId: request.ruleId,
|
|
20442
|
+
profile: chosen.profile,
|
|
20443
|
+
windowSec,
|
|
20444
|
+
plannedSec,
|
|
20445
|
+
archiveSec: Math.round(coverage.coveredMs / 1e3),
|
|
20446
|
+
/** What the operator ASKED for against what the file HOLDS — the ratio
|
|
20447
|
+
* the robot's night made someone file a report over: ten hours in,
|
|
20448
|
+
* 6.4 s of video out. Low is not by itself a defect (a lazy source is
|
|
20449
|
+
* legitimately idle most of a night), which is why it never gates. */
|
|
20450
|
+
plannedOfWindow: ratio(chosen.coverage.coveredMs, window.endMs - window.startMs),
|
|
20451
|
+
archiveOfWindow: ratio(coverage.coveredMs, window.endMs - window.startMs),
|
|
20452
|
+
/** The one that IS a defect: what the planner saw against what the
|
|
20453
|
+
* export's verified walk found. 0.20 on camera 4374, 1.00 when healthy. */
|
|
20454
|
+
plannedOfArchive: ratio(chosen.coverage.coveredMs, coverage.coveredMs),
|
|
20455
|
+
shortfallSec: Math.round(shortfallMs / 1e3)
|
|
20456
|
+
};
|
|
20457
|
+
if (shortfallMs > 3e4) this.ports.logger.warn("timelapse coverage: the plan was cut from less footage than the archive holds", {
|
|
20458
|
+
tags,
|
|
20459
|
+
meta
|
|
20460
|
+
});
|
|
20461
|
+
else this.ports.logger.info("timelapse coverage: the window against the archive", {
|
|
20462
|
+
tags,
|
|
20463
|
+
meta
|
|
20464
|
+
});
|
|
20465
|
+
return {
|
|
20466
|
+
rows,
|
|
20467
|
+
coverage
|
|
20468
|
+
};
|
|
20469
|
+
}
|
|
20470
|
+
/**
|
|
20390
20471
|
* Run the pacing check against the finished file, and SAY what it found.
|
|
20391
20472
|
*
|
|
20392
20473
|
* Never throws and never blocks past its bound: the check is telemetry about
|
|
@@ -28409,6 +28490,34 @@ var MEDIA_INDEXES = [
|
|
|
28409
28490
|
"deviceId",
|
|
28410
28491
|
"sizeBytes"
|
|
28411
28492
|
]
|
|
28493
|
+
}),
|
|
28494
|
+
(
|
|
28495
|
+
/**
|
|
28496
|
+
* The per-LOCATION footprint — `COUNT(*)`/`SUM(sizeBytes) WHERE locationId
|
|
28497
|
+
* = ?`, and the legacy `locationId IS NULL` half of the same question. See
|
|
28498
|
+
* {@link MediaStore.footprintByLocation} (D388) and D447.
|
|
28499
|
+
*
|
|
28500
|
+
* **Why not `idx_media_location`.** It leads with `locationId`, so it finds
|
|
28501
|
+
* the range — and then, exactly as the `idx_media_device_size` note above
|
|
28502
|
+
* describes for `deviceId`, fetches the table row behind every entry to read
|
|
28503
|
+
* `sizeBytes`. Every row of the store is on SOME location, so the range is
|
|
28504
|
+
* the table: measured 2026-09-10/11 on `hub/sqlite-settings`, 4 128–6 962 ms
|
|
28505
|
+
* per statement, every five minutes (the occupancy refresh), all night —
|
|
28506
|
+
* `plan=["SEARCH … USING INDEX idx_media_location (locationId=?)"]` on every
|
|
28507
|
+
* WARN. That thread is synchronous, so every configuration read in the
|
|
28508
|
+
* cluster queued behind it: the orchestrator's `configMs=6640` at 23:26:11
|
|
28509
|
+
* is this statement's 6 002 ms at 23:26:10.
|
|
28510
|
+
*
|
|
28511
|
+
* With `sizeBytes` in the index the planner never touches the table
|
|
28512
|
+
* (`USING COVERING INDEX idx_media_location_size`), and the seal gate keeps
|
|
28513
|
+
* its own narrower index. Same shape, same one-off caveat as its two
|
|
28514
|
+
* siblings: ~30 bytes a row, one more b-tree per insert, and the first
|
|
28515
|
+
* `declareCollection` after this ships builds it over every existing row —
|
|
28516
|
+
* do not deploy it beside a running drain or seal.
|
|
28517
|
+
*/
|
|
28518
|
+
{
|
|
28519
|
+
name: "idx_media_location_size",
|
|
28520
|
+
columns: ["locationId", "sizeBytes"]
|
|
28412
28521
|
})
|
|
28413
28522
|
];
|
|
28414
28523
|
/** The storage location of a media row/record: its stamped `locationId`, or
|
|
@@ -65209,6 +65318,44 @@ function buildAudioLabels(input) {
|
|
|
65209
65318
|
}];
|
|
65210
65319
|
}
|
|
65211
65320
|
//#endregion
|
|
65321
|
+
//#region src/pipeline-analytics/services/event-kinds-config.ts
|
|
65322
|
+
/** The honest answer for a pipeline that could not be read: nothing advertised. */
|
|
65323
|
+
var NO_EMITTABLE = {
|
|
65324
|
+
macroClasses: [],
|
|
65325
|
+
audioEnabled: false
|
|
65326
|
+
};
|
|
65327
|
+
/** Depth-first walk of the step tree; the detector is a root today, but the
|
|
65328
|
+
* question is "is it enabled anywhere", not "is it a root". */
|
|
65329
|
+
function* walkSteps(steps) {
|
|
65330
|
+
for (const step of steps) {
|
|
65331
|
+
yield step;
|
|
65332
|
+
if (step.children !== void 0) yield* walkSteps(step.children);
|
|
65333
|
+
}
|
|
65334
|
+
}
|
|
65335
|
+
/**
|
|
65336
|
+
* The macro classes an enabled `object-detection` step emits. Order follows
|
|
65337
|
+
* the authority, not the stored array, so two cameras narrowed to the same
|
|
65338
|
+
* set render the same lanes in the same order.
|
|
65339
|
+
*/
|
|
65340
|
+
function macroClassesOf(step) {
|
|
65341
|
+
const stored = step.settings?.["enabledMacroClasses"];
|
|
65342
|
+
if (!Array.isArray(stored) || stored.length === 0) return [...FIRST_LEVEL_MACRO_CLASSES];
|
|
65343
|
+
const chosen = new Set(stored.filter((v) => typeof v === "string"));
|
|
65344
|
+
return FIRST_LEVEL_MACRO_CLASSES.filter((macro) => chosen.has(macro));
|
|
65345
|
+
}
|
|
65346
|
+
function emittableFromPipelineConfig(config) {
|
|
65347
|
+
let macroClasses = [];
|
|
65348
|
+
for (const step of walkSteps(config.steps)) {
|
|
65349
|
+
if (step.addonId !== "object-detection" || step.enabled === false) continue;
|
|
65350
|
+
macroClasses = macroClassesOf(step);
|
|
65351
|
+
break;
|
|
65352
|
+
}
|
|
65353
|
+
return {
|
|
65354
|
+
macroClasses,
|
|
65355
|
+
audioEnabled: config.audio?.enabled === true
|
|
65356
|
+
};
|
|
65357
|
+
}
|
|
65358
|
+
//#endregion
|
|
65212
65359
|
//#region src/pipeline-analytics/services/event-kinds.ts
|
|
65213
65360
|
/**
|
|
65214
65361
|
* Extensible per-device event kinds (Part B).
|
|
@@ -78116,28 +78263,26 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78116
78263
|
};
|
|
78117
78264
|
}
|
|
78118
78265
|
/**
|
|
78119
|
-
* Every event kind the device can produce: built-ins (motion + audio),
|
|
78120
|
-
* detection classes
|
|
78121
|
-
*
|
|
78122
|
-
*
|
|
78266
|
+
* Every event kind the device can produce: built-ins (motion + audio), the
|
|
78267
|
+
* detection classes its CONFIGURED pipeline can emit, and sensor kinds from
|
|
78268
|
+
* LINKED devices (device-manager `getLinkedDevices`, binding-driven per
|
|
78269
|
+
* linked device). Degrades to the built-ins on error.
|
|
78270
|
+
*
|
|
78271
|
+
* The detection and audio facts are read from `resolvePipeline` — the
|
|
78272
|
+
* configuration — and never from `getCameraStatus`, which is RUNTIME: an
|
|
78273
|
+
* on-motion camera holds no session between motion events
|
|
78274
|
+
* (`reasons.detection: 'armed:on-motion'`, `detection: null`) and was being
|
|
78275
|
+
* advertised as detecting nothing while its tracks said person / vehicle /
|
|
78276
|
+
* animal ([D449](../../../../docs/decisions/adr-0449-the-timeline-taxonomy-is-the-configured-one-raw-forward-has-no-switch-and-a-one-row-write-waits-on-the-disk.md)).
|
|
78123
78277
|
*/
|
|
78124
78278
|
async listEventKinds(input) {
|
|
78125
78279
|
const api = this.ctx.api;
|
|
78126
|
-
const
|
|
78127
|
-
this.ctx.logger.warn("listEventKinds: getCameraStatus failed", {
|
|
78128
|
-
tags: { deviceId: input.deviceId },
|
|
78129
|
-
meta: { error: errMsg$1(err) }
|
|
78130
|
-
});
|
|
78131
|
-
return null;
|
|
78132
|
-
});
|
|
78133
|
-
const detectionOn = status?.detection != null;
|
|
78134
|
-
const audioOn = status?.audio?.enabled === true;
|
|
78135
|
-
const detectionMacros = MACRO_LABELS.map((l) => l.id).filter((id) => id !== "package");
|
|
78280
|
+
const emittable = await this.emittableFromConfig(input.deviceId, "listEventKinds");
|
|
78136
78281
|
return composeEventKinds({
|
|
78137
78282
|
linkedDevices: { getLinkedDevices: (i) => api.deviceManager.getLinkedDevices.query(i) },
|
|
78138
78283
|
bindings: { getBindings: (i) => api.deviceManager.getBindings.query(i) },
|
|
78139
|
-
enabledMacroClasses: async () =>
|
|
78140
|
-
audioEnabled: async () =>
|
|
78284
|
+
enabledMacroClasses: async () => emittable.macroClasses,
|
|
78285
|
+
audioEnabled: async () => emittable.audioEnabled,
|
|
78141
78286
|
packageZonesEnabled: async (deviceId) => {
|
|
78142
78287
|
return (await this.resolveDevicePackageRules(deviceId)).some((r) => r.enabled !== false);
|
|
78143
78288
|
},
|
|
@@ -78151,6 +78296,24 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78151
78296
|
}, input.deviceId);
|
|
78152
78297
|
}
|
|
78153
78298
|
/**
|
|
78299
|
+
* What one camera's configured pipeline can emit (`resolvePipeline`), or
|
|
78300
|
+
* the honest fallback when the orchestrator could not answer: nothing
|
|
78301
|
+
* advertised, and a WARN saying so. The cap's answer is a plain list, so it
|
|
78302
|
+
* has no room for "unknown" — that residual of D315 is named in D449; the
|
|
78303
|
+
* steady state (an armed camera with no session) is no longer in it.
|
|
78304
|
+
*/
|
|
78305
|
+
async emittableFromConfig(deviceId, scope) {
|
|
78306
|
+
try {
|
|
78307
|
+
return emittableFromPipelineConfig(await this.ctx.api.pipelineOrchestrator.resolvePipeline.query({ deviceId }));
|
|
78308
|
+
} catch (err) {
|
|
78309
|
+
this.ctx.logger.warn(`${scope}: resolvePipeline failed — no ML kinds advertised`, {
|
|
78310
|
+
tags: { deviceId },
|
|
78311
|
+
meta: { error: errMsg$1(err) }
|
|
78312
|
+
});
|
|
78313
|
+
return NO_EMITTABLE;
|
|
78314
|
+
}
|
|
78315
|
+
}
|
|
78316
|
+
/**
|
|
78154
78317
|
* `listEventKinds` for many cameras, in one call.
|
|
78155
78318
|
*
|
|
78156
78319
|
* The saving is not the round-trips the CALLER avoids — it is the fan-out
|
|
@@ -78162,11 +78325,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78162
78325
|
* `getLinkedDevices` alone took 4110 ms — each one runs a full-fleet
|
|
78163
78326
|
* `listAll` inside device-manager and thirty of them QUEUE.
|
|
78164
78327
|
*
|
|
78165
|
-
* Now
|
|
78166
|
-
* `
|
|
78167
|
-
* `
|
|
78168
|
-
*
|
|
78169
|
-
* maps instead of RPC.
|
|
78328
|
+
* Now two calls for the whole topology, whatever N is —
|
|
78329
|
+
* `prefetchDeviceTopology`'s `getLinkedDevicesBatch` + `getBindingsBatch` —
|
|
78330
|
+
* plus one `resolvePipeline` per camera, all in flight at once (D449; there
|
|
78331
|
+
* is no batch form of it). `composeEventKinds` is unchanged — it is handed
|
|
78332
|
+
* the same per-device client interfaces, backed by maps instead of RPC.
|
|
78170
78333
|
*
|
|
78171
78334
|
* A camera whose composition fails yields an EMPTY list rather than dropping
|
|
78172
78335
|
* out of the response: a caller that asked for twelve and got eleven cannot
|
|
@@ -78175,15 +78338,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78175
78338
|
async listEventKindsBatch(input) {
|
|
78176
78339
|
const api = this.ctx.api;
|
|
78177
78340
|
const deviceIds = [...new Set(input.deviceIds)];
|
|
78178
|
-
const
|
|
78179
|
-
this.ctx.logger.warn("listEventKindsBatch: getCameraStatuses failed", { meta: {
|
|
78180
|
-
deviceCount: deviceIds.length,
|
|
78181
|
-
error: errMsg$1(err)
|
|
78182
|
-
} });
|
|
78183
|
-
return [];
|
|
78184
|
-
});
|
|
78185
|
-
const statusByDevice = new Map(statuses.map((st) => [st.deviceId, st]));
|
|
78186
|
-
const detectionMacros = MACRO_LABELS.map((l) => l.id).filter((id) => id !== "package");
|
|
78341
|
+
const emittableByDevice = new Map(await Promise.all(deviceIds.map(async (deviceId) => [deviceId, await this.emittableFromConfig(deviceId, "listEventKindsBatch")])));
|
|
78187
78342
|
const topology = await prefetchDeviceTopology({
|
|
78188
78343
|
getLinkedDevicesBatch: (i) => api.deviceManager.getLinkedDevicesBatch.query(i),
|
|
78189
78344
|
getBindingsBatch: (i) => api.deviceManager.getBindingsBatch.query(i)
|
|
@@ -78195,14 +78350,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78195
78350
|
return null;
|
|
78196
78351
|
});
|
|
78197
78352
|
return composeEventKindsForDevices(deviceIds, async (deviceId) => {
|
|
78198
|
-
const
|
|
78199
|
-
const detectionOn = status?.detection != null;
|
|
78200
|
-
const audioOn = status?.audio?.enabled === true;
|
|
78353
|
+
const emittable = emittableByDevice.get(deviceId) ?? NO_EMITTABLE;
|
|
78201
78354
|
return composeEventKinds({
|
|
78202
78355
|
linkedDevices: topology?.linkedDevices ?? { getLinkedDevices: (i) => api.deviceManager.getLinkedDevices.query(i) },
|
|
78203
78356
|
bindings: topology?.bindings ?? { getBindings: (i) => api.deviceManager.getBindings.query(i) },
|
|
78204
|
-
enabledMacroClasses: async () =>
|
|
78205
|
-
audioEnabled: async () =>
|
|
78357
|
+
enabledMacroClasses: async () => emittable.macroClasses,
|
|
78358
|
+
audioEnabled: async () => emittable.audioEnabled,
|
|
78206
78359
|
packageZonesEnabled: async (id) => {
|
|
78207
78360
|
return (await this.resolveDevicePackageRules(id)).some((r) => r.enabled !== false);
|
|
78208
78361
|
},
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Bfm6wOeY.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-post-analysis",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.220",
|
|
4
4
|
"description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|