@camstack/addon-post-analysis 1.2.663 → 1.2.665
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/CHANGELOG.md +14 -0
- package/dist/{clip-model-registry-CwXJyrLg.mjs → clip-model-registry-DZTzG2p4.mjs} +377 -132
- package/dist/{clip-model-registry-Cz8LWYwU.js → clip-model-registry-nn_STiGt.js} +382 -131
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-jPBj20ez.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Hy74pMMo.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BXTAZYaJ.mjs +26 -0
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Br6eWzp_.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-DOdK6KJ5.mjs → hostInit-BAYLsfoZ.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +438 -61
- package/dist/pipeline-analytics/index.mjs +438 -61
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Be6bZvOS.mjs +0 -26
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B5Bw-VHt.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as MAX_TRACK_RELATIONS, $i as
|
|
1
|
+
import { $ as MAX_TRACK_RELATIONS, $i as zoneAnalyticsCapability, $n as buildViewerLink, $r as ncMediaPolicyError, $t as NcTemplatePreviewSchema, A as DETECTION_MACRO_CLASSES, Aa as record, Ai as ruleKindOf, An as TrackPlacementSchema, Ar as isAlarmControllableCap, At as NcAlarmConfigSchema, B as EXPORT_DENSE_MAX_RANGES, Bi as summarizeReview, Bn as alertEpisodeId, Br as isKnownTemplateVar, Bt as NcRuleSchema, C as COCO_TO_MACRO, Ca as boolean, Cn as SetTrackPurityResultSchema, Cr as ensureAlarmProfiles, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as number, Di as ruleBelongsToAlarm$1, Dn as TimelapseRuleInputSchema, Dr as failureContributionCapability, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as looseObject, Ei as ruleAutoTriggersAlarm, En as TRACK_RELATIONS_DEFAULT_LIMIT, Er as faceGalleryCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as union, Fi as startMoverUnderClearance, Fn as TrackSourceSchema, Fr as isDefaultAlarmProfileId, Ft as NcConditionDescriptorSchema, G as LabelAttributionSchema, Gi as systemEventFilterApplies, Gn as applyNonBlockingEdit, Gr as isScheduleActive, Gt as NcSnoozeInputSchema, H as FULL_IMAGE_BBOX, Hi as syncActiveAlarmProfileSettings, Hn as alertEpisodePayload, Hr as isMediaOwnerType, Ht as NcRuleUpdateInputSchema, I as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Ia as unknown, Ii as storageOccupancyCapability, In as activateAlarmProfile, Ir as isDetectionMacroClass, It as NcConditionsSchema, J as LockStateSchema, Ji as templateCarriesSummary, Jn as assertTimelapseCadences, Jr as isVectorIndexDimConflict, Jt as NcStateSummarySectionIdSchema, K as ListTrackRelationsInputSchema, Ki as systemEventSummaryScheduleOf, Kn as applySecureOnArmEdit, Kr as isSystemDelivery, Kt as NcSnoozeSchema, L as EVENT_DENSITY_BATCH_MAX, La as DeviceType, Li as subKindsOf, Ln as addonWidgetsSourceCapability, Lr as isDrawableSubjectPath, Lt as NcRuleActionSchema, M as DeclaredDevices, Ma as string, Mi as secureActuationFor, Mn as TrackRelationKindSchema, Mr as isAlertEpisodeSeverity, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as templateLiteral, Ni as sensorRaisedOf, Nn as TrackRelationSchema, Nr as isAppLinkBase, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as object, Oi as ruleDeviceCompatCapQueries, On as TimelapseRulePatchSchema, Or as gifDeriveSpeed, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as tuple, Pi as servedTemplateVars, Pn as TrackSideSchema, Pr as isAudioClassName, Pt as NcAlarmSettingsPatchSchema, Q as MAX_TRACK_PURITIES, Qi as withSystemEventClasses, Qn as buildSubjectPath, Qr as memoizePinnedFfmpeg, Qt as NcTemplatePreviewInputSchema, R as EVENT_KIND_BY_CAP, Ra as errMsg, Ri as subjectPathSampleFromBox, Rn as alarmDeviceSecureOnArm, Rr as isEventKindLabelKey, Rt as NcRuleInputSchema, S as BucketAccumulator, Sa as array, Si as resolveNcMediaImage, Sn as SetTrackPurityInputSchema, Sr as encodeVectorBase64, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as literal, Ti as rootSupplyServes, Tn as TIMELAPSE_DENSE_FLOOR_SEC, Tr as evictionPolicyOfLocation, Tt as NC_SNOOZE_MAX_MINUTES, U as FailureCounters, Ui as systemEventDeliveryProblems, Un as applyBlockingEdit, Ur as isRetiredClearKey, Ut as NcScheduleSchema, V as FIRST_LEVEL_MACRO_CLASSES, Vi as sunTimes, Vn as alertEpisodeKind, Vr as isLabelSupplyStepId, Vt as NcRuleTargetSchema, W as LABEL_SUPPLY_MACRO, Wi as systemEventFamilyProblems, Wn as applyModeDelaysEdits, Wr as isRootSuppliableStepId, Wt as NcSharedSequenceSchema, X as MAX_BIRTH_DECISION_RECORDS, Xi as vectorDimFromBase64, Xn as audioModeOf, Xr as linkedStepOf, Xt as NcTaxonomySchema, Y as MAX_ARCHIVED_DEBUG_NOTES, Yi as templateVarsFor, Yn as audioMetricsCapability, Yr as kebabToCamel, Yt as NcSystemEventKindSchema, Z as MAX_BIRTH_LATENCY_PROXY_MS, Zi as viewerLinkBaseOf, Zn as buildEventKindDescriptor, Zr as mayWriteToLocation, Zt as NcTemplateDefaultSchema, _ as AlarmArmModeSchema, _a as nodePin, _i as resolveClipSearchMinScore, _n as SCENE_DEFAULT_QUIET_SECONDS, _t as NC_CONFIRM_DEFAULT_TIMEOUT_MS, a as CLIP_EMBEDDING_STEP_ID, aa as CAP_ADVANCE_FIELDS, ai as paintHex, an as RECORDING_EXPORT_MAX_READ_BYTES, ar as consumableLowPercentOf, at as NC_ALARM_DELAY_FIELDS, b as BaseDevice, ba as ZodObject, bi as resolveModeDelays, bn as SCENE_RESET_RECAPTURES, bt as NC_DEFAULT_SNOOZE_MINUTES, c as readClusterModelPinStrict, ca as RECORD_MAX_DATA_BYTES, cn as RetrainDismissedSchema, cr as createAlarmProfile, ct as NC_ALARM_SYSTEM_EVENT_KINDS, d as APPEARANCE_BIN_COUNT, da as customAction, di as plateGalleryCapability, dn as RetrainVerdictSubjectSchema, dr as derivePostTriggeredAction, dt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, ea as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, ei as normalizeNcAction, en as NcTemplateVarDescriptorSchema, er as bundledCamstack, et as MOTION_CLOSE_AFTER_MS, f as APPEARANCE_BLOCKS, fa as defineAddonMigrations, fi as projectSubjectPath, fn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, fr as deriveRecordingMode, ft as NC_CLIP_POST_ROLL_DEFAULT_SEC, g as ActionTargetResolver, ga as isDeviceScopedCap, gi as renameAlarmProfile, gn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, gr as deviceRemovalHookCapability, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as hydrateSchema, hi as readTimelapseGeneratedAt, hn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, hr as deviceAttentionKind, ht as NC_CONDITION_CATALOG, ia as BaseAddon, ii as occupancyScope, ir as consumableFormOf, it as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, j as DETECTION_PIPELINE_CAP_NAME, ja as strictObject, ji as sceneMonitorCapability, jn as TrackPuritySchema, jr as isAlertEpisodeOpen, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as partialRecord, ki as ruleGroupingApplies, kn as TimelapseRuleSchema, kr as gifFromMp4Args, kt as NC_TEMPLATE_VARS, l as resolveClusterModelPin, la as RECORD_QUERY_MAX, li as pickFaceRescoreIntervalMinutes, ln as RetrainOriginSchema, lr as deleteAlarmProfile, lt as NC_ALARM_TRIGGERED_MAX_SEC, m as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ma as defineRecordKinds, mi as readMotionTriggeredBy, mn as SCENE_DEFAULT_ANCHOR_THRESHOLD, mr as describeSystemEventFamilyProblem, mt as NC_CLIP_RETENTION_MAX_SEC, na as evaluateSensorEdge, ni as normalizeNcMediaPolicy, nn as OpsLogEntrySchema, nr as classifyRetrainReview, nt as MotionTriggeredBySchema, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as CamProfileSchema, oi as paletteResultSchema, on as RECORDING_SOURCE_CAMSTACK_ADDON, or as coreBlockIdFromAddonId, ot as NC_ALARM_DELAY_MAX_SEC, p as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, pa as defineCustomActions, pi as readDeviceStateFrom, pn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, pr as describeSystemEventDeliveryProblem, pt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, q as ListTrackRelationsResultSchema, qi as systemEventSummarySplitOf, qn as armRemovalReconcile, qr as isSystemEventSummaryRule, qt as NcSnoozeSuppressedSchema, ra as isSourceCap, ri as notificationRulesCapability, rn as PackageLlmVerdictSchema, rr as cloneAlarmSettings, rt as NC_ALARM_DEFAULT_PROFILE_ID, s as FACE_EMBEDDING_STEP_ID, sa as InvalidRecordError, si as parseAlertEpisodePayload, sn as RebuildObjectEmbeddingsRefusalSchema, sr as cosineSimilarity$1, st as NC_ALARM_NOTIFY_FIELDS, t as BUILTIN_CLIP_MODELS, ta as canonicalHash, ti as normalizeNcCapTarget, tn as OPS_LOG_DEFAULT_LIMIT, tr as calendarEventLocalSpan, tt as MediaFileKindEnum, u as ACTION_TARGET_INVALIDATION_EVENTS, ua as createEvent, ui as pipelineAnalyticsCapability, un as RetrainStatusSchema, ur as deriveDetailCropRect, ut as NC_AUDIO_CONFIRM_HITS_DEFAULT, v as ArchivedDebugNoteSchema, vi as resolveDeliveryParams, vn as SCENE_DEFAULT_UNCOVERED_POLICY, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as discriminatedUnion, wi as retrainRevisionConflictMessage, wn as SetTrackRelationInputSchema, wr as eventKindLabel, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as _enum, xi as resolveModeNotifyPolicy, xn as SceneMonitorSchema, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as sleep$1, yi as resolveLocationMode, yn as SCENE_DIVERGED, yr as embeddingRefusalOf, yt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, z as EVENT_OWNER_TYPES, za as EventCategory, zi as subjectPathSvgPoints, zn as alarmPanelCapability, zr as isEventOwnerType, zt as NcRulePatchSchema } from "../clip-model-registry-DZTzG2p4.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -12276,6 +12276,16 @@ var NcStateSummaryTestSchema = object({
|
|
|
12276
12276
|
}))
|
|
12277
12277
|
});
|
|
12278
12278
|
//#endregion
|
|
12279
|
+
//#region src/notification-center/template-defaults.ts
|
|
12280
|
+
function servedTemplateDefaults(texts) {
|
|
12281
|
+
return [{
|
|
12282
|
+
id: "appliance-cycle",
|
|
12283
|
+
kind: "sensor",
|
|
12284
|
+
title: texts.raw("appliance.finished.title") ?? "",
|
|
12285
|
+
body: texts.raw("appliance.finished.body") ?? ""
|
|
12286
|
+
}];
|
|
12287
|
+
}
|
|
12288
|
+
//#endregion
|
|
12279
12289
|
//#region src/notification-center/clock-text.ts
|
|
12280
12290
|
/**
|
|
12281
12291
|
* `{{time}}` — hours and minutes in the NOTIFICATION language.
|
|
@@ -13659,7 +13669,10 @@ var ncActions = defineCustomActions({
|
|
|
13659
13669
|
catalog: array(NcConditionDescriptorSchema),
|
|
13660
13670
|
taxonomy: NcTaxonomySchema
|
|
13661
13671
|
})),
|
|
13662
|
-
"nc.getTemplateCatalog": customAction(object({}), object({
|
|
13672
|
+
"nc.getTemplateCatalog": customAction(object({}), object({
|
|
13673
|
+
vars: array(NcTemplateVarDescriptorSchema),
|
|
13674
|
+
defaults: array(NcTemplateDefaultSchema)
|
|
13675
|
+
})),
|
|
13663
13676
|
"nc.previewTemplate": customAction(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, { kind: "mutation" }),
|
|
13664
13677
|
"nc.createRule": customAction(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
|
|
13665
13678
|
kind: "mutation",
|
|
@@ -14439,7 +14452,10 @@ function makeNcActionHandlers(deps) {
|
|
|
14439
14452
|
catalog: [...NC_CONDITION_CATALOG],
|
|
14440
14453
|
taxonomy: NC_TAXONOMY
|
|
14441
14454
|
}),
|
|
14442
|
-
"nc.getTemplateCatalog": async () => ({
|
|
14455
|
+
"nc.getTemplateCatalog": async () => ({
|
|
14456
|
+
vars: servedTemplateVars(),
|
|
14457
|
+
defaults: deps.previewTexts === void 0 ? [] : servedTemplateDefaults(deps.previewTexts)
|
|
14458
|
+
}),
|
|
14443
14459
|
"nc.previewTemplate": async (input) => {
|
|
14444
14460
|
const texts = deps.previewTexts;
|
|
14445
14461
|
if (texts === void 0) throw new Error("nc.previewTemplate: text catalog not wired");
|
|
@@ -49856,7 +49872,7 @@ async function encodeRgbCropToJpeg(bytes, width, height) {
|
|
|
49856
49872
|
* what lets the caller stop base64-ing purely to be understood.
|
|
49857
49873
|
*/
|
|
49858
49874
|
async function decodeJpegToRgb(jpeg) {
|
|
49859
|
-
const { data, info } = await sharp(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).raw().toBuffer({ resolveWithObject: true });
|
|
49875
|
+
const { data, info } = await sharp(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).removeAlpha().toColourspace("srgb").raw().toBuffer({ resolveWithObject: true });
|
|
49860
49876
|
return {
|
|
49861
49877
|
bytes: data,
|
|
49862
49878
|
width: info.width,
|
|
@@ -52942,6 +52958,42 @@ function collectPackageZoneIds(rules) {
|
|
|
52942
52958
|
}
|
|
52943
52959
|
return ids;
|
|
52944
52960
|
}
|
|
52961
|
+
/**
|
|
52962
|
+
* How much the package zone box grows on every side when asking whether motion
|
|
52963
|
+
* reached it. Mirrors the D144 dilation of the pipeline's burst trigger
|
|
52964
|
+
* (`PACKAGE_ZONE_DILATION` in addon-pipeline, not importable from here): about
|
|
52965
|
+
* one adult's width beside a doormat, the place a courier stands.
|
|
52966
|
+
*/
|
|
52967
|
+
var PACKAGE_ZONE_MOTION_DILATION = .5;
|
|
52968
|
+
/**
|
|
52969
|
+
* A motion region covering more than this fraction of the frame is a light
|
|
52970
|
+
* change or the weather, not a person: it never counts as a deliverer.
|
|
52971
|
+
*/
|
|
52972
|
+
var PACKAGE_ZONE_MOTION_MAX_REGION_FRAC = .8;
|
|
52973
|
+
/**
|
|
52974
|
+
* Did analyzer motion reach the dilated package zone with a person-sized
|
|
52975
|
+
* region? Unknown geometry answers false: this stamp only ever BACKS the open
|
|
52976
|
+
* of a candidate, so failing closed keeps the light-wedge guard intact.
|
|
52977
|
+
*/
|
|
52978
|
+
function delivererMotionNearPackageZone(input) {
|
|
52979
|
+
const { zoneRect, frameWidth, frameHeight } = input;
|
|
52980
|
+
if (zoneRect === null || frameWidth <= 0 || frameHeight <= 0) return false;
|
|
52981
|
+
const dx = zoneRect.w * PACKAGE_ZONE_MOTION_DILATION;
|
|
52982
|
+
const dy = zoneRect.h * PACKAGE_ZONE_MOTION_DILATION;
|
|
52983
|
+
const left = Math.max(0, zoneRect.x - dx);
|
|
52984
|
+
const top = Math.max(0, zoneRect.y - dy);
|
|
52985
|
+
const right = Math.min(1, zoneRect.x + zoneRect.w + dx);
|
|
52986
|
+
const bottom = Math.min(1, zoneRect.y + zoneRect.h + dy);
|
|
52987
|
+
for (const region of input.regions) {
|
|
52988
|
+
const x = region.bbox.x / frameWidth;
|
|
52989
|
+
const y = region.bbox.y / frameHeight;
|
|
52990
|
+
const w = region.bbox.w / frameWidth;
|
|
52991
|
+
const h = region.bbox.h / frameHeight;
|
|
52992
|
+
if (w * h > PACKAGE_ZONE_MOTION_MAX_REGION_FRAC) continue;
|
|
52993
|
+
if (x < right && left < x + w && y < bottom && top < y + h) return true;
|
|
52994
|
+
}
|
|
52995
|
+
return false;
|
|
52996
|
+
}
|
|
52945
52997
|
//#endregion
|
|
52946
52998
|
//#region src/pipeline-analytics/pipeline/package/package-collector-mirror.ts
|
|
52947
52999
|
/**
|
|
@@ -53887,6 +53939,15 @@ var PACKAGE_STILLNESS_TTL_MS = 5 * 6e4;
|
|
|
53887
53939
|
*/
|
|
53888
53940
|
var PACKAGE_STILLNESS_ASSOC_IOU = .3;
|
|
53889
53941
|
/**
|
|
53942
|
+
* Lowest score of a below-threshold box that may CONTINUE an open candidate
|
|
53943
|
+
* (D665: a box under the floor may continue, never open). Observed on a real
|
|
53944
|
+
* parcel after the day to IR flip: 0.35 to 0.50.
|
|
53945
|
+
*/
|
|
53946
|
+
var PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE = .3;
|
|
53947
|
+
/** Above-floor sightings a candidate needs before a sub-floor box may count:
|
|
53948
|
+
* one confident look plus a doormat-shaped reading is not a parcel. */
|
|
53949
|
+
var PACKAGE_CONTINUATION_MIN_REAL_HITS = 2;
|
|
53950
|
+
/**
|
|
53890
53951
|
* Two absence reads closer than this are ONE look, not two. A settle burst
|
|
53891
53952
|
* runs the package step 5 s apart and a poll 45 s apart, so a real second
|
|
53892
53953
|
* look is always further away; two frames of one moment (a re-dispatch, a
|
|
@@ -53936,6 +53997,7 @@ var PackageStillnessTracker = class {
|
|
|
53936
53997
|
const next = [...this.byDevice.get(input.deviceId) ?? []];
|
|
53937
53998
|
const consumed = /* @__PURE__ */ new Set();
|
|
53938
53999
|
const promoted = [];
|
|
54000
|
+
const continuedLog = [];
|
|
53939
54001
|
for (const s of input.sightings) {
|
|
53940
54002
|
const index = this.bestMatch(next, s, consumed);
|
|
53941
54003
|
if (index < 0) {
|
|
@@ -53948,17 +54010,33 @@ var PackageStillnessTracker = class {
|
|
|
53948
54010
|
if (prev === void 0) continue;
|
|
53949
54011
|
consumed.add(prev.id);
|
|
53950
54012
|
const updated = confirm(prev, input, s);
|
|
53951
|
-
const
|
|
53952
|
-
|
|
53953
|
-
|
|
53954
|
-
|
|
53955
|
-
|
|
53956
|
-
const
|
|
53957
|
-
|
|
53958
|
-
|
|
54013
|
+
const parked = this.promoteWhenReady(updated, input);
|
|
54014
|
+
next[index] = parked ?? updated;
|
|
54015
|
+
if (parked !== null) promoted.push(toEntry(parked));
|
|
54016
|
+
}
|
|
54017
|
+
if (input.looked === true && input.continuationBoxes !== void 0) for (let i = 0; i < next.length; i += 1) {
|
|
54018
|
+
const c = next[i];
|
|
54019
|
+
if (c === void 0 || c.promotedAt !== null || consumed.has(c.id)) continue;
|
|
54020
|
+
if (c.realHits < PACKAGE_CONTINUATION_MIN_REAL_HITS) continue;
|
|
54021
|
+
const box = this.continuationFor(c, input.continuationBoxes);
|
|
54022
|
+
if (box === null) continue;
|
|
54023
|
+
consumed.add(c.id);
|
|
54024
|
+
const continued = {
|
|
54025
|
+
...c,
|
|
54026
|
+
lastSeenAt: input.timestamp,
|
|
54027
|
+
hits: c.hits + 1,
|
|
54028
|
+
absenceReads: [],
|
|
54029
|
+
lastLook: "present",
|
|
54030
|
+
earlyUnbackedReported: false
|
|
53959
54031
|
};
|
|
53960
|
-
|
|
53961
|
-
|
|
54032
|
+
continuedLog.push({
|
|
54033
|
+
id: c.id,
|
|
54034
|
+
hits: continued.hits,
|
|
54035
|
+
score: box.score
|
|
54036
|
+
});
|
|
54037
|
+
const parked = this.promoteWhenReady(continued, input);
|
|
54038
|
+
next[i] = parked ?? continued;
|
|
54039
|
+
if (parked !== null) promoted.push(toEntry(parked));
|
|
53962
54040
|
}
|
|
53963
54041
|
const present = input.rawPackageBoxes !== void 0 ? withRawPresence(next, consumed, input.rawPackageBoxes, input.timestamp, this.config.assocIou) : next;
|
|
53964
54042
|
const looked = input.looked === true && input.rawPackageBoxes !== void 0 ? withLook(present, consumed, input, input.rawPackageBoxes, this.config.assocIou) : present;
|
|
@@ -53994,7 +54072,8 @@ var PackageStillnessTracker = class {
|
|
|
53994
54072
|
...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
|
|
53995
54073
|
}),
|
|
53996
54074
|
earlyDepartureIds: [...early],
|
|
53997
|
-
earlyEvidenceUnbacked: unbacked
|
|
54075
|
+
earlyEvidenceUnbacked: unbacked,
|
|
54076
|
+
continued: continuedLog
|
|
53998
54077
|
};
|
|
53999
54078
|
}
|
|
54000
54079
|
/** Why complete early evidence may NOT conclude, or null when it may. */
|
|
@@ -54021,7 +54100,8 @@ var PackageStillnessTracker = class {
|
|
|
54021
54100
|
...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
|
|
54022
54101
|
}),
|
|
54023
54102
|
earlyDepartureIds: [],
|
|
54024
|
-
earlyEvidenceUnbacked: []
|
|
54103
|
+
earlyEvidenceUnbacked: [],
|
|
54104
|
+
continued: []
|
|
54025
54105
|
};
|
|
54026
54106
|
}
|
|
54027
54107
|
/** The devices holding a candidate — what a sweep has to visit. */
|
|
@@ -54085,6 +54165,43 @@ var PackageStillnessTracker = class {
|
|
|
54085
54165
|
/** Best same-class candidate over the association floor, ignoring any already
|
|
54086
54166
|
* consumed by an earlier sighting in THIS frame (two boxes never collapse
|
|
54087
54167
|
* into one parcel). */
|
|
54168
|
+
continuationFor(c, boxes) {
|
|
54169
|
+
let best = null;
|
|
54170
|
+
let bestIou = this.config.assocIou;
|
|
54171
|
+
for (const b of boxes) {
|
|
54172
|
+
if (b.score < PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE) continue;
|
|
54173
|
+
const overlap = iou$2(c.bbox, b.bbox);
|
|
54174
|
+
if (overlap >= bestIou) {
|
|
54175
|
+
bestIou = overlap;
|
|
54176
|
+
best = b;
|
|
54177
|
+
}
|
|
54178
|
+
}
|
|
54179
|
+
return best;
|
|
54180
|
+
}
|
|
54181
|
+
promoteWhenReady(candidate, input) {
|
|
54182
|
+
const updated = this.backedLate(candidate, input);
|
|
54183
|
+
const minHits = input.minHits ?? this.config.minHits;
|
|
54184
|
+
return updated.promotedAt === null && updated.activityBackedAtOpen && updated.hits >= minHits && input.timestamp - updated.firstSeenAt >= input.minDwellMs ? {
|
|
54185
|
+
...updated,
|
|
54186
|
+
promotedAt: input.timestamp
|
|
54187
|
+
} : null;
|
|
54188
|
+
}
|
|
54189
|
+
/**
|
|
54190
|
+
* Zone motion learned AFTER the open still backs the candidate when its stamp
|
|
54191
|
+
* lies in the window around the FIRST sighting (the same slack a pick-up
|
|
54192
|
+
* gets): the backing stays anchored at the open, a later passer-by never
|
|
54193
|
+
* validates scenery (D1003).
|
|
54194
|
+
*/
|
|
54195
|
+
backedLate(c, input) {
|
|
54196
|
+
if (c.activityBackedAtOpen || c.promotedAt !== null) return c;
|
|
54197
|
+
const at = input.lastDelivererMotionAt;
|
|
54198
|
+
const windowMs = input.activityWindowMs ?? 0;
|
|
54199
|
+
if (at === void 0 || windowMs <= 0) return c;
|
|
54200
|
+
return at >= c.firstSeenAt - windowMs && at <= c.firstSeenAt + this.config.roundGapMs ? {
|
|
54201
|
+
...c,
|
|
54202
|
+
activityBackedAtOpen: true
|
|
54203
|
+
} : c;
|
|
54204
|
+
}
|
|
54088
54205
|
bestMatch(candidates, s, consumed) {
|
|
54089
54206
|
let bestIndex = -1;
|
|
54090
54207
|
let bestIou = this.config.assocIou;
|
|
@@ -54101,7 +54218,7 @@ var PackageStillnessTracker = class {
|
|
|
54101
54218
|
}
|
|
54102
54219
|
openCandidate(input, s) {
|
|
54103
54220
|
const windowMs = input.activityWindowMs ?? 0;
|
|
54104
|
-
const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs;
|
|
54221
|
+
const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs || input.lastDelivererMotionAt !== void 0 && input.timestamp - input.lastDelivererMotionAt <= windowMs;
|
|
54105
54222
|
return {
|
|
54106
54223
|
id: this.newId(),
|
|
54107
54224
|
deviceId: input.deviceId,
|
|
@@ -54112,6 +54229,7 @@ var PackageStillnessTracker = class {
|
|
|
54112
54229
|
firstSeenAt: input.timestamp,
|
|
54113
54230
|
lastSeenAt: input.timestamp,
|
|
54114
54231
|
hits: 1,
|
|
54232
|
+
realHits: 1,
|
|
54115
54233
|
promotedAt: null,
|
|
54116
54234
|
absenceReads: [],
|
|
54117
54235
|
lastLook: null,
|
|
@@ -54134,6 +54252,7 @@ function confirm(prev, input, s) {
|
|
|
54134
54252
|
frameHeight: input.frameHeight,
|
|
54135
54253
|
lastSeenAt: input.timestamp,
|
|
54136
54254
|
hits: prev.hits + 1,
|
|
54255
|
+
realHits: prev.realHits + 1,
|
|
54137
54256
|
absenceReads: [],
|
|
54138
54257
|
lastLook: "present",
|
|
54139
54258
|
earlyUnbackedReported: false,
|
|
@@ -54670,6 +54789,9 @@ var FrameState = class {
|
|
|
54670
54789
|
* which would let the streetlight wedge vouch for its own delivery.
|
|
54671
54790
|
*/
|
|
54672
54791
|
lastNonPackageActivityAt = /* @__PURE__ */ new Map();
|
|
54792
|
+
/** Last analyzer motion near a package drop-off zone, per device (D1003).
|
|
54793
|
+
* Backs the OPEN of a candidate only; never a pick-up witness. */
|
|
54794
|
+
lastPackageZoneMotionAt = /* @__PURE__ */ new Map();
|
|
54673
54795
|
/**
|
|
54674
54796
|
* WHO was in the drop-off zone, and when (D404). The pick-up is CONCLUDED
|
|
54675
54797
|
* minutes after the fact, so the collector must be remembered while it is
|
|
@@ -58029,6 +58151,7 @@ function nextPackageLookDropEpisodes(previous, drops) {
|
|
|
58029
58151
|
//#region src/pipeline-analytics/pipeline/package/package-look-evidence.ts
|
|
58030
58152
|
function packageLookEvidence(input) {
|
|
58031
58153
|
const raw = [];
|
|
58154
|
+
const continuation = [];
|
|
58032
58155
|
for (const d of input.frame.detections) {
|
|
58033
58156
|
if (d.kind !== "first-level" || !input.packageClasses.has(d.macroClass)) continue;
|
|
58034
58157
|
raw.push({
|
|
@@ -58041,11 +58164,16 @@ function packageLookEvidence(input) {
|
|
|
58041
58164
|
for (const d of input.frame.discarded ?? []) {
|
|
58042
58165
|
if (!input.packageClasses.has(d.macroClass)) continue;
|
|
58043
58166
|
const [x1, y1, x2, y2] = d.bbox;
|
|
58044
|
-
|
|
58167
|
+
const box = {
|
|
58045
58168
|
x: x1,
|
|
58046
58169
|
y: y1,
|
|
58047
58170
|
w: x2 - x1,
|
|
58048
58171
|
h: y2 - y1
|
|
58172
|
+
};
|
|
58173
|
+
raw.push(box);
|
|
58174
|
+
if (d.reason === "below-threshold") continuation.push({
|
|
58175
|
+
bbox: box,
|
|
58176
|
+
score: d.score
|
|
58049
58177
|
});
|
|
58050
58178
|
}
|
|
58051
58179
|
return {
|
|
@@ -58057,6 +58185,7 @@ function packageLookEvidence(input) {
|
|
|
58057
58185
|
h: t.bbox.h
|
|
58058
58186
|
})),
|
|
58059
58187
|
rawPackageBoxes: raw,
|
|
58188
|
+
continuationBoxes: continuation,
|
|
58060
58189
|
earlyPickup: input.earlyPickupEnabled
|
|
58061
58190
|
};
|
|
58062
58191
|
}
|
|
@@ -58244,6 +58373,13 @@ function clamp01(value) {
|
|
|
58244
58373
|
}
|
|
58245
58374
|
//#endregion
|
|
58246
58375
|
//#region src/pipeline-analytics/pipeline/package/package-scene-diff.ts
|
|
58376
|
+
/**
|
|
58377
|
+
* Zone-crop scene difference for a packages rule (D931).
|
|
58378
|
+
*
|
|
58379
|
+
* A uniform brightness shift is removed by subtracting the median luma delta,
|
|
58380
|
+
* so a shadow that lifts the whole crop leaves no blob. What remains is a
|
|
58381
|
+
* connected region. The package model is not consulted here.
|
|
58382
|
+
*/
|
|
58247
58383
|
/** Residual, after the median shift, that counts as a changed pixel. */
|
|
58248
58384
|
var PACKAGE_SCENE_DIFF_LUMA_THRESHOLD = 28;
|
|
58249
58385
|
/** A changed region this large, relative to the zone, is the zone itself. */
|
|
@@ -58258,12 +58394,26 @@ var PACKAGE_SCENE_DIFF_SETTLE_MS = 3e3;
|
|
|
58258
58394
|
var PACKAGE_SCENE_DIFF_LOOK_GAP_MS = 2e3;
|
|
58259
58395
|
/** Confidence stamped on a scene-diff sighting the model did not box. */
|
|
58260
58396
|
var PACKAGE_SCENE_DIFF_CONFIDENCE = .5;
|
|
58397
|
+
/** True when the crop was shot in infrared (no colour). */
|
|
58398
|
+
function isIrCrop(crop) {
|
|
58399
|
+
return crop.chromaSpread < IR_CHROMA_THRESHOLD;
|
|
58400
|
+
}
|
|
58261
58401
|
function diffZoneLuma(input) {
|
|
58262
58402
|
const { before, after } = input;
|
|
58263
58403
|
if (before.width !== after.width || before.height !== after.height || before.luma.length !== after.luma.length) return {
|
|
58264
58404
|
blobs: [],
|
|
58265
58405
|
rejections: [{ reason: "crop-mismatch" }]
|
|
58266
58406
|
};
|
|
58407
|
+
if (isIrCrop(before) !== isIrCrop(after)) return {
|
|
58408
|
+
blobs: [],
|
|
58409
|
+
rejections: [{
|
|
58410
|
+
reason: "lighting-mode-changed",
|
|
58411
|
+
chroma: {
|
|
58412
|
+
before: before.chromaSpread,
|
|
58413
|
+
after: after.chromaSpread
|
|
58414
|
+
}
|
|
58415
|
+
}]
|
|
58416
|
+
};
|
|
58267
58417
|
const median = medianDelta(before.luma, after.luma);
|
|
58268
58418
|
const mask = residualMask(before.luma, after.luma, median);
|
|
58269
58419
|
const zonePx = before.width * before.height;
|
|
@@ -58478,6 +58628,7 @@ function okCrop(rgb, width, height, jpeg) {
|
|
|
58478
58628
|
width,
|
|
58479
58629
|
height,
|
|
58480
58630
|
luma: lumaFromRgb(rgb, width, height),
|
|
58631
|
+
chromaSpread: chromaSpreadFromRgb(rgb, width, height),
|
|
58481
58632
|
jpeg: new Uint8Array(jpeg)
|
|
58482
58633
|
};
|
|
58483
58634
|
}
|
|
@@ -58494,6 +58645,46 @@ function lumaFromRgb(bytes, width, height) {
|
|
|
58494
58645
|
}
|
|
58495
58646
|
return luma;
|
|
58496
58647
|
}
|
|
58648
|
+
/** The grid `IR_CHROMA_THRESHOLD` was calibrated on (`chromaSpread`, scene-crop.ts). */
|
|
58649
|
+
var CHROMA_GRID = 32;
|
|
58650
|
+
/**
|
|
58651
|
+
* Mean of max(r,g,b) - min(r,g,b), 0..255, measured on a 32x32 box-averaged
|
|
58652
|
+
* copy of the crop: the same measure and grid as `chromaSpread` in
|
|
58653
|
+
* `scene-crop.ts` (which needs sharp), so `IR_CHROMA_THRESHOLD` means the same
|
|
58654
|
+
* thing. Averaging first matters: sensor noise and JPEG chroma speckle on an IR
|
|
58655
|
+
* frame cancel out, where per-pixel they read as colour.
|
|
58656
|
+
*/
|
|
58657
|
+
function chromaSpreadFromRgb(bytes, width, height) {
|
|
58658
|
+
if (width <= 0 || height <= 0 || bytes.length < width * height * 3) return 0;
|
|
58659
|
+
const gw = Math.min(CHROMA_GRID, width);
|
|
58660
|
+
const gh = Math.min(CHROMA_GRID, height);
|
|
58661
|
+
const sum = new Float64Array(gw * gh * 3);
|
|
58662
|
+
const count = new Float64Array(gw * gh);
|
|
58663
|
+
for (let y = 0; y < height; y++) {
|
|
58664
|
+
const cy = Math.min(gh - 1, Math.floor(y * gh / height));
|
|
58665
|
+
for (let x = 0; x < width; x++) {
|
|
58666
|
+
const cx = Math.min(gw - 1, Math.floor(x * gw / width));
|
|
58667
|
+
const cell = cy * gw + cx;
|
|
58668
|
+
const o = (y * width + x) * 3;
|
|
58669
|
+
sum[cell * 3] = (sum[cell * 3] ?? 0) + (bytes[o] ?? 0);
|
|
58670
|
+
sum[cell * 3 + 1] = (sum[cell * 3 + 1] ?? 0) + (bytes[o + 1] ?? 0);
|
|
58671
|
+
sum[cell * 3 + 2] = (sum[cell * 3 + 2] ?? 0) + (bytes[o + 2] ?? 0);
|
|
58672
|
+
count[cell] = (count[cell] ?? 0) + 1;
|
|
58673
|
+
}
|
|
58674
|
+
}
|
|
58675
|
+
let total = 0;
|
|
58676
|
+
let cells = 0;
|
|
58677
|
+
for (let cell = 0; cell < gw * gh; cell++) {
|
|
58678
|
+
const n = count[cell] ?? 0;
|
|
58679
|
+
if (n === 0) continue;
|
|
58680
|
+
const r = (sum[cell * 3] ?? 0) / n;
|
|
58681
|
+
const g = (sum[cell * 3 + 1] ?? 0) / n;
|
|
58682
|
+
const b = (sum[cell * 3 + 2] ?? 0) / n;
|
|
58683
|
+
total += Math.max(r, g, b) - Math.min(r, g, b);
|
|
58684
|
+
cells += 1;
|
|
58685
|
+
}
|
|
58686
|
+
return cells === 0 ? 0 : total / cells;
|
|
58687
|
+
}
|
|
58497
58688
|
//#endregion
|
|
58498
58689
|
//#region src/pipeline-analytics/pipeline/package/package-scene-step.ts
|
|
58499
58690
|
/**
|
|
@@ -58593,6 +58784,14 @@ function compareAfterLeave(prev, input) {
|
|
|
58593
58784
|
minAreaFrac: input.minAreaFrac,
|
|
58594
58785
|
occluders: input.occluders
|
|
58595
58786
|
});
|
|
58787
|
+
if (diff.rejections.some((r) => r.reason === "lighting-mode-changed")) return {
|
|
58788
|
+
state: {
|
|
58789
|
+
...rememberClear(prev, input).state,
|
|
58790
|
+
lastDiffAt: input.timestamp
|
|
58791
|
+
},
|
|
58792
|
+
sightings: [],
|
|
58793
|
+
rejections: diff.rejections
|
|
58794
|
+
};
|
|
58596
58795
|
const blocked = diff.rejections.some((r) => r.reason === "overlaps-person" || r.reason === "overlaps-vehicle");
|
|
58597
58796
|
if (diff.blobs.length === 0) {
|
|
58598
58797
|
if (blocked) return {
|
|
@@ -58636,6 +58835,20 @@ function confirmPending(prev, input) {
|
|
|
58636
58835
|
sightings: [],
|
|
58637
58836
|
rejections: []
|
|
58638
58837
|
};
|
|
58838
|
+
if (isIrCrop(prev.after) !== isIrCrop(input.crop)) return {
|
|
58839
|
+
state: {
|
|
58840
|
+
...rememberClear(prev, input).state,
|
|
58841
|
+
lastDiffAt: input.timestamp
|
|
58842
|
+
},
|
|
58843
|
+
sightings: [],
|
|
58844
|
+
rejections: [{
|
|
58845
|
+
reason: "lighting-mode-changed",
|
|
58846
|
+
chroma: {
|
|
58847
|
+
before: prev.after.chromaSpread,
|
|
58848
|
+
after: input.crop.chromaSpread
|
|
58849
|
+
}
|
|
58850
|
+
}]
|
|
58851
|
+
};
|
|
58639
58852
|
const sightings = [];
|
|
58640
58853
|
const rejections = [];
|
|
58641
58854
|
const still = [];
|
|
@@ -58682,7 +58895,7 @@ function judgeBlob(prev, input, blob) {
|
|
|
58682
58895
|
bbox: blob.bbox
|
|
58683
58896
|
} };
|
|
58684
58897
|
const stable = meanAbsInBox(after, crop, blob.bbox) <= PACKAGE_SCENE_DIFF_STABLE_MEAN;
|
|
58685
|
-
const present = meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
|
|
58898
|
+
const present = isIrCrop(before) !== isIrCrop(crop) || meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
|
|
58686
58899
|
if (!stable || !present) return { rejection: {
|
|
58687
58900
|
reason: "unstable",
|
|
58688
58901
|
bbox: blob.bbox,
|
|
@@ -58850,7 +59063,8 @@ var PackageSceneCoordinator = class {
|
|
|
58850
59063
|
const crop = {
|
|
58851
59064
|
width: fetched.width,
|
|
58852
59065
|
height: fetched.height,
|
|
58853
|
-
luma: fetched.luma
|
|
59066
|
+
luma: fetched.luma,
|
|
59067
|
+
chromaSpread: fetched.chromaSpread
|
|
58854
59068
|
};
|
|
58855
59069
|
const stepped = stepPackageScene({
|
|
58856
59070
|
deviceId,
|
|
@@ -58916,6 +59130,10 @@ var PackageSceneCoordinator = class {
|
|
|
58916
59130
|
tags: { deviceId },
|
|
58917
59131
|
meta: {
|
|
58918
59132
|
reason: rejection.reason,
|
|
59133
|
+
...rejection.chroma !== void 0 ? {
|
|
59134
|
+
chromaBefore: Number(rejection.chroma.before.toFixed(2)),
|
|
59135
|
+
chromaAfter: Number(rejection.chroma.after.toFixed(2))
|
|
59136
|
+
} : {},
|
|
58919
59137
|
...rejection.areaFrac !== void 0 ? { areaFrac: Number(rejection.areaFrac.toFixed(5)) } : {}
|
|
58920
59138
|
}
|
|
58921
59139
|
});
|
|
@@ -59024,14 +59242,8 @@ function planPackageSceneFrame(deviceId, input) {
|
|
|
59024
59242
|
frameWidth: input.frameWidth,
|
|
59025
59243
|
frameHeight: input.frameHeight
|
|
59026
59244
|
});
|
|
59027
|
-
const sceneRect = sceneZones.size === 0 ? null : packageZoneCropRect({
|
|
59028
|
-
zones: input.zones,
|
|
59029
|
-
packageZoneIds: sceneZones,
|
|
59030
|
-
frameWidth: input.frameWidth,
|
|
59031
|
-
frameHeight: input.frameHeight
|
|
59032
|
-
});
|
|
59033
59245
|
return {
|
|
59034
|
-
sightings:
|
|
59246
|
+
sightings: input.sightings,
|
|
59035
59247
|
note: {
|
|
59036
59248
|
deviceId,
|
|
59037
59249
|
timestamp: input.timestamp,
|
|
@@ -59074,15 +59286,6 @@ function occludersOf(tracked) {
|
|
|
59074
59286
|
}
|
|
59075
59287
|
return out;
|
|
59076
59288
|
}
|
|
59077
|
-
function withoutSceneCenters(input, rect) {
|
|
59078
|
-
if (input.frameWidth <= 0 || input.frameHeight <= 0) return input.sightings;
|
|
59079
|
-
return input.sightings.filter((sighting) => !centerInside(sighting.bbox, rect, input));
|
|
59080
|
-
}
|
|
59081
|
-
function centerInside(bbox, rect, input) {
|
|
59082
|
-
const cx = (bbox.x + bbox.w / 2) / input.frameWidth;
|
|
59083
|
-
const cy = (bbox.y + bbox.h / 2) / input.frameHeight;
|
|
59084
|
-
return cx >= rect.x && cx < rect.x + rect.w && cy >= rect.y && cy < rect.y + rect.h;
|
|
59085
|
-
}
|
|
59086
59289
|
//#endregion
|
|
59087
59290
|
//#region src/pipeline-analytics/pipeline/package/package-llm-confirm.ts
|
|
59088
59291
|
/**
|
|
@@ -59243,56 +59446,143 @@ function sweepOne(deps, deviceId) {
|
|
|
59243
59446
|
}
|
|
59244
59447
|
//#endregion
|
|
59245
59448
|
//#region src/pipeline-analytics/pipeline/package/package-still-capture.ts
|
|
59246
|
-
/**
|
|
59247
|
-
|
|
59449
|
+
/**
|
|
59450
|
+
* The runner's native lease on a look frame (holdFrames 8) is released about
|
|
59451
|
+
* 100 ms after inference, so a handle older than this answers `native-miss` or
|
|
59452
|
+
* `preview-only` — promotion runs seconds to minutes after the look (D60).
|
|
59453
|
+
*/
|
|
59454
|
+
var PACKAGE_NATIVE_HANDLE_MAX_AGE_MS = 1e3;
|
|
59455
|
+
/** Total time a promotion may spend landing its still before it ships without (D60). */
|
|
59456
|
+
var PACKAGE_STILL_BOUND_MS = 6e3;
|
|
59457
|
+
/** One live-source attempt never takes longer than this (a sleeping camera). */
|
|
59458
|
+
var PACKAGE_STILL_ATTEMPT_TIMEOUT_MS = 3e3;
|
|
59459
|
+
/** Pause between two live attempts. */
|
|
59460
|
+
var PACKAGE_STILL_RETRY_PAUSE_MS = 750;
|
|
59461
|
+
var PACKAGE_STILL_LIVE_ATTEMPTS = 3;
|
|
59462
|
+
var realPause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
59248
59463
|
async function jpegOf(reply) {
|
|
59249
59464
|
if (reply.tier === "ram-fullframe") return "preview-only";
|
|
59250
59465
|
if (reply.jpegBytes !== void 0 && reply.jpegBytes.byteLength > 0) return reply.jpegBytes;
|
|
59251
59466
|
if (reply.bytes !== void 0 && reply.bytes.length === reply.width * reply.height * 3) return encodeRgbCropToJpeg(reply.bytes, reply.width, reply.height);
|
|
59252
59467
|
return "empty-frame";
|
|
59253
59468
|
}
|
|
59469
|
+
/** One live read, cut off after `limitMs` so a hung camera cannot hold the delivery. */
|
|
59470
|
+
async function readLive(source, limitMs) {
|
|
59471
|
+
let timer;
|
|
59472
|
+
const timeout = new Promise((resolve) => {
|
|
59473
|
+
timer = setTimeout(() => resolve(null), limitMs);
|
|
59474
|
+
});
|
|
59475
|
+
try {
|
|
59476
|
+
return await Promise.race([source(), timeout]);
|
|
59477
|
+
} finally {
|
|
59478
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
59479
|
+
}
|
|
59480
|
+
}
|
|
59481
|
+
/** Retry the live source under {@link PACKAGE_STILL_BOUND_MS} until a frame lands (D60). */
|
|
59482
|
+
async function liveStill(input, source) {
|
|
59483
|
+
const now = input.now ?? Date.now;
|
|
59484
|
+
const pause = input.pause ?? realPause;
|
|
59485
|
+
const deadline = now() + PACKAGE_STILL_BOUND_MS;
|
|
59486
|
+
let reason = "live-unavailable";
|
|
59487
|
+
let attempts = 0;
|
|
59488
|
+
while (attempts < PACKAGE_STILL_LIVE_ATTEMPTS) {
|
|
59489
|
+
const remaining = deadline - now();
|
|
59490
|
+
if (remaining <= 0) return {
|
|
59491
|
+
jpeg: null,
|
|
59492
|
+
reason: "bound-exhausted",
|
|
59493
|
+
attempts
|
|
59494
|
+
};
|
|
59495
|
+
attempts += 1;
|
|
59496
|
+
try {
|
|
59497
|
+
const jpeg = await readLive(source, Math.min(PACKAGE_STILL_ATTEMPT_TIMEOUT_MS, remaining));
|
|
59498
|
+
if (jpeg !== null && jpeg.byteLength > 0) return {
|
|
59499
|
+
jpeg,
|
|
59500
|
+
reason,
|
|
59501
|
+
attempts
|
|
59502
|
+
};
|
|
59503
|
+
reason = jpeg === null ? "live-unavailable" : "empty-frame";
|
|
59504
|
+
} catch {
|
|
59505
|
+
reason = "live-unavailable";
|
|
59506
|
+
}
|
|
59507
|
+
if (attempts < PACKAGE_STILL_LIVE_ATTEMPTS) await pause(PACKAGE_STILL_RETRY_PAUSE_MS);
|
|
59508
|
+
}
|
|
59509
|
+
return {
|
|
59510
|
+
jpeg: null,
|
|
59511
|
+
reason,
|
|
59512
|
+
attempts
|
|
59513
|
+
};
|
|
59514
|
+
}
|
|
59254
59515
|
/**
|
|
59255
59516
|
* The entry carrying its still's `keyFrameMediaId`, or the entry unchanged when
|
|
59256
59517
|
* it already had one or none could be stored (said, with the reason).
|
|
59518
|
+
*
|
|
59519
|
+
* Ladder: the native handle while its lease can still be alive, then a frame
|
|
59520
|
+
* that exists now (retried under a bound). Every landed still is logged next to
|
|
59521
|
+
* every miss (D400).
|
|
59257
59522
|
*/
|
|
59258
59523
|
async function withParcelStill(input) {
|
|
59259
59524
|
const { entry, logger } = input;
|
|
59260
59525
|
if (entry.keyFrameMediaId !== void 0) return entry;
|
|
59261
|
-
const
|
|
59526
|
+
const tags = { deviceId: entry.deviceId };
|
|
59527
|
+
const miss = (reason, extra = {}) => {
|
|
59262
59528
|
logger.warn("package still NOT stored — the delivery ships without a picture of the parcel", {
|
|
59263
|
-
tags
|
|
59529
|
+
tags,
|
|
59264
59530
|
meta: {
|
|
59265
59531
|
entryId: entry.id,
|
|
59266
59532
|
reason,
|
|
59267
|
-
...
|
|
59533
|
+
...extra
|
|
59268
59534
|
}
|
|
59269
59535
|
});
|
|
59270
59536
|
return entry;
|
|
59271
59537
|
};
|
|
59272
|
-
if (input.handle === null) return miss("no-frame-handle");
|
|
59273
59538
|
if (input.writer === null) return miss("no-media-store");
|
|
59539
|
+
const now = input.now ?? Date.now;
|
|
59540
|
+
let jpeg = null;
|
|
59541
|
+
let source = "native";
|
|
59542
|
+
let stamp = input.timestamp;
|
|
59543
|
+
let nativeMiss = "no-frame-handle";
|
|
59544
|
+
let liveReason = null;
|
|
59545
|
+
let attempts = 0;
|
|
59274
59546
|
try {
|
|
59275
|
-
|
|
59276
|
-
|
|
59277
|
-
|
|
59278
|
-
|
|
59279
|
-
|
|
59280
|
-
|
|
59281
|
-
|
|
59282
|
-
|
|
59283
|
-
|
|
59284
|
-
|
|
59285
|
-
|
|
59547
|
+
if (input.handle !== null) {
|
|
59548
|
+
const reply = await input.query({
|
|
59549
|
+
handle: input.handle,
|
|
59550
|
+
bbox: {
|
|
59551
|
+
x: 0,
|
|
59552
|
+
y: 0,
|
|
59553
|
+
w: entry.frameWidth,
|
|
59554
|
+
h: entry.frameHeight
|
|
59555
|
+
},
|
|
59556
|
+
acceptJpegBytes: true,
|
|
59557
|
+
encodeJpeg: true,
|
|
59558
|
+
pin: nodePin(input.handle.nodeId)
|
|
59559
|
+
});
|
|
59560
|
+
if (reply === null) nativeMiss = "native-miss";
|
|
59561
|
+
else {
|
|
59562
|
+
const out = await jpegOf(reply);
|
|
59563
|
+
if (typeof out === "string") nativeMiss = out;
|
|
59564
|
+
else jpeg = out;
|
|
59565
|
+
}
|
|
59566
|
+
}
|
|
59567
|
+
if (jpeg === null && input.live !== null) {
|
|
59568
|
+
const live = await liveStill(input, input.live);
|
|
59569
|
+
attempts = live.attempts;
|
|
59570
|
+
if (live.jpeg !== null) {
|
|
59571
|
+
jpeg = live.jpeg;
|
|
59572
|
+
source = "live";
|
|
59573
|
+
stamp = now();
|
|
59574
|
+
} else liveReason = live.reason;
|
|
59575
|
+
}
|
|
59576
|
+
if (jpeg === null) return miss(liveReason ?? nativeMiss, {
|
|
59577
|
+
...input.handle !== null ? { nativeMiss } : {},
|
|
59578
|
+
...liveReason !== null ? { liveAttempts: attempts } : {}
|
|
59286
59579
|
});
|
|
59287
|
-
if (reply === null) return miss("native-miss");
|
|
59288
|
-
const jpeg = await jpegOf(reply);
|
|
59289
|
-
if (typeof jpeg === "string") return miss(jpeg);
|
|
59290
59580
|
const keyFrameMediaId = await input.writer.putReplacing({
|
|
59291
59581
|
deviceId: entry.deviceId,
|
|
59292
59582
|
ownerKind: "track",
|
|
59293
59583
|
ownerId: entry.id,
|
|
59294
59584
|
kind: "keyFrame",
|
|
59295
|
-
timestamp:
|
|
59585
|
+
timestamp: stamp,
|
|
59296
59586
|
data: Buffer.from(jpeg),
|
|
59297
59587
|
subjectBox: {
|
|
59298
59588
|
x: entry.bbox.x,
|
|
@@ -59303,12 +59593,20 @@ async function withParcelStill(input) {
|
|
|
59303
59593
|
frameHeight: entry.frameHeight
|
|
59304
59594
|
}
|
|
59305
59595
|
});
|
|
59596
|
+
logger.info("package still stored", {
|
|
59597
|
+
tags,
|
|
59598
|
+
meta: {
|
|
59599
|
+
entryId: entry.id,
|
|
59600
|
+
mediaId: keyFrameMediaId,
|
|
59601
|
+
source
|
|
59602
|
+
}
|
|
59603
|
+
});
|
|
59306
59604
|
return {
|
|
59307
59605
|
...entry,
|
|
59308
59606
|
keyFrameMediaId
|
|
59309
59607
|
};
|
|
59310
59608
|
} catch (err) {
|
|
59311
|
-
return miss("write-failed", errMsg(err));
|
|
59609
|
+
return miss("write-failed", { error: errMsg(err) });
|
|
59312
59610
|
}
|
|
59313
59611
|
}
|
|
59314
59612
|
/**
|
|
@@ -70967,6 +71265,7 @@ Object.freeze({
|
|
|
70967
71265
|
"addon-pages-source": ["listPages"],
|
|
70968
71266
|
"addon-routes": ["getRoutes"],
|
|
70969
71267
|
"addon-widgets-source": ["listWidgets"],
|
|
71268
|
+
"board-key-source": ["listSources"],
|
|
70970
71269
|
"broker": ["list", "listProviders"],
|
|
70971
71270
|
"calendar-source": ["listCalendars", "listEvents"],
|
|
70972
71271
|
"custom-model-registry": ["listModels"],
|
|
@@ -72495,7 +72794,7 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
72495
72794
|
type: "slider",
|
|
72496
72795
|
key: "packageDropActivityWindowSec",
|
|
72497
72796
|
label: "Deliverer window",
|
|
72498
|
-
description: "A delivery only counts if a person or vehicle was in the configured package drop-off zone within this many seconds before the package appeared — and a pick-up only with somebody in that zone when it vanished. Motion elsewhere on the camera does not count. 0 disables the requirement.",
|
|
72797
|
+
description: "A delivery only counts if a person or vehicle was in the configured package drop-off zone within this many seconds before the package appeared — and a pick-up only with somebody in that zone when it vanished. Person-sized motion beside the drop-off zone also counts when nobody was tracked. Motion elsewhere on the camera does not count. 0 disables the requirement.",
|
|
72499
72798
|
min: 0,
|
|
72500
72799
|
max: 1800,
|
|
72501
72800
|
step: 30,
|
|
@@ -87015,6 +87314,13 @@ var NC_STILL_WAIT_MAX_AGE_MS = 3e4;
|
|
|
87015
87314
|
function canStillGainMedia(entry) {
|
|
87016
87315
|
return entry.recordKind === "object-event";
|
|
87017
87316
|
}
|
|
87317
|
+
/** The variables that carry a finished cycle's run summary. */
|
|
87318
|
+
var CYCLE_SUMMARY_VARS = [
|
|
87319
|
+
"duration",
|
|
87320
|
+
"energy",
|
|
87321
|
+
"afterDuration",
|
|
87322
|
+
"usedEnergy"
|
|
87323
|
+
];
|
|
87018
87324
|
var NcDispatcher = class {
|
|
87019
87325
|
deps;
|
|
87020
87326
|
targetCache = null;
|
|
@@ -87918,6 +88224,7 @@ var NcDispatcher = class {
|
|
|
87918
88224
|
}
|
|
87919
88225
|
/** Rules already warned about a deprecated variable in this process — once each. */
|
|
87920
88226
|
deprecatedVarWarned = /* @__PURE__ */ new Set();
|
|
88227
|
+
cycleSummaryWarned = /* @__PURE__ */ new Set();
|
|
87921
88228
|
/**
|
|
87922
88229
|
* A rule template written by an older client may still say `{{camera}}`. It
|
|
87923
88230
|
* renders (the alias), and the rule is named once so the operator can edit it.
|
|
@@ -87937,6 +88244,27 @@ var NcDispatcher = class {
|
|
|
87937
88244
|
});
|
|
87938
88245
|
}
|
|
87939
88246
|
/**
|
|
88247
|
+
* A FINISHED-cycle row whose own template places none of the four cycle
|
|
88248
|
+
* variables delivers no run summary (D391: accepted, produced less). Said
|
|
88249
|
+
* once per rule; the operator's text is never touched.
|
|
88250
|
+
*/
|
|
88251
|
+
warnCycleSummaryDropped(entry) {
|
|
88252
|
+
const deviceId = entry.payload.subject.applianceCycle?.deviceId;
|
|
88253
|
+
const template = entry.payload.template;
|
|
88254
|
+
if (deviceId === void 0 || template === void 0) return;
|
|
88255
|
+
if (this.cycleSummaryWarned.has(entry.ruleId)) return;
|
|
88256
|
+
const used = new Set([...templatePlaceholders(template.title ?? ""), ...templatePlaceholders(template.body ?? "")]);
|
|
88257
|
+
if (CYCLE_SUMMARY_VARS.some((name) => used.has(name))) return;
|
|
88258
|
+
this.cycleSummaryWarned.add(entry.ruleId);
|
|
88259
|
+
this.deps.logger.warn("appliance cycle finished but the rule template places no run summary — the message carries no duration or energy", {
|
|
88260
|
+
tags: {
|
|
88261
|
+
deviceId,
|
|
88262
|
+
ruleId: entry.ruleId
|
|
88263
|
+
},
|
|
88264
|
+
meta: { placeholders: [...CYCLE_SUMMARY_VARS] }
|
|
88265
|
+
});
|
|
88266
|
+
}
|
|
88267
|
+
/**
|
|
87940
88268
|
* The facts of a finished appliance cycle; unknown on every other row and on
|
|
87941
88269
|
* any failure (a part that cannot be read is omitted, never a blocked send).
|
|
87942
88270
|
*/
|
|
@@ -87981,6 +88309,7 @@ var NcDispatcher = class {
|
|
|
87981
88309
|
const cycleFacts = await this.applianceCycleFacts(entry);
|
|
87982
88310
|
const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
|
|
87983
88311
|
this.warnDeprecatedVars(entry);
|
|
88312
|
+
this.warnCycleSummaryDropped(entry);
|
|
87984
88313
|
const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
|
|
87985
88314
|
const title = renderRowTitle(this.texts, entry, vars);
|
|
87986
88315
|
const authoredBody = entry.payload.template?.body;
|
|
@@ -101963,7 +102292,10 @@ function buildProvider(host) {
|
|
|
101963
102292
|
catalog: [...NC_CONDITION_CATALOG],
|
|
101964
102293
|
taxonomy: NC_TAXONOMY
|
|
101965
102294
|
}),
|
|
101966
|
-
getTemplateCatalog: async () => ({
|
|
102295
|
+
getTemplateCatalog: async () => ({
|
|
102296
|
+
vars: servedTemplateVars(),
|
|
102297
|
+
defaults: servedTemplateDefaults(host.texts)
|
|
102298
|
+
}),
|
|
101967
102299
|
previewTemplate: async (input) => previewTemplate(host.texts, input),
|
|
101968
102300
|
getHistory: async ({ filter }) => {
|
|
101969
102301
|
const entries = await host.outbox.queryHistory({
|
|
@@ -124900,6 +125232,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
124900
125232
|
this.packageDropDetector?.forgetDevice(data.deviceId);
|
|
124901
125233
|
this.packageScene?.forget(data.deviceId);
|
|
124902
125234
|
this.frameState.lastNonPackageActivityAt.delete(data.deviceId);
|
|
125235
|
+
this.frameState.lastPackageZoneMotionAt.delete(data.deviceId);
|
|
124903
125236
|
this.frameState.packageCollectors.forgetDevice(data.deviceId);
|
|
124904
125237
|
this.frameState.packageOversizeLogAt.delete(data.deviceId);
|
|
124905
125238
|
this.frameState.packageLookFrames.delete(data.deviceId);
|
|
@@ -124924,6 +125257,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
124924
125257
|
this.packageDropDetector?.forgetDevice(deviceId);
|
|
124925
125258
|
this.packageScene?.forget(deviceId);
|
|
124926
125259
|
this.frameState.lastNonPackageActivityAt.delete(deviceId);
|
|
125260
|
+
this.frameState.lastPackageZoneMotionAt.delete(deviceId);
|
|
124927
125261
|
this.frameState.packageCollectors.forgetDevice(deviceId);
|
|
124928
125262
|
this.frameState.packageOversizeLogAt.delete(deviceId);
|
|
124929
125263
|
this.frameState.packageLookFrames.delete(deviceId);
|
|
@@ -127496,6 +127830,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127496
127830
|
detected,
|
|
127497
127831
|
atMs: timestamp
|
|
127498
127832
|
});
|
|
127833
|
+
if (detected) await this.stampPackageZoneMotion(deviceId, payload, timestamp);
|
|
127499
127834
|
if (detected) this.eventStore?.noteMotionSample(deviceId, payload.regionCount, timestamp);
|
|
127500
127835
|
await this.motionEpisodes?.observe(deviceId, "analyzer", detected, timestamp, {
|
|
127501
127836
|
regionCount: payload.regionCount,
|
|
@@ -127514,6 +127849,28 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127514
127849
|
});
|
|
127515
127850
|
}
|
|
127516
127851
|
/**
|
|
127852
|
+
* Stamp analyzer motion that reached a package drop-off zone (D1003): the
|
|
127853
|
+
* courier who never became an L1 track still backs the OPEN of a candidate.
|
|
127854
|
+
* Onboard motion carries no regions or dimensions and never stamps.
|
|
127855
|
+
*/
|
|
127856
|
+
async stampPackageZoneMotion(deviceId, payload, timestamp) {
|
|
127857
|
+
const proxy = await this.ensureProxy(deviceId);
|
|
127858
|
+
const packageZoneIds = collectPackageZoneIds(proxy?.state.zoneRules.value?.package ?? []);
|
|
127859
|
+
if (packageZoneIds.size === 0) return;
|
|
127860
|
+
const zoneRect = packageZoneCropRect({
|
|
127861
|
+
zones: proxy?.state.zones.value?.zones ?? [],
|
|
127862
|
+
packageZoneIds,
|
|
127863
|
+
frameWidth: payload.frameWidth,
|
|
127864
|
+
frameHeight: payload.frameHeight
|
|
127865
|
+
});
|
|
127866
|
+
if (delivererMotionNearPackageZone({
|
|
127867
|
+
regions: payload.regions,
|
|
127868
|
+
frameWidth: payload.frameWidth,
|
|
127869
|
+
frameHeight: payload.frameHeight,
|
|
127870
|
+
zoneRect
|
|
127871
|
+
})) this.frameState.lastPackageZoneMotionAt.set(deviceId, timestamp);
|
|
127872
|
+
}
|
|
127873
|
+
/**
|
|
127517
127874
|
* Persist firmware-driven (onboard / device-activity) motion events into
|
|
127518
127875
|
* the SAME episode tracker as {@link handleMotionAnalysis}, keyed per
|
|
127519
127876
|
* `(deviceId, source)` so the two paths can never share — or fight over —
|
|
@@ -127846,6 +128203,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127846
128203
|
encodeJpeg: request.encodeJpeg
|
|
127847
128204
|
}, request.pin);
|
|
127848
128205
|
}
|
|
128206
|
+
zoneMotionStamp(deviceId) {
|
|
128207
|
+
const at = this.frameState.lastPackageZoneMotionAt.get(deviceId);
|
|
128208
|
+
return at === void 0 ? {} : { lastDelivererMotionAt: at };
|
|
128209
|
+
}
|
|
127849
128210
|
deliverPackageSceneSightings(delivery) {
|
|
127850
128211
|
const settings = this.packageDropSettingsFromCache(delivery.deviceId);
|
|
127851
128212
|
const lastActivityAt = this.frameState.lastNonPackageActivityAt.get(delivery.deviceId);
|
|
@@ -127859,6 +128220,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127859
128220
|
minDwellMs: settings.packageDropDwellSec * 1e3,
|
|
127860
128221
|
activityWindowMs: settings.packageDropActivityWindowSec * 1e3,
|
|
127861
128222
|
...lastActivityAt !== void 0 ? { lastActivityAt } : {},
|
|
128223
|
+
...this.zoneMotionStamp(delivery.deviceId),
|
|
127862
128224
|
looked: false
|
|
127863
128225
|
});
|
|
127864
128226
|
this.bridgePackageStillness({
|
|
@@ -128044,6 +128406,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128044
128406
|
minDwellMs: input.settings.packageDropDwellSec * 1e3,
|
|
128045
128407
|
activityWindowMs: input.settings.packageDropActivityWindowSec * 1e3,
|
|
128046
128408
|
...lastActivityAt !== void 0 ? { lastActivityAt } : {},
|
|
128409
|
+
...this.zoneMotionStamp(input.deviceId),
|
|
128047
128410
|
...lookEvidence
|
|
128048
128411
|
});
|
|
128049
128412
|
this.bridgePackageStillness({
|
|
@@ -128149,11 +128512,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128149
128512
|
const lookFrame = this.frameState.packageLookFrames.get(input.deviceId);
|
|
128150
128513
|
promoteWithStill({
|
|
128151
128514
|
entry,
|
|
128152
|
-
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <=
|
|
128515
|
+
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <= PACKAGE_NATIVE_HANDLE_MAX_AGE_MS ? lookFrame.handle : null,
|
|
128153
128516
|
timestamp: lookFrame?.at ?? input.timestamp,
|
|
128154
128517
|
query: (request) => this.queryPackageSceneCrop(request),
|
|
128155
128518
|
writer: this.mediaStore,
|
|
128156
128519
|
logger: this.ctx.logger,
|
|
128520
|
+
live: async () => {
|
|
128521
|
+
const snap = await this.ctx.api.snapshot.getSnapshot.query({ deviceId: input.deviceId });
|
|
128522
|
+
return snap === null ? null : Uint8Array.from(Buffer.from(snap.base64, "base64"));
|
|
128523
|
+
},
|
|
128157
128524
|
detector: this.packageDropDetector
|
|
128158
128525
|
}).catch((err) => {
|
|
128159
128526
|
this.ctx.logger.warn("package promotion failed", {
|
|
@@ -128235,6 +128602,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128235
128602
|
});
|
|
128236
128603
|
}).catch(() => void 0);
|
|
128237
128604
|
}
|
|
128605
|
+
for (const c of outcome.continued) this.ctx.logger.info("package candidate continued by a sub-floor box", {
|
|
128606
|
+
tags,
|
|
128607
|
+
meta: {
|
|
128608
|
+
entryId: c.id,
|
|
128609
|
+
hits: c.hits,
|
|
128610
|
+
score: c.score,
|
|
128611
|
+
via: input.via
|
|
128612
|
+
}
|
|
128613
|
+
});
|
|
128238
128614
|
for (const u of outcome.earlyEvidenceUnbacked) this.ctx.logger.info("early pick-up evidence complete — nobody around, left to the TTL", {
|
|
128239
128615
|
tags,
|
|
128240
128616
|
meta: {
|
|
@@ -128262,6 +128638,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128262
128638
|
observedDwellMs,
|
|
128263
128639
|
requiredDwellMs,
|
|
128264
128640
|
activityBackedAtOpen: ex.activityBackedAtOpen,
|
|
128641
|
+
lastZoneMotionAt: this.frameState.lastPackageZoneMotionAt.get(input.deviceId) ?? null,
|
|
128265
128642
|
reasons
|
|
128266
128643
|
}
|
|
128267
128644
|
});
|