@camstack/addon-post-analysis 1.1.32 → 1.1.33
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-CyyCe4TK.mjs → dist-CA0GikiM.mjs} +193 -5
- package/dist/{dist-DU_JRm-j.js → dist-V8XOHDoj.js} +198 -4
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/{node-K1C_KC3d.js → node-E-m2CYYM.js} +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-aNR1K97R.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BbAKLasA.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-D6C5R_2c.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-ai-NpPOn.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-B3eluJsX.mjs → hostInit-lUENPwl7.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +359 -3
- package/dist/pipeline-analytics/index.mjs +358 -2
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as string, S as object, _ as createEvent, a as cosineSimilarity, b as boolean, d as plateGalleryCapability, f as videoclipsCapability, g as DeviceType, h as BaseAddon, i as audioMetricsCapability, l as nodePin, m as errMsg, n as EVENT_PAD_MS, p as zoneAnalyticsCapability, r as addonWidgetsSourceCapability, s as faceGalleryCapability, t as EVENT_KIND_BY_CAP, u as pipelineAnalyticsCapability, v as hydrateSchema, w as EventCategory, x as number, y as array } from "../dist-CA0GikiM.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import sharp from "sharp";
|
|
4
4
|
//#region src/pipeline-analytics/videoclips-provider.ts
|
|
@@ -5396,6 +5396,7 @@ var PERSON_COLOR = "#22c55e";
|
|
|
5396
5396
|
var VEHICLE_COLOR = "#3b82f6";
|
|
5397
5397
|
var ANIMAL_COLOR = "#f97316";
|
|
5398
5398
|
var GENERIC_DETECTION_COLOR = "#64748b";
|
|
5399
|
+
var PACKAGE_COLOR = "#a855f7";
|
|
5399
5400
|
var VEHICLE_CLASSES = new Set([
|
|
5400
5401
|
"vehicle",
|
|
5401
5402
|
"car",
|
|
@@ -5473,6 +5474,34 @@ async function composeEventKinds(deps, deviceId) {
|
|
|
5473
5474
|
} catch (err) {
|
|
5474
5475
|
deps.onError?.("observedClassNames", err);
|
|
5475
5476
|
}
|
|
5477
|
+
try {
|
|
5478
|
+
if (deps.packageZonesEnabled && await deps.packageZonesEnabled(deviceId)) {
|
|
5479
|
+
out.push({
|
|
5480
|
+
kind: "package-delivered",
|
|
5481
|
+
label: "Package delivered",
|
|
5482
|
+
color: PACKAGE_COLOR,
|
|
5483
|
+
icon: "package",
|
|
5484
|
+
category: "package",
|
|
5485
|
+
source: {
|
|
5486
|
+
capName: "pipeline-analytics",
|
|
5487
|
+
deviceId
|
|
5488
|
+
}
|
|
5489
|
+
});
|
|
5490
|
+
out.push({
|
|
5491
|
+
kind: "package-picked-up",
|
|
5492
|
+
label: "Package picked up",
|
|
5493
|
+
color: PACKAGE_COLOR,
|
|
5494
|
+
icon: "package",
|
|
5495
|
+
category: "package",
|
|
5496
|
+
source: {
|
|
5497
|
+
capName: "pipeline-analytics",
|
|
5498
|
+
deviceId
|
|
5499
|
+
}
|
|
5500
|
+
});
|
|
5501
|
+
}
|
|
5502
|
+
} catch (err) {
|
|
5503
|
+
deps.onError?.("packageZonesEnabled", err);
|
|
5504
|
+
}
|
|
5476
5505
|
try {
|
|
5477
5506
|
const { devices } = await deps.linkedDevices.getLinkedDevices({ deviceId });
|
|
5478
5507
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7325,6 +7354,208 @@ function resolveMediaSettings(raw) {
|
|
|
7325
7354
|
snapshotMaxIdleMs: pick("snapshotMaxIdleMs")
|
|
7326
7355
|
};
|
|
7327
7356
|
}
|
|
7357
|
+
//#endregion
|
|
7358
|
+
//#region src/pipeline-analytics/package-settings.ts
|
|
7359
|
+
/**
|
|
7360
|
+
* Per-device package-drop detector settings (surface C of the
|
|
7361
|
+
* detection-config-exposure plan — a per-device post-analysis section).
|
|
7362
|
+
* Cascade: a per-device override on top of the declared default, resolved
|
|
7363
|
+
* per field (an invalid/missing value falls back to its default — parse
|
|
7364
|
+
* never throws). Mirrors `media-settings` / `tracking-settings`.
|
|
7365
|
+
*
|
|
7366
|
+
* Keys are namespaced (`packageDrop*`) because the device store is a FLAT
|
|
7367
|
+
* blob shared across every post-analysis section — a bare `enabled` would
|
|
7368
|
+
* collide with another section.
|
|
7369
|
+
*
|
|
7370
|
+
* See docs/superpowers/specs/2026-07-17-package-zones-design.md §3.3 +
|
|
7371
|
+
* docs/superpowers/specs/2026-07-17-detection-config-exposure-design.md
|
|
7372
|
+
* (surface C). Dwell default is 15s per operator decision (the design's
|
|
7373
|
+
* §3.3 draft said 45s).
|
|
7374
|
+
*/
|
|
7375
|
+
var PackageDropSettingsSchema = object({
|
|
7376
|
+
/** Master switch — off by default; opt-in per camera (porch/door cams). */
|
|
7377
|
+
packageDropEnabled: boolean().default(false),
|
|
7378
|
+
/**
|
|
7379
|
+
* Minimum OBSERVED dwell (seconds, since first-seen) before a newly
|
|
7380
|
+
* promoted stationary package counts as a delivery. Kills a bag briefly
|
|
7381
|
+
* set down and snatched back. Operator default 15s. Values beyond the
|
|
7382
|
+
* stationary promotion window (30s) are satisfied only once the entry
|
|
7383
|
+
* has actually been observed that long.
|
|
7384
|
+
*/
|
|
7385
|
+
packageDropDwellSec: number().int().min(0).max(3600).default(15),
|
|
7386
|
+
/** Reject tiny far-field blobs: min bbox area as a fraction of the frame. */
|
|
7387
|
+
packageDropMinBboxAreaFrac: number().min(0).max(1).default(.004),
|
|
7388
|
+
/** Emit `package-picked-up` when a delivered package's entry departs. */
|
|
7389
|
+
packageDropPickupEnabled: boolean().default(true),
|
|
7390
|
+
/**
|
|
7391
|
+
* Stationary classes that count as a package. MODEL-AGNOSTIC: defaults to
|
|
7392
|
+
* the single `package` class a dedicated parcel model emits; the interim
|
|
7393
|
+
* COCO vector (suitcase/backpack/handbag) already maps to `package`
|
|
7394
|
+
* upstream, so this stays `['package']`.
|
|
7395
|
+
*/
|
|
7396
|
+
packageDropClassFilter: array(string()).default(["package"])
|
|
7397
|
+
});
|
|
7398
|
+
var PACKAGE_DROP_DEFAULTS = PackageDropSettingsSchema.parse({});
|
|
7399
|
+
/**
|
|
7400
|
+
* Resolve a per-device store blob into typed package-drop settings.
|
|
7401
|
+
* Unknown/invalid fields fall back to the default for that field (never
|
|
7402
|
+
* throws on a bad blob).
|
|
7403
|
+
*/
|
|
7404
|
+
function resolvePackageDropSettings(raw) {
|
|
7405
|
+
const pick = (key) => {
|
|
7406
|
+
const parsed = PackageDropSettingsSchema.shape[key].safeParse(raw[key]);
|
|
7407
|
+
return parsed.success ? parsed.data : PACKAGE_DROP_DEFAULTS[key];
|
|
7408
|
+
};
|
|
7409
|
+
return {
|
|
7410
|
+
packageDropEnabled: pick("packageDropEnabled"),
|
|
7411
|
+
packageDropDwellSec: pick("packageDropDwellSec"),
|
|
7412
|
+
packageDropMinBboxAreaFrac: pick("packageDropMinBboxAreaFrac"),
|
|
7413
|
+
packageDropPickupEnabled: pick("packageDropPickupEnabled"),
|
|
7414
|
+
packageDropClassFilter: pick("packageDropClassFilter")
|
|
7415
|
+
};
|
|
7416
|
+
}
|
|
7417
|
+
//#endregion
|
|
7418
|
+
//#region src/pipeline-analytics/pipeline/package-drop-detector.ts
|
|
7419
|
+
/** The class every durable package event carries. */
|
|
7420
|
+
var PACKAGE_EVENT_CLASS = "package";
|
|
7421
|
+
/** Fixed high importance — package delivery is inherently high-signal (§6). */
|
|
7422
|
+
var PACKAGE_IMPORTANCE = 1;
|
|
7423
|
+
/** Object-event `state` used for a delivery (a parked package) / a pick-up
|
|
7424
|
+
* (its departure). Both are valid `TrackState` enum members so the events
|
|
7425
|
+
* stay `getObjectEvents`-output-valid. */
|
|
7426
|
+
var DELIVERED_STATE = "idle";
|
|
7427
|
+
var PICKED_UP_STATE = "left";
|
|
7428
|
+
/** Deterministic durable-event ids keyed on the stationary entry id. */
|
|
7429
|
+
function deliveredEventId(entryId) {
|
|
7430
|
+
return `pa-pkg-${entryId}-delivered`;
|
|
7431
|
+
}
|
|
7432
|
+
function pickedUpEventId(entryId) {
|
|
7433
|
+
return `pa-pkg-${entryId}-pickedup`;
|
|
7434
|
+
}
|
|
7435
|
+
var PackageDropDetector = class {
|
|
7436
|
+
deps;
|
|
7437
|
+
constructor(deps) {
|
|
7438
|
+
this.deps = deps;
|
|
7439
|
+
}
|
|
7440
|
+
/** Single entrypoint — bridge the registry's `onChange` here. Never
|
|
7441
|
+
* throws (telemetry-lossy, D8): a failure only misses one package event. */
|
|
7442
|
+
async onStationaryChange(change) {
|
|
7443
|
+
try {
|
|
7444
|
+
if (change.phase === "appeared") await this.onAppeared(change.entry, change.timestamp);
|
|
7445
|
+
else await this.onDeparted(change.entry, change.timestamp);
|
|
7446
|
+
} catch (err) {
|
|
7447
|
+
this.deps.onError?.("onStationaryChange", err);
|
|
7448
|
+
this.deps.logger.warn("package-drop detector failed on change", {
|
|
7449
|
+
tags: { deviceId: change.entry.deviceId },
|
|
7450
|
+
meta: {
|
|
7451
|
+
phase: change.phase,
|
|
7452
|
+
entryId: change.entry.id,
|
|
7453
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7454
|
+
}
|
|
7455
|
+
});
|
|
7456
|
+
}
|
|
7457
|
+
}
|
|
7458
|
+
async onAppeared(entry, timestamp) {
|
|
7459
|
+
const settings = await this.deps.resolveSettings(entry.deviceId);
|
|
7460
|
+
if (!settings.packageDropEnabled) return;
|
|
7461
|
+
if (!settings.packageDropClassFilter.includes(entry.className)) return;
|
|
7462
|
+
const rules = (await this.deps.resolvePackageRules(entry.deviceId)).filter((r) => r.enabled !== false);
|
|
7463
|
+
if (rules.length === 0) return;
|
|
7464
|
+
const ruleZoneIds = new Set(rules.flatMap((r) => r.zoneIds));
|
|
7465
|
+
const hitZones = computeStationaryEntryZones(entry, await this.deps.resolveZones(entry.deviceId)).filter((z) => ruleZoneIds.has(z));
|
|
7466
|
+
if (hitZones.length === 0) return;
|
|
7467
|
+
if (timestamp - entry.firstSeenAt < settings.packageDropDwellSec * 1e3) return;
|
|
7468
|
+
const frameArea = entry.frameWidth * entry.frameHeight;
|
|
7469
|
+
if (frameArea <= 0) return;
|
|
7470
|
+
if (entry.bbox.w * entry.bbox.h / frameArea < settings.packageDropMinBboxAreaFrac) return;
|
|
7471
|
+
const eventId = deliveredEventId(entry.id);
|
|
7472
|
+
if ((await this.deps.events.queryObject({
|
|
7473
|
+
deviceId: entry.deviceId,
|
|
7474
|
+
classFilter: "package"
|
|
7475
|
+
})).some((e) => e.id === eventId)) return;
|
|
7476
|
+
const ev = {
|
|
7477
|
+
id: eventId,
|
|
7478
|
+
kind: "object",
|
|
7479
|
+
deviceId: entry.deviceId,
|
|
7480
|
+
timestamp,
|
|
7481
|
+
source: "pipeline",
|
|
7482
|
+
trackId: entry.sourceTrackId ?? entry.id,
|
|
7483
|
+
className: PACKAGE_EVENT_CLASS,
|
|
7484
|
+
...entry.label !== void 0 ? { label: entry.label } : {},
|
|
7485
|
+
confidence: PACKAGE_IMPORTANCE,
|
|
7486
|
+
bbox: {
|
|
7487
|
+
x: entry.bbox.x,
|
|
7488
|
+
y: entry.bbox.y,
|
|
7489
|
+
w: entry.bbox.w,
|
|
7490
|
+
h: entry.bbox.h
|
|
7491
|
+
},
|
|
7492
|
+
zones: hitZones,
|
|
7493
|
+
state: DELIVERED_STATE,
|
|
7494
|
+
frameWidth: entry.frameWidth,
|
|
7495
|
+
frameHeight: entry.frameHeight,
|
|
7496
|
+
...entry.keyFrameMediaKey !== void 0 ? { mediaKey: entry.keyFrameMediaKey } : {},
|
|
7497
|
+
importance: PACKAGE_IMPORTANCE
|
|
7498
|
+
};
|
|
7499
|
+
await this.deps.events.insertObject(ev);
|
|
7500
|
+
this.deps.emit.delivered({
|
|
7501
|
+
deviceId: entry.deviceId,
|
|
7502
|
+
entryId: entry.id,
|
|
7503
|
+
eventId,
|
|
7504
|
+
className: entry.className,
|
|
7505
|
+
zoneIds: hitZones,
|
|
7506
|
+
...entry.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: entry.keyFrameMediaKey } : {},
|
|
7507
|
+
bbox: {
|
|
7508
|
+
x: entry.bbox.x,
|
|
7509
|
+
y: entry.bbox.y,
|
|
7510
|
+
w: entry.bbox.w,
|
|
7511
|
+
h: entry.bbox.h
|
|
7512
|
+
},
|
|
7513
|
+
timestamp
|
|
7514
|
+
});
|
|
7515
|
+
}
|
|
7516
|
+
async onDeparted(entry, timestamp) {
|
|
7517
|
+
if (!(await this.deps.resolveSettings(entry.deviceId)).packageDropPickupEnabled) return;
|
|
7518
|
+
const deliveredId = deliveredEventId(entry.id);
|
|
7519
|
+
const pickedUpId = pickedUpEventId(entry.id);
|
|
7520
|
+
const rows = await this.deps.events.queryObject({
|
|
7521
|
+
deviceId: entry.deviceId,
|
|
7522
|
+
classFilter: PACKAGE_EVENT_CLASS
|
|
7523
|
+
});
|
|
7524
|
+
const delivered = rows.find((e) => e.id === deliveredId);
|
|
7525
|
+
if (delivered === void 0) return;
|
|
7526
|
+
if (rows.some((e) => e.id === pickedUpId)) return;
|
|
7527
|
+
const ev = {
|
|
7528
|
+
id: pickedUpId,
|
|
7529
|
+
kind: "object",
|
|
7530
|
+
deviceId: entry.deviceId,
|
|
7531
|
+
timestamp,
|
|
7532
|
+
source: "pipeline",
|
|
7533
|
+
trackId: entry.sourceTrackId ?? entry.id,
|
|
7534
|
+
className: PACKAGE_EVENT_CLASS,
|
|
7535
|
+
...entry.label !== void 0 ? { label: entry.label } : {},
|
|
7536
|
+
confidence: PACKAGE_IMPORTANCE,
|
|
7537
|
+
bbox: {
|
|
7538
|
+
x: entry.bbox.x,
|
|
7539
|
+
y: entry.bbox.y,
|
|
7540
|
+
w: entry.bbox.w,
|
|
7541
|
+
h: entry.bbox.h
|
|
7542
|
+
},
|
|
7543
|
+
...delivered.zones !== void 0 ? { zones: delivered.zones } : {},
|
|
7544
|
+
state: PICKED_UP_STATE,
|
|
7545
|
+
frameWidth: entry.frameWidth,
|
|
7546
|
+
frameHeight: entry.frameHeight,
|
|
7547
|
+
importance: PACKAGE_IMPORTANCE
|
|
7548
|
+
};
|
|
7549
|
+
await this.deps.events.insertObject(ev);
|
|
7550
|
+
this.deps.emit.pickedUp({
|
|
7551
|
+
deviceId: entry.deviceId,
|
|
7552
|
+
entryId: entry.id,
|
|
7553
|
+
deliveredEventId: deliveredId,
|
|
7554
|
+
className: entry.className,
|
|
7555
|
+
timestamp
|
|
7556
|
+
});
|
|
7557
|
+
}
|
|
7558
|
+
};
|
|
7328
7559
|
function centroidOf(b) {
|
|
7329
7560
|
return {
|
|
7330
7561
|
x: b.x + b.w / 2,
|
|
@@ -10805,6 +11036,9 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
10805
11036
|
/** GLOBAL face-recognition master switch (addon store), TTL-cached. */
|
|
10806
11037
|
faceGlobalEnabledCache = null;
|
|
10807
11038
|
mediaCacheByDevice = /* @__PURE__ */ new Map();
|
|
11039
|
+
packageDropCacheByDevice = /* @__PURE__ */ new Map();
|
|
11040
|
+
/** Turns stationary appear/depart into package-delivered/picked-up events. */
|
|
11041
|
+
packageDropDetector = null;
|
|
10808
11042
|
/** Consecutive detector-dropout frames skipped per (deviceId, source) key. */
|
|
10809
11043
|
dropoutSkipsByKey = /* @__PURE__ */ new Map();
|
|
10810
11044
|
/** Best (highest-confidence) frame per track — drives the single overwrite
|
|
@@ -10922,6 +11156,11 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
10922
11156
|
timestamp
|
|
10923
11157
|
}
|
|
10924
11158
|
});
|
|
11159
|
+
this.packageDropDetector?.onStationaryChange({
|
|
11160
|
+
phase,
|
|
11161
|
+
entry,
|
|
11162
|
+
timestamp
|
|
11163
|
+
});
|
|
10925
11164
|
}
|
|
10926
11165
|
});
|
|
10927
11166
|
await this.stationaryRegistry.load();
|
|
@@ -10930,15 +11169,55 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
10930
11169
|
store: api.settingsStore,
|
|
10931
11170
|
logger: logger.child("MediaStore")
|
|
10932
11171
|
});
|
|
10933
|
-
|
|
11172
|
+
const eventStore = new EventStore({
|
|
10934
11173
|
store: api.settingsStore,
|
|
10935
11174
|
logger: logger.child("EventStore"),
|
|
10936
11175
|
media: this.mediaStore
|
|
10937
11176
|
});
|
|
11177
|
+
this.eventStore = eventStore;
|
|
10938
11178
|
this.sensorEventStore = new SensorEventStore({
|
|
10939
11179
|
store: api.settingsStore,
|
|
10940
11180
|
logger: logger.child("SensorEventStore")
|
|
10941
11181
|
});
|
|
11182
|
+
this.packageDropDetector = new PackageDropDetector({
|
|
11183
|
+
events: eventStore,
|
|
11184
|
+
emit: {
|
|
11185
|
+
delivered: (payload) => {
|
|
11186
|
+
this.ctx.eventBus.emit({
|
|
11187
|
+
id: `pa-package-delivered-${payload.entryId}`,
|
|
11188
|
+
timestamp: new Date(payload.timestamp),
|
|
11189
|
+
source: {
|
|
11190
|
+
type: "addon",
|
|
11191
|
+
id: "pipeline-analytics",
|
|
11192
|
+
addonId: "pipeline-analytics"
|
|
11193
|
+
},
|
|
11194
|
+
category: EventCategory.PipelineAnalyticsPackageDelivered,
|
|
11195
|
+
data: { ...payload }
|
|
11196
|
+
});
|
|
11197
|
+
},
|
|
11198
|
+
pickedUp: (payload) => {
|
|
11199
|
+
this.ctx.eventBus.emit({
|
|
11200
|
+
id: `pa-package-pickedup-${payload.entryId}`,
|
|
11201
|
+
timestamp: new Date(payload.timestamp),
|
|
11202
|
+
source: {
|
|
11203
|
+
type: "addon",
|
|
11204
|
+
id: "pipeline-analytics",
|
|
11205
|
+
addonId: "pipeline-analytics"
|
|
11206
|
+
},
|
|
11207
|
+
category: EventCategory.PipelineAnalyticsPackagePickedUp,
|
|
11208
|
+
data: { ...payload }
|
|
11209
|
+
});
|
|
11210
|
+
}
|
|
11211
|
+
},
|
|
11212
|
+
logger: logger.child("PackageDropDetector"),
|
|
11213
|
+
resolveZones: (deviceId) => this.resolveDeviceZones(deviceId),
|
|
11214
|
+
resolvePackageRules: (deviceId) => this.resolveDevicePackageRules(deviceId),
|
|
11215
|
+
resolveSettings: (deviceId) => this.resolveDevicePackageDropSettings(deviceId),
|
|
11216
|
+
onError: (scope, err) => logger.warn("package-drop detector error", { meta: {
|
|
11217
|
+
scope,
|
|
11218
|
+
error: errMsg(err)
|
|
11219
|
+
} })
|
|
11220
|
+
});
|
|
10942
11221
|
this.linkedCamerasCache = new LinkedCamerasCache({
|
|
10943
11222
|
cameras: { listCameraIds: async () => {
|
|
10944
11223
|
return (await api.deviceManager.listAll.query({})).filter((d) => d.isCamera).map((d) => d.id);
|
|
@@ -11218,6 +11497,7 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
11218
11497
|
this.trackingCacheByDevice.delete(data.deviceId);
|
|
11219
11498
|
this.faceCacheByDevice.delete(data.deviceId);
|
|
11220
11499
|
this.mediaCacheByDevice.delete(data.deviceId);
|
|
11500
|
+
this.packageDropCacheByDevice.delete(data.deviceId);
|
|
11221
11501
|
}
|
|
11222
11502
|
});
|
|
11223
11503
|
this.unsubDeviceUnreg = this.ctx.eventBus.subscribe({ category: EventCategory.DeviceUnregistered }, (ev) => {
|
|
@@ -11233,6 +11513,7 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
11233
11513
|
this.trackingCacheByDevice.delete(deviceId);
|
|
11234
11514
|
this.faceCacheByDevice.delete(deviceId);
|
|
11235
11515
|
this.mediaCacheByDevice.delete(deviceId);
|
|
11516
|
+
this.packageDropCacheByDevice.delete(deviceId);
|
|
11236
11517
|
this.bindingCache?.invalidate(deviceId);
|
|
11237
11518
|
this.zoneAnalytics?.forgetDevice(deviceId);
|
|
11238
11519
|
this.audioMetrics?.forgetDevice(deviceId);
|
|
@@ -11580,6 +11861,7 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
11580
11861
|
this.faceCacheByDevice.clear();
|
|
11581
11862
|
this.faceGlobalEnabledCache = null;
|
|
11582
11863
|
this.mediaCacheByDevice.clear();
|
|
11864
|
+
this.packageDropCacheByDevice.clear();
|
|
11583
11865
|
this.trackStore?.clearAll();
|
|
11584
11866
|
this.stationaryRegistry = null;
|
|
11585
11867
|
this.bindingCache?.clearAll();
|
|
@@ -12055,6 +12337,32 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
12055
12337
|
});
|
|
12056
12338
|
return settings;
|
|
12057
12339
|
}
|
|
12340
|
+
async resolveDevicePackageDropSettings(deviceId) {
|
|
12341
|
+
const now = Date.now();
|
|
12342
|
+
const cached = this.packageDropCacheByDevice.get(deviceId);
|
|
12343
|
+
if (cached && now < cached.expiresAt) return cached.settings;
|
|
12344
|
+
const settings = resolvePackageDropSettings(await this.ctx?.settings?.readDeviceStore(deviceId) ?? {});
|
|
12345
|
+
this.packageDropCacheByDevice.set(deviceId, {
|
|
12346
|
+
settings,
|
|
12347
|
+
expiresAt: now + SETTINGS_CACHE_TTL_MS
|
|
12348
|
+
});
|
|
12349
|
+
return settings;
|
|
12350
|
+
}
|
|
12351
|
+
/**
|
|
12352
|
+
* Resolve a device's ENABLED `package`-stage zone rules independent of the
|
|
12353
|
+
* live frame path (mirrors `resolveDeviceZones`). Warms the proxy and,
|
|
12354
|
+
* when the cached slice is empty, forces one refresh. The `package` slice
|
|
12355
|
+
* is written by the orchestrator's package-stage provider (a later slice);
|
|
12356
|
+
* until then this returns `[]` and no package events fire.
|
|
12357
|
+
*/
|
|
12358
|
+
async resolveDevicePackageRules(deviceId) {
|
|
12359
|
+
const proxy = await this.ensureProxy(deviceId);
|
|
12360
|
+
if (!proxy) return [];
|
|
12361
|
+
const cached = proxy.state.zoneRules.value?.package;
|
|
12362
|
+
if (cached && cached.length > 0) return cached;
|
|
12363
|
+
await proxy.state.zoneRules.refresh().catch(() => void 0);
|
|
12364
|
+
return proxy.state.zoneRules.value?.package ?? [];
|
|
12365
|
+
}
|
|
12058
12366
|
/**
|
|
12059
12367
|
* Route one track's `runDetailSubtree` results (two-plane detail dispatch)
|
|
12060
12368
|
* into the EXISTING per-track consumers, discriminated by payload SHAPE:
|
|
@@ -13156,6 +13464,9 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
13156
13464
|
linkedDevices: { getLinkedDevices: (i) => api.deviceManager.getLinkedDevices.query(i) },
|
|
13157
13465
|
bindings: { getBindings: (i) => api.deviceManager.getBindings.query(i) },
|
|
13158
13466
|
observedClassNames: async (deviceId) => this.trackStore?.observedClassNames(deviceId) ?? [],
|
|
13467
|
+
packageZonesEnabled: async (deviceId) => {
|
|
13468
|
+
return (await this.resolveDevicePackageRules(deviceId)).some((r) => r.enabled !== false);
|
|
13469
|
+
},
|
|
13159
13470
|
onError: (scope, err) => this.ctx.logger.warn("listEventKinds: partial compose", {
|
|
13160
13471
|
tags: { deviceId: input.deviceId },
|
|
13161
13472
|
meta: {
|
|
@@ -13819,6 +14130,51 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
|
|
|
13819
14130
|
}
|
|
13820
14131
|
]
|
|
13821
14132
|
},
|
|
14133
|
+
{
|
|
14134
|
+
id: "package-drop",
|
|
14135
|
+
title: "Package detection",
|
|
14136
|
+
description: "Detect a delivered package (a package-class object left parked inside a package zone) and, symmetrically, its pick-up. Draw the package zone in the zone editor; enable the package class in the object-detection step. Per-device override.",
|
|
14137
|
+
columns: 2,
|
|
14138
|
+
fields: [
|
|
14139
|
+
{
|
|
14140
|
+
type: "boolean",
|
|
14141
|
+
key: "packageDropEnabled",
|
|
14142
|
+
label: "Enable package detection",
|
|
14143
|
+
description: "Turn package delivered / picked-up detection on for this camera (off elsewhere).",
|
|
14144
|
+
default: PACKAGE_DROP_DEFAULTS.packageDropEnabled
|
|
14145
|
+
},
|
|
14146
|
+
{
|
|
14147
|
+
type: "boolean",
|
|
14148
|
+
key: "packageDropPickupEnabled",
|
|
14149
|
+
label: "Emit pick-up",
|
|
14150
|
+
description: "Also emit a package-picked-up event when the delivered package leaves.",
|
|
14151
|
+
default: PACKAGE_DROP_DEFAULTS.packageDropPickupEnabled
|
|
14152
|
+
},
|
|
14153
|
+
{
|
|
14154
|
+
type: "slider",
|
|
14155
|
+
key: "packageDropDwellSec",
|
|
14156
|
+
label: "Minimum dwell",
|
|
14157
|
+
description: "How long a package must stay parked before it counts as a delivery. Higher = fewer false positives from bags briefly set down.",
|
|
14158
|
+
min: 0,
|
|
14159
|
+
max: 300,
|
|
14160
|
+
step: 5,
|
|
14161
|
+
default: PACKAGE_DROP_DEFAULTS.packageDropDwellSec,
|
|
14162
|
+
showValue: true,
|
|
14163
|
+
unit: "s"
|
|
14164
|
+
},
|
|
14165
|
+
{
|
|
14166
|
+
type: "slider",
|
|
14167
|
+
key: "packageDropMinBboxAreaFrac",
|
|
14168
|
+
label: "Minimum package size",
|
|
14169
|
+
description: "Reject tiny far-field blobs: the package box must cover at least this fraction of the frame.",
|
|
14170
|
+
min: 0,
|
|
14171
|
+
max: .1,
|
|
14172
|
+
step: .001,
|
|
14173
|
+
default: PACKAGE_DROP_DEFAULTS.packageDropMinBboxAreaFrac,
|
|
14174
|
+
showValue: true
|
|
14175
|
+
}
|
|
14176
|
+
]
|
|
14177
|
+
},
|
|
13822
14178
|
{
|
|
13823
14179
|
id: "audio-detection",
|
|
13824
14180
|
title: "Audio detection",
|
|
@@ -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-BbAKLasA.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.1.
|
|
3
|
+
"version": "1.1.33",
|
|
4
4
|
"description": "CamStack Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|