@camstack/addon-post-analysis 1.2.664 → 1.2.666
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 +17 -0
- package/dist/{clip-model-registry-TeNUAAPP.js → clip-model-registry-BIhYJRFt.js} +324 -132
- package/dist/{clip-model-registry-Ch9Sz-Qb.mjs → clip-model-registry-Uc_C1x81.mjs} +319 -133
- 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-CXG7YPYq.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-FkeZoNXj.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CnYZAdvw.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-BZ_D3_sF.mjs → hostInit-BBhgVoNl.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +430 -46
- package/dist/pipeline-analytics/index.mjs +430 -46
- 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-CIt_RDlm.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-4TTlyJ66.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-Uc_C1x81.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";
|
|
@@ -7346,6 +7346,18 @@ function sensorEdgeOf(kind, value) {
|
|
|
7346
7346
|
if (HALTED.has(opening.state)) return "neither";
|
|
7347
7347
|
if (OPENING.has(opening.state)) return "opening";
|
|
7348
7348
|
}
|
|
7349
|
+
/**
|
|
7350
|
+
* The WORD a sensor change is told with — the state the device moved to, read off
|
|
7351
|
+
* the same slice the edge is: `unlocked`, `open`, `closed`… for a lock, cover, contact
|
|
7352
|
+
* or switch, else `raised` / `cleared` for a boolean sensor (water, smoke…). Undefined
|
|
7353
|
+
* when the slice cannot say: a sentence never invents a state (D393).
|
|
7354
|
+
*/
|
|
7355
|
+
function sensorStateWordOf(kind, value, raised) {
|
|
7356
|
+
const opening = alarmOpeningFromSensorEvent(kind, value);
|
|
7357
|
+
if (opening.kind === "reading") return opening.state;
|
|
7358
|
+
if (raised === void 0) return void 0;
|
|
7359
|
+
return raised ? "raised" : "cleared";
|
|
7360
|
+
}
|
|
7349
7361
|
//#endregion
|
|
7350
7362
|
//#region src/notification-center/audio-rule-matcher.ts
|
|
7351
7363
|
/**
|
|
@@ -7853,6 +7865,7 @@ function subjectFromSensorEvent(ev, markerTrackId, linked) {
|
|
|
7853
7865
|
const eventType = readSensorEventType(ev.value);
|
|
7854
7866
|
const sensorEdge = sensorEdgeOf(ev.kind, ev.value);
|
|
7855
7867
|
const sensorRaised = sensorRaisedOf(ev.kind, ev.value);
|
|
7868
|
+
const sensorState = sensorStateWordOf(ev.kind, ev.value, sensorRaised);
|
|
7856
7869
|
return {
|
|
7857
7870
|
kind: "device-event",
|
|
7858
7871
|
recordId: String(ev.id),
|
|
@@ -7867,6 +7880,7 @@ function subjectFromSensorEvent(ev, markerTrackId, linked) {
|
|
|
7867
7880
|
...eventType !== void 0 ? { eventType } : {},
|
|
7868
7881
|
...sensorEdge !== void 0 ? { sensorEdge } : {},
|
|
7869
7882
|
...sensorRaised !== void 0 ? { sensorRaised } : {},
|
|
7883
|
+
...sensorState !== void 0 ? { sensorState } : {},
|
|
7870
7884
|
...linked !== void 0 ? {
|
|
7871
7885
|
linkedSensorId: ev.sourceDeviceId,
|
|
7872
7886
|
linkedCameraIds: [...linked.linkedCameraIds],
|
|
@@ -9205,6 +9219,17 @@ var en_default = {
|
|
|
9205
9219
|
"alarm.combined.title": "Alarm: {{deviceName}}",
|
|
9206
9220
|
"alarm.intrusion.title": "Intrusion — {{locations}}",
|
|
9207
9221
|
"alarm.intrusion.body": "Intrusion: {{locations}}",
|
|
9222
|
+
"alarm.intrusion.body.withChanges": "Intrusion: {{locations}} — {{changes}}",
|
|
9223
|
+
"alarm.change.lock.unlocked": "{{device}} unlocked",
|
|
9224
|
+
"alarm.change.lock.unlocking": "{{device}} unlocking",
|
|
9225
|
+
"alarm.change.lock.jammed": "{{device}} jammed",
|
|
9226
|
+
"alarm.change.lock.door-open": "{{device}} open",
|
|
9227
|
+
"alarm.change.open": "{{device}} open",
|
|
9228
|
+
"alarm.change.opening": "{{device}} opening",
|
|
9229
|
+
"alarm.change.closed": "{{device}} closed",
|
|
9230
|
+
"alarm.change.on": "{{device}} on",
|
|
9231
|
+
"alarm.change.raised": "{{device}} triggered",
|
|
9232
|
+
"alarm.change.cleared": "{{device}} cleared",
|
|
9208
9233
|
"system.alarm-arm-refused.title.home": "Arming home refused",
|
|
9209
9234
|
"system.alarm-arm-refused.title.away": "Arming away refused",
|
|
9210
9235
|
"system.alarm-arm-refused.title.night": "Arming night refused",
|
|
@@ -9622,6 +9647,17 @@ var it_default = {
|
|
|
9622
9647
|
"alarm.combined.title": "Allarme: {{deviceName}}",
|
|
9623
9648
|
"alarm.intrusion.title": "Intrusione — {{locations}}",
|
|
9624
9649
|
"alarm.intrusion.body": "Intrusione: {{locations}}",
|
|
9650
|
+
"alarm.intrusion.body.withChanges": "Intrusione: {{locations}} — {{changes}}",
|
|
9651
|
+
"alarm.change.lock.unlocked": "{{device}} aperta",
|
|
9652
|
+
"alarm.change.lock.unlocking": "{{device}} in apertura",
|
|
9653
|
+
"alarm.change.lock.jammed": "{{device}} bloccata",
|
|
9654
|
+
"alarm.change.lock.door-open": "{{device}} aperta",
|
|
9655
|
+
"alarm.change.open": "{{device}} aperto",
|
|
9656
|
+
"alarm.change.opening": "{{device}} in apertura",
|
|
9657
|
+
"alarm.change.closed": "{{device}} chiuso",
|
|
9658
|
+
"alarm.change.on": "{{device}} acceso",
|
|
9659
|
+
"alarm.change.raised": "{{device}} attivato",
|
|
9660
|
+
"alarm.change.cleared": "{{device}} ripristinato",
|
|
9625
9661
|
"system.alarm-arm-refused.title.home": "Inserimento Casa rifiutato",
|
|
9626
9662
|
"system.alarm-arm-refused.title.away": "Inserimento Fuori rifiutato",
|
|
9627
9663
|
"system.alarm-arm-refused.title.night": "Inserimento Notte rifiutato",
|
|
@@ -12276,6 +12312,16 @@ var NcStateSummaryTestSchema = object({
|
|
|
12276
12312
|
}))
|
|
12277
12313
|
});
|
|
12278
12314
|
//#endregion
|
|
12315
|
+
//#region src/notification-center/template-defaults.ts
|
|
12316
|
+
function servedTemplateDefaults(texts) {
|
|
12317
|
+
return [{
|
|
12318
|
+
id: "appliance-cycle",
|
|
12319
|
+
kind: "sensor",
|
|
12320
|
+
title: texts.raw("appliance.finished.title") ?? "",
|
|
12321
|
+
body: texts.raw("appliance.finished.body") ?? ""
|
|
12322
|
+
}];
|
|
12323
|
+
}
|
|
12324
|
+
//#endregion
|
|
12279
12325
|
//#region src/notification-center/clock-text.ts
|
|
12280
12326
|
/**
|
|
12281
12327
|
* `{{time}}` — hours and minutes in the NOTIFICATION language.
|
|
@@ -13659,7 +13705,10 @@ var ncActions = defineCustomActions({
|
|
|
13659
13705
|
catalog: array(NcConditionDescriptorSchema),
|
|
13660
13706
|
taxonomy: NcTaxonomySchema
|
|
13661
13707
|
})),
|
|
13662
|
-
"nc.getTemplateCatalog": customAction(object({}), object({
|
|
13708
|
+
"nc.getTemplateCatalog": customAction(object({}), object({
|
|
13709
|
+
vars: array(NcTemplateVarDescriptorSchema),
|
|
13710
|
+
defaults: array(NcTemplateDefaultSchema)
|
|
13711
|
+
})),
|
|
13663
13712
|
"nc.previewTemplate": customAction(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, { kind: "mutation" }),
|
|
13664
13713
|
"nc.createRule": customAction(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
|
|
13665
13714
|
kind: "mutation",
|
|
@@ -14439,7 +14488,10 @@ function makeNcActionHandlers(deps) {
|
|
|
14439
14488
|
catalog: [...NC_CONDITION_CATALOG],
|
|
14440
14489
|
taxonomy: NC_TAXONOMY
|
|
14441
14490
|
}),
|
|
14442
|
-
"nc.getTemplateCatalog": async () => ({
|
|
14491
|
+
"nc.getTemplateCatalog": async () => ({
|
|
14492
|
+
vars: servedTemplateVars(),
|
|
14493
|
+
defaults: deps.previewTexts === void 0 ? [] : servedTemplateDefaults(deps.previewTexts)
|
|
14494
|
+
}),
|
|
14443
14495
|
"nc.previewTemplate": async (input) => {
|
|
14444
14496
|
const texts = deps.previewTexts;
|
|
14445
14497
|
if (texts === void 0) throw new Error("nc.previewTemplate: text catalog not wired");
|
|
@@ -49856,7 +49908,7 @@ async function encodeRgbCropToJpeg(bytes, width, height) {
|
|
|
49856
49908
|
* what lets the caller stop base64-ing purely to be understood.
|
|
49857
49909
|
*/
|
|
49858
49910
|
async function decodeJpegToRgb(jpeg) {
|
|
49859
|
-
const { data, info } = await sharp(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).raw().toBuffer({ resolveWithObject: true });
|
|
49911
|
+
const { data, info } = await sharp(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).removeAlpha().toColourspace("srgb").raw().toBuffer({ resolveWithObject: true });
|
|
49860
49912
|
return {
|
|
49861
49913
|
bytes: data,
|
|
49862
49914
|
width: info.width,
|
|
@@ -52942,6 +52994,42 @@ function collectPackageZoneIds(rules) {
|
|
|
52942
52994
|
}
|
|
52943
52995
|
return ids;
|
|
52944
52996
|
}
|
|
52997
|
+
/**
|
|
52998
|
+
* How much the package zone box grows on every side when asking whether motion
|
|
52999
|
+
* reached it. Mirrors the D144 dilation of the pipeline's burst trigger
|
|
53000
|
+
* (`PACKAGE_ZONE_DILATION` in addon-pipeline, not importable from here): about
|
|
53001
|
+
* one adult's width beside a doormat, the place a courier stands.
|
|
53002
|
+
*/
|
|
53003
|
+
var PACKAGE_ZONE_MOTION_DILATION = .5;
|
|
53004
|
+
/**
|
|
53005
|
+
* A motion region covering more than this fraction of the frame is a light
|
|
53006
|
+
* change or the weather, not a person: it never counts as a deliverer.
|
|
53007
|
+
*/
|
|
53008
|
+
var PACKAGE_ZONE_MOTION_MAX_REGION_FRAC = .8;
|
|
53009
|
+
/**
|
|
53010
|
+
* Did analyzer motion reach the dilated package zone with a person-sized
|
|
53011
|
+
* region? Unknown geometry answers false: this stamp only ever BACKS the open
|
|
53012
|
+
* of a candidate, so failing closed keeps the light-wedge guard intact.
|
|
53013
|
+
*/
|
|
53014
|
+
function delivererMotionNearPackageZone(input) {
|
|
53015
|
+
const { zoneRect, frameWidth, frameHeight } = input;
|
|
53016
|
+
if (zoneRect === null || frameWidth <= 0 || frameHeight <= 0) return false;
|
|
53017
|
+
const dx = zoneRect.w * PACKAGE_ZONE_MOTION_DILATION;
|
|
53018
|
+
const dy = zoneRect.h * PACKAGE_ZONE_MOTION_DILATION;
|
|
53019
|
+
const left = Math.max(0, zoneRect.x - dx);
|
|
53020
|
+
const top = Math.max(0, zoneRect.y - dy);
|
|
53021
|
+
const right = Math.min(1, zoneRect.x + zoneRect.w + dx);
|
|
53022
|
+
const bottom = Math.min(1, zoneRect.y + zoneRect.h + dy);
|
|
53023
|
+
for (const region of input.regions) {
|
|
53024
|
+
const x = region.bbox.x / frameWidth;
|
|
53025
|
+
const y = region.bbox.y / frameHeight;
|
|
53026
|
+
const w = region.bbox.w / frameWidth;
|
|
53027
|
+
const h = region.bbox.h / frameHeight;
|
|
53028
|
+
if (w * h > PACKAGE_ZONE_MOTION_MAX_REGION_FRAC) continue;
|
|
53029
|
+
if (x < right && left < x + w && y < bottom && top < y + h) return true;
|
|
53030
|
+
}
|
|
53031
|
+
return false;
|
|
53032
|
+
}
|
|
52945
53033
|
//#endregion
|
|
52946
53034
|
//#region src/pipeline-analytics/pipeline/package/package-collector-mirror.ts
|
|
52947
53035
|
/**
|
|
@@ -53887,6 +53975,15 @@ var PACKAGE_STILLNESS_TTL_MS = 5 * 6e4;
|
|
|
53887
53975
|
*/
|
|
53888
53976
|
var PACKAGE_STILLNESS_ASSOC_IOU = .3;
|
|
53889
53977
|
/**
|
|
53978
|
+
* Lowest score of a below-threshold box that may CONTINUE an open candidate
|
|
53979
|
+
* (D665: a box under the floor may continue, never open). Observed on a real
|
|
53980
|
+
* parcel after the day to IR flip: 0.35 to 0.50.
|
|
53981
|
+
*/
|
|
53982
|
+
var PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE = .3;
|
|
53983
|
+
/** Above-floor sightings a candidate needs before a sub-floor box may count:
|
|
53984
|
+
* one confident look plus a doormat-shaped reading is not a parcel. */
|
|
53985
|
+
var PACKAGE_CONTINUATION_MIN_REAL_HITS = 2;
|
|
53986
|
+
/**
|
|
53890
53987
|
* Two absence reads closer than this are ONE look, not two. A settle burst
|
|
53891
53988
|
* runs the package step 5 s apart and a poll 45 s apart, so a real second
|
|
53892
53989
|
* look is always further away; two frames of one moment (a re-dispatch, a
|
|
@@ -53936,6 +54033,7 @@ var PackageStillnessTracker = class {
|
|
|
53936
54033
|
const next = [...this.byDevice.get(input.deviceId) ?? []];
|
|
53937
54034
|
const consumed = /* @__PURE__ */ new Set();
|
|
53938
54035
|
const promoted = [];
|
|
54036
|
+
const continuedLog = [];
|
|
53939
54037
|
for (const s of input.sightings) {
|
|
53940
54038
|
const index = this.bestMatch(next, s, consumed);
|
|
53941
54039
|
if (index < 0) {
|
|
@@ -53948,17 +54046,33 @@ var PackageStillnessTracker = class {
|
|
|
53948
54046
|
if (prev === void 0) continue;
|
|
53949
54047
|
consumed.add(prev.id);
|
|
53950
54048
|
const updated = confirm(prev, input, s);
|
|
53951
|
-
const
|
|
53952
|
-
|
|
53953
|
-
|
|
53954
|
-
|
|
53955
|
-
|
|
53956
|
-
const
|
|
53957
|
-
|
|
53958
|
-
|
|
54049
|
+
const parked = this.promoteWhenReady(updated, input);
|
|
54050
|
+
next[index] = parked ?? updated;
|
|
54051
|
+
if (parked !== null) promoted.push(toEntry(parked));
|
|
54052
|
+
}
|
|
54053
|
+
if (input.looked === true && input.continuationBoxes !== void 0) for (let i = 0; i < next.length; i += 1) {
|
|
54054
|
+
const c = next[i];
|
|
54055
|
+
if (c === void 0 || c.promotedAt !== null || consumed.has(c.id)) continue;
|
|
54056
|
+
if (c.realHits < PACKAGE_CONTINUATION_MIN_REAL_HITS) continue;
|
|
54057
|
+
const box = this.continuationFor(c, input.continuationBoxes);
|
|
54058
|
+
if (box === null) continue;
|
|
54059
|
+
consumed.add(c.id);
|
|
54060
|
+
const continued = {
|
|
54061
|
+
...c,
|
|
54062
|
+
lastSeenAt: input.timestamp,
|
|
54063
|
+
hits: c.hits + 1,
|
|
54064
|
+
absenceReads: [],
|
|
54065
|
+
lastLook: "present",
|
|
54066
|
+
earlyUnbackedReported: false
|
|
53959
54067
|
};
|
|
53960
|
-
|
|
53961
|
-
|
|
54068
|
+
continuedLog.push({
|
|
54069
|
+
id: c.id,
|
|
54070
|
+
hits: continued.hits,
|
|
54071
|
+
score: box.score
|
|
54072
|
+
});
|
|
54073
|
+
const parked = this.promoteWhenReady(continued, input);
|
|
54074
|
+
next[i] = parked ?? continued;
|
|
54075
|
+
if (parked !== null) promoted.push(toEntry(parked));
|
|
53962
54076
|
}
|
|
53963
54077
|
const present = input.rawPackageBoxes !== void 0 ? withRawPresence(next, consumed, input.rawPackageBoxes, input.timestamp, this.config.assocIou) : next;
|
|
53964
54078
|
const looked = input.looked === true && input.rawPackageBoxes !== void 0 ? withLook(present, consumed, input, input.rawPackageBoxes, this.config.assocIou) : present;
|
|
@@ -53994,7 +54108,8 @@ var PackageStillnessTracker = class {
|
|
|
53994
54108
|
...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
|
|
53995
54109
|
}),
|
|
53996
54110
|
earlyDepartureIds: [...early],
|
|
53997
|
-
earlyEvidenceUnbacked: unbacked
|
|
54111
|
+
earlyEvidenceUnbacked: unbacked,
|
|
54112
|
+
continued: continuedLog
|
|
53998
54113
|
};
|
|
53999
54114
|
}
|
|
54000
54115
|
/** Why complete early evidence may NOT conclude, or null when it may. */
|
|
@@ -54021,7 +54136,8 @@ var PackageStillnessTracker = class {
|
|
|
54021
54136
|
...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
|
|
54022
54137
|
}),
|
|
54023
54138
|
earlyDepartureIds: [],
|
|
54024
|
-
earlyEvidenceUnbacked: []
|
|
54139
|
+
earlyEvidenceUnbacked: [],
|
|
54140
|
+
continued: []
|
|
54025
54141
|
};
|
|
54026
54142
|
}
|
|
54027
54143
|
/** The devices holding a candidate — what a sweep has to visit. */
|
|
@@ -54085,6 +54201,43 @@ var PackageStillnessTracker = class {
|
|
|
54085
54201
|
/** Best same-class candidate over the association floor, ignoring any already
|
|
54086
54202
|
* consumed by an earlier sighting in THIS frame (two boxes never collapse
|
|
54087
54203
|
* into one parcel). */
|
|
54204
|
+
continuationFor(c, boxes) {
|
|
54205
|
+
let best = null;
|
|
54206
|
+
let bestIou = this.config.assocIou;
|
|
54207
|
+
for (const b of boxes) {
|
|
54208
|
+
if (b.score < PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE) continue;
|
|
54209
|
+
const overlap = iou$2(c.bbox, b.bbox);
|
|
54210
|
+
if (overlap >= bestIou) {
|
|
54211
|
+
bestIou = overlap;
|
|
54212
|
+
best = b;
|
|
54213
|
+
}
|
|
54214
|
+
}
|
|
54215
|
+
return best;
|
|
54216
|
+
}
|
|
54217
|
+
promoteWhenReady(candidate, input) {
|
|
54218
|
+
const updated = this.backedLate(candidate, input);
|
|
54219
|
+
const minHits = input.minHits ?? this.config.minHits;
|
|
54220
|
+
return updated.promotedAt === null && updated.activityBackedAtOpen && updated.hits >= minHits && input.timestamp - updated.firstSeenAt >= input.minDwellMs ? {
|
|
54221
|
+
...updated,
|
|
54222
|
+
promotedAt: input.timestamp
|
|
54223
|
+
} : null;
|
|
54224
|
+
}
|
|
54225
|
+
/**
|
|
54226
|
+
* Zone motion learned AFTER the open still backs the candidate when its stamp
|
|
54227
|
+
* lies in the window around the FIRST sighting (the same slack a pick-up
|
|
54228
|
+
* gets): the backing stays anchored at the open, a later passer-by never
|
|
54229
|
+
* validates scenery (D1003).
|
|
54230
|
+
*/
|
|
54231
|
+
backedLate(c, input) {
|
|
54232
|
+
if (c.activityBackedAtOpen || c.promotedAt !== null) return c;
|
|
54233
|
+
const at = input.lastDelivererMotionAt;
|
|
54234
|
+
const windowMs = input.activityWindowMs ?? 0;
|
|
54235
|
+
if (at === void 0 || windowMs <= 0) return c;
|
|
54236
|
+
return at >= c.firstSeenAt - windowMs && at <= c.firstSeenAt + this.config.roundGapMs ? {
|
|
54237
|
+
...c,
|
|
54238
|
+
activityBackedAtOpen: true
|
|
54239
|
+
} : c;
|
|
54240
|
+
}
|
|
54088
54241
|
bestMatch(candidates, s, consumed) {
|
|
54089
54242
|
let bestIndex = -1;
|
|
54090
54243
|
let bestIou = this.config.assocIou;
|
|
@@ -54101,7 +54254,7 @@ var PackageStillnessTracker = class {
|
|
|
54101
54254
|
}
|
|
54102
54255
|
openCandidate(input, s) {
|
|
54103
54256
|
const windowMs = input.activityWindowMs ?? 0;
|
|
54104
|
-
const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs;
|
|
54257
|
+
const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs || input.lastDelivererMotionAt !== void 0 && input.timestamp - input.lastDelivererMotionAt <= windowMs;
|
|
54105
54258
|
return {
|
|
54106
54259
|
id: this.newId(),
|
|
54107
54260
|
deviceId: input.deviceId,
|
|
@@ -54112,6 +54265,7 @@ var PackageStillnessTracker = class {
|
|
|
54112
54265
|
firstSeenAt: input.timestamp,
|
|
54113
54266
|
lastSeenAt: input.timestamp,
|
|
54114
54267
|
hits: 1,
|
|
54268
|
+
realHits: 1,
|
|
54115
54269
|
promotedAt: null,
|
|
54116
54270
|
absenceReads: [],
|
|
54117
54271
|
lastLook: null,
|
|
@@ -54134,6 +54288,7 @@ function confirm(prev, input, s) {
|
|
|
54134
54288
|
frameHeight: input.frameHeight,
|
|
54135
54289
|
lastSeenAt: input.timestamp,
|
|
54136
54290
|
hits: prev.hits + 1,
|
|
54291
|
+
realHits: prev.realHits + 1,
|
|
54137
54292
|
absenceReads: [],
|
|
54138
54293
|
lastLook: "present",
|
|
54139
54294
|
earlyUnbackedReported: false,
|
|
@@ -54670,6 +54825,9 @@ var FrameState = class {
|
|
|
54670
54825
|
* which would let the streetlight wedge vouch for its own delivery.
|
|
54671
54826
|
*/
|
|
54672
54827
|
lastNonPackageActivityAt = /* @__PURE__ */ new Map();
|
|
54828
|
+
/** Last analyzer motion near a package drop-off zone, per device (D1003).
|
|
54829
|
+
* Backs the OPEN of a candidate only; never a pick-up witness. */
|
|
54830
|
+
lastPackageZoneMotionAt = /* @__PURE__ */ new Map();
|
|
54673
54831
|
/**
|
|
54674
54832
|
* WHO was in the drop-off zone, and when (D404). The pick-up is CONCLUDED
|
|
54675
54833
|
* minutes after the fact, so the collector must be remembered while it is
|
|
@@ -58029,6 +58187,7 @@ function nextPackageLookDropEpisodes(previous, drops) {
|
|
|
58029
58187
|
//#region src/pipeline-analytics/pipeline/package/package-look-evidence.ts
|
|
58030
58188
|
function packageLookEvidence(input) {
|
|
58031
58189
|
const raw = [];
|
|
58190
|
+
const continuation = [];
|
|
58032
58191
|
for (const d of input.frame.detections) {
|
|
58033
58192
|
if (d.kind !== "first-level" || !input.packageClasses.has(d.macroClass)) continue;
|
|
58034
58193
|
raw.push({
|
|
@@ -58041,11 +58200,16 @@ function packageLookEvidence(input) {
|
|
|
58041
58200
|
for (const d of input.frame.discarded ?? []) {
|
|
58042
58201
|
if (!input.packageClasses.has(d.macroClass)) continue;
|
|
58043
58202
|
const [x1, y1, x2, y2] = d.bbox;
|
|
58044
|
-
|
|
58203
|
+
const box = {
|
|
58045
58204
|
x: x1,
|
|
58046
58205
|
y: y1,
|
|
58047
58206
|
w: x2 - x1,
|
|
58048
58207
|
h: y2 - y1
|
|
58208
|
+
};
|
|
58209
|
+
raw.push(box);
|
|
58210
|
+
if (d.reason === "below-threshold") continuation.push({
|
|
58211
|
+
bbox: box,
|
|
58212
|
+
score: d.score
|
|
58049
58213
|
});
|
|
58050
58214
|
}
|
|
58051
58215
|
return {
|
|
@@ -58057,6 +58221,7 @@ function packageLookEvidence(input) {
|
|
|
58057
58221
|
h: t.bbox.h
|
|
58058
58222
|
})),
|
|
58059
58223
|
rawPackageBoxes: raw,
|
|
58224
|
+
continuationBoxes: continuation,
|
|
58060
58225
|
earlyPickup: input.earlyPickupEnabled
|
|
58061
58226
|
};
|
|
58062
58227
|
}
|
|
@@ -58244,6 +58409,13 @@ function clamp01(value) {
|
|
|
58244
58409
|
}
|
|
58245
58410
|
//#endregion
|
|
58246
58411
|
//#region src/pipeline-analytics/pipeline/package/package-scene-diff.ts
|
|
58412
|
+
/**
|
|
58413
|
+
* Zone-crop scene difference for a packages rule (D931).
|
|
58414
|
+
*
|
|
58415
|
+
* A uniform brightness shift is removed by subtracting the median luma delta,
|
|
58416
|
+
* so a shadow that lifts the whole crop leaves no blob. What remains is a
|
|
58417
|
+
* connected region. The package model is not consulted here.
|
|
58418
|
+
*/
|
|
58247
58419
|
/** Residual, after the median shift, that counts as a changed pixel. */
|
|
58248
58420
|
var PACKAGE_SCENE_DIFF_LUMA_THRESHOLD = 28;
|
|
58249
58421
|
/** A changed region this large, relative to the zone, is the zone itself. */
|
|
@@ -58258,12 +58430,26 @@ var PACKAGE_SCENE_DIFF_SETTLE_MS = 3e3;
|
|
|
58258
58430
|
var PACKAGE_SCENE_DIFF_LOOK_GAP_MS = 2e3;
|
|
58259
58431
|
/** Confidence stamped on a scene-diff sighting the model did not box. */
|
|
58260
58432
|
var PACKAGE_SCENE_DIFF_CONFIDENCE = .5;
|
|
58433
|
+
/** True when the crop was shot in infrared (no colour). */
|
|
58434
|
+
function isIrCrop(crop) {
|
|
58435
|
+
return crop.chromaSpread < IR_CHROMA_THRESHOLD;
|
|
58436
|
+
}
|
|
58261
58437
|
function diffZoneLuma(input) {
|
|
58262
58438
|
const { before, after } = input;
|
|
58263
58439
|
if (before.width !== after.width || before.height !== after.height || before.luma.length !== after.luma.length) return {
|
|
58264
58440
|
blobs: [],
|
|
58265
58441
|
rejections: [{ reason: "crop-mismatch" }]
|
|
58266
58442
|
};
|
|
58443
|
+
if (isIrCrop(before) !== isIrCrop(after)) return {
|
|
58444
|
+
blobs: [],
|
|
58445
|
+
rejections: [{
|
|
58446
|
+
reason: "lighting-mode-changed",
|
|
58447
|
+
chroma: {
|
|
58448
|
+
before: before.chromaSpread,
|
|
58449
|
+
after: after.chromaSpread
|
|
58450
|
+
}
|
|
58451
|
+
}]
|
|
58452
|
+
};
|
|
58267
58453
|
const median = medianDelta(before.luma, after.luma);
|
|
58268
58454
|
const mask = residualMask(before.luma, after.luma, median);
|
|
58269
58455
|
const zonePx = before.width * before.height;
|
|
@@ -58478,6 +58664,7 @@ function okCrop(rgb, width, height, jpeg) {
|
|
|
58478
58664
|
width,
|
|
58479
58665
|
height,
|
|
58480
58666
|
luma: lumaFromRgb(rgb, width, height),
|
|
58667
|
+
chromaSpread: chromaSpreadFromRgb(rgb, width, height),
|
|
58481
58668
|
jpeg: new Uint8Array(jpeg)
|
|
58482
58669
|
};
|
|
58483
58670
|
}
|
|
@@ -58494,6 +58681,46 @@ function lumaFromRgb(bytes, width, height) {
|
|
|
58494
58681
|
}
|
|
58495
58682
|
return luma;
|
|
58496
58683
|
}
|
|
58684
|
+
/** The grid `IR_CHROMA_THRESHOLD` was calibrated on (`chromaSpread`, scene-crop.ts). */
|
|
58685
|
+
var CHROMA_GRID = 32;
|
|
58686
|
+
/**
|
|
58687
|
+
* Mean of max(r,g,b) - min(r,g,b), 0..255, measured on a 32x32 box-averaged
|
|
58688
|
+
* copy of the crop: the same measure and grid as `chromaSpread` in
|
|
58689
|
+
* `scene-crop.ts` (which needs sharp), so `IR_CHROMA_THRESHOLD` means the same
|
|
58690
|
+
* thing. Averaging first matters: sensor noise and JPEG chroma speckle on an IR
|
|
58691
|
+
* frame cancel out, where per-pixel they read as colour.
|
|
58692
|
+
*/
|
|
58693
|
+
function chromaSpreadFromRgb(bytes, width, height) {
|
|
58694
|
+
if (width <= 0 || height <= 0 || bytes.length < width * height * 3) return 0;
|
|
58695
|
+
const gw = Math.min(CHROMA_GRID, width);
|
|
58696
|
+
const gh = Math.min(CHROMA_GRID, height);
|
|
58697
|
+
const sum = new Float64Array(gw * gh * 3);
|
|
58698
|
+
const count = new Float64Array(gw * gh);
|
|
58699
|
+
for (let y = 0; y < height; y++) {
|
|
58700
|
+
const cy = Math.min(gh - 1, Math.floor(y * gh / height));
|
|
58701
|
+
for (let x = 0; x < width; x++) {
|
|
58702
|
+
const cx = Math.min(gw - 1, Math.floor(x * gw / width));
|
|
58703
|
+
const cell = cy * gw + cx;
|
|
58704
|
+
const o = (y * width + x) * 3;
|
|
58705
|
+
sum[cell * 3] = (sum[cell * 3] ?? 0) + (bytes[o] ?? 0);
|
|
58706
|
+
sum[cell * 3 + 1] = (sum[cell * 3 + 1] ?? 0) + (bytes[o + 1] ?? 0);
|
|
58707
|
+
sum[cell * 3 + 2] = (sum[cell * 3 + 2] ?? 0) + (bytes[o + 2] ?? 0);
|
|
58708
|
+
count[cell] = (count[cell] ?? 0) + 1;
|
|
58709
|
+
}
|
|
58710
|
+
}
|
|
58711
|
+
let total = 0;
|
|
58712
|
+
let cells = 0;
|
|
58713
|
+
for (let cell = 0; cell < gw * gh; cell++) {
|
|
58714
|
+
const n = count[cell] ?? 0;
|
|
58715
|
+
if (n === 0) continue;
|
|
58716
|
+
const r = (sum[cell * 3] ?? 0) / n;
|
|
58717
|
+
const g = (sum[cell * 3 + 1] ?? 0) / n;
|
|
58718
|
+
const b = (sum[cell * 3 + 2] ?? 0) / n;
|
|
58719
|
+
total += Math.max(r, g, b) - Math.min(r, g, b);
|
|
58720
|
+
cells += 1;
|
|
58721
|
+
}
|
|
58722
|
+
return cells === 0 ? 0 : total / cells;
|
|
58723
|
+
}
|
|
58497
58724
|
//#endregion
|
|
58498
58725
|
//#region src/pipeline-analytics/pipeline/package/package-scene-step.ts
|
|
58499
58726
|
/**
|
|
@@ -58593,6 +58820,14 @@ function compareAfterLeave(prev, input) {
|
|
|
58593
58820
|
minAreaFrac: input.minAreaFrac,
|
|
58594
58821
|
occluders: input.occluders
|
|
58595
58822
|
});
|
|
58823
|
+
if (diff.rejections.some((r) => r.reason === "lighting-mode-changed")) return {
|
|
58824
|
+
state: {
|
|
58825
|
+
...rememberClear(prev, input).state,
|
|
58826
|
+
lastDiffAt: input.timestamp
|
|
58827
|
+
},
|
|
58828
|
+
sightings: [],
|
|
58829
|
+
rejections: diff.rejections
|
|
58830
|
+
};
|
|
58596
58831
|
const blocked = diff.rejections.some((r) => r.reason === "overlaps-person" || r.reason === "overlaps-vehicle");
|
|
58597
58832
|
if (diff.blobs.length === 0) {
|
|
58598
58833
|
if (blocked) return {
|
|
@@ -58636,6 +58871,20 @@ function confirmPending(prev, input) {
|
|
|
58636
58871
|
sightings: [],
|
|
58637
58872
|
rejections: []
|
|
58638
58873
|
};
|
|
58874
|
+
if (isIrCrop(prev.after) !== isIrCrop(input.crop)) return {
|
|
58875
|
+
state: {
|
|
58876
|
+
...rememberClear(prev, input).state,
|
|
58877
|
+
lastDiffAt: input.timestamp
|
|
58878
|
+
},
|
|
58879
|
+
sightings: [],
|
|
58880
|
+
rejections: [{
|
|
58881
|
+
reason: "lighting-mode-changed",
|
|
58882
|
+
chroma: {
|
|
58883
|
+
before: prev.after.chromaSpread,
|
|
58884
|
+
after: input.crop.chromaSpread
|
|
58885
|
+
}
|
|
58886
|
+
}]
|
|
58887
|
+
};
|
|
58639
58888
|
const sightings = [];
|
|
58640
58889
|
const rejections = [];
|
|
58641
58890
|
const still = [];
|
|
@@ -58682,7 +58931,7 @@ function judgeBlob(prev, input, blob) {
|
|
|
58682
58931
|
bbox: blob.bbox
|
|
58683
58932
|
} };
|
|
58684
58933
|
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;
|
|
58934
|
+
const present = isIrCrop(before) !== isIrCrop(crop) || meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
|
|
58686
58935
|
if (!stable || !present) return { rejection: {
|
|
58687
58936
|
reason: "unstable",
|
|
58688
58937
|
bbox: blob.bbox,
|
|
@@ -58850,7 +59099,8 @@ var PackageSceneCoordinator = class {
|
|
|
58850
59099
|
const crop = {
|
|
58851
59100
|
width: fetched.width,
|
|
58852
59101
|
height: fetched.height,
|
|
58853
|
-
luma: fetched.luma
|
|
59102
|
+
luma: fetched.luma,
|
|
59103
|
+
chromaSpread: fetched.chromaSpread
|
|
58854
59104
|
};
|
|
58855
59105
|
const stepped = stepPackageScene({
|
|
58856
59106
|
deviceId,
|
|
@@ -58916,6 +59166,10 @@ var PackageSceneCoordinator = class {
|
|
|
58916
59166
|
tags: { deviceId },
|
|
58917
59167
|
meta: {
|
|
58918
59168
|
reason: rejection.reason,
|
|
59169
|
+
...rejection.chroma !== void 0 ? {
|
|
59170
|
+
chromaBefore: Number(rejection.chroma.before.toFixed(2)),
|
|
59171
|
+
chromaAfter: Number(rejection.chroma.after.toFixed(2))
|
|
59172
|
+
} : {},
|
|
58919
59173
|
...rejection.areaFrac !== void 0 ? { areaFrac: Number(rejection.areaFrac.toFixed(5)) } : {}
|
|
58920
59174
|
}
|
|
58921
59175
|
});
|
|
@@ -59024,14 +59278,8 @@ function planPackageSceneFrame(deviceId, input) {
|
|
|
59024
59278
|
frameWidth: input.frameWidth,
|
|
59025
59279
|
frameHeight: input.frameHeight
|
|
59026
59280
|
});
|
|
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
59281
|
return {
|
|
59034
|
-
sightings:
|
|
59282
|
+
sightings: input.sightings,
|
|
59035
59283
|
note: {
|
|
59036
59284
|
deviceId,
|
|
59037
59285
|
timestamp: input.timestamp,
|
|
@@ -59074,15 +59322,6 @@ function occludersOf(tracked) {
|
|
|
59074
59322
|
}
|
|
59075
59323
|
return out;
|
|
59076
59324
|
}
|
|
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
59325
|
//#endregion
|
|
59087
59326
|
//#region src/pipeline-analytics/pipeline/package/package-llm-confirm.ts
|
|
59088
59327
|
/**
|
|
@@ -71062,6 +71301,7 @@ Object.freeze({
|
|
|
71062
71301
|
"addon-pages-source": ["listPages"],
|
|
71063
71302
|
"addon-routes": ["getRoutes"],
|
|
71064
71303
|
"addon-widgets-source": ["listWidgets"],
|
|
71304
|
+
"board-key-source": ["listSources"],
|
|
71065
71305
|
"broker": ["list", "listProviders"],
|
|
71066
71306
|
"calendar-source": ["listCalendars", "listEvents"],
|
|
71067
71307
|
"custom-model-registry": ["listModels"],
|
|
@@ -71192,8 +71432,8 @@ Object.freeze({
|
|
|
71192
71432
|
* ## Caps with no boolean level pass through untouched
|
|
71193
71433
|
*
|
|
71194
71434
|
* `EVENT_KIND_BY_CAP` covers sixteen caps; `SOURCE_CAP_ACTIVE_FIELD` covers the
|
|
71195
|
-
* ten that have a boolean whose edge means something. `button`, `presence
|
|
71196
|
-
*
|
|
71435
|
+
* ten that have a boolean whose edge means something. `button`, `presence`
|
|
71436
|
+
* and `event-emitter` are NOT levels (`enum-sensor` is a stampless word level, see `UNSTAMPED_WORD_FIELD`) — their ARRIVAL is the
|
|
71197
71437
|
* event, and three presses in a row are three markers. Gating them would
|
|
71198
71438
|
* re-ship the failure of 2026-08-08, when camera 101's press produced a row, a
|
|
71199
71439
|
* ring, a matched rule and no marker. (`doorbell` is not a level either, but
|
|
@@ -71319,6 +71559,15 @@ var WORD_LEVEL_FIELD = {
|
|
|
71319
71559
|
* running for an hour at boot did not just start).
|
|
71320
71560
|
*/
|
|
71321
71561
|
var BOOLEAN_LEVEL_FIELD = { "running-state": "running" };
|
|
71562
|
+
/**
|
|
71563
|
+
* Caps whose level is a WORD with NO change stamp: cap → field. `enum-sensor`
|
|
71564
|
+
* carries `{ value, lastFetchedAt }`, and `lastFetchedAt` moves on every
|
|
71565
|
+
* provider refresh, so it says nothing about when the word changed. Gated by
|
|
71566
|
+
* the word alone: the first sighting seeds, only a different word passes, and
|
|
71567
|
+
* a null / missing value is dropped and never remembered (planning#396: such
|
|
71568
|
+
* slices passed as `changed` many times a minute and could match an NC rule).
|
|
71569
|
+
*/
|
|
71570
|
+
var UNSTAMPED_WORD_FIELD = { "enum-sensor": "value" };
|
|
71322
71571
|
/** The word a word-level cap rests in — its `false`, for the first-sighting rule. */
|
|
71323
71572
|
var WORD_LEVEL_INACTIVE = {
|
|
71324
71573
|
cover: "closed",
|
|
@@ -71371,6 +71620,8 @@ var SensorTransitionGate = class {
|
|
|
71371
71620
|
*/
|
|
71372
71621
|
observe(deviceId, capName, slice) {
|
|
71373
71622
|
if (slice["lastChangedAt"] === 0) return this.drop(deviceId, capName, "cold-slice");
|
|
71623
|
+
const unstampedField = UNSTAMPED_WORD_FIELD[capName];
|
|
71624
|
+
if (unstampedField !== void 0) return this.observeUnstampedWord(deviceId, capName, slice, unstampedField);
|
|
71374
71625
|
const wordField = WORD_LEVEL_FIELD[capName];
|
|
71375
71626
|
if (wordField !== void 0) return this.observeWord(deviceId, capName, slice, wordField);
|
|
71376
71627
|
const levelField = BOOLEAN_LEVEL_FIELD[capName];
|
|
@@ -71441,11 +71692,25 @@ var SensorTransitionGate = class {
|
|
|
71441
71692
|
observeWord(deviceId, capName, slice, wordField) {
|
|
71442
71693
|
const raw = slice[wordField];
|
|
71443
71694
|
if (typeof raw !== "string" || raw.length === 0) return this.drop(deviceId, capName, "non-word-value");
|
|
71695
|
+
const stamp = slice["lastChangedAt"];
|
|
71696
|
+
if (typeof stamp !== "number" || !Number.isFinite(stamp) || stamp <= 0) return this.drop(deviceId, capName, "cold-slice");
|
|
71444
71697
|
const key = `${deviceId}:${capName}`;
|
|
71445
71698
|
const prior = this.lastWords.get(key);
|
|
71446
71699
|
this.lastWords.set(key, raw);
|
|
71447
71700
|
if (prior === void 0) return this.firstWordSighting(deviceId, capName, slice, raw);
|
|
71448
71701
|
if (prior === raw) return this.drop(deviceId, capName, "no-change");
|
|
71702
|
+
if (stamp < this.startedAtMs) return this.drop(deviceId, capName, "baseline-seeded-stale-active");
|
|
71703
|
+
return { changed: true };
|
|
71704
|
+
}
|
|
71705
|
+
/** A word-level cap with no change stamp: only a DIFFERENT word is a transition. */
|
|
71706
|
+
observeUnstampedWord(deviceId, capName, slice, field) {
|
|
71707
|
+
const raw = slice[field];
|
|
71708
|
+
if (typeof raw !== "string" || raw.length === 0) return this.drop(deviceId, capName, "non-word-value");
|
|
71709
|
+
const key = `${deviceId}:${capName}`;
|
|
71710
|
+
const prior = this.lastWords.get(key);
|
|
71711
|
+
this.lastWords.set(key, raw);
|
|
71712
|
+
if (prior === void 0) return this.drop(deviceId, capName, "baseline-seeded-inactive");
|
|
71713
|
+
if (prior === raw) return this.drop(deviceId, capName, "no-change");
|
|
71449
71714
|
return { changed: true };
|
|
71450
71715
|
}
|
|
71451
71716
|
/**
|
|
@@ -72590,7 +72855,7 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
72590
72855
|
type: "slider",
|
|
72591
72856
|
key: "packageDropActivityWindowSec",
|
|
72592
72857
|
label: "Deliverer window",
|
|
72593
|
-
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.",
|
|
72858
|
+
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.",
|
|
72594
72859
|
min: 0,
|
|
72595
72860
|
max: 1800,
|
|
72596
72861
|
step: 30,
|
|
@@ -81521,11 +81786,11 @@ function unique(values) {
|
|
|
81521
81786
|
* therefore keeps the alarm's own sentence, which is also what every row
|
|
81522
81787
|
* enqueued before this deploy carries.
|
|
81523
81788
|
*/
|
|
81524
|
-
function combinedAlarmText(kind, source) {
|
|
81789
|
+
function combinedAlarmText(kind, source, followers = []) {
|
|
81525
81790
|
if (kind !== "alarm-triggered" || source === void 0) return null;
|
|
81526
81791
|
return {
|
|
81527
81792
|
titleKey: source.className !== void 0 ? "alarm.combined.title" : "system.alarm-triggered.title",
|
|
81528
|
-
bodyKey: "alarm.intrusion.body"
|
|
81793
|
+
bodyKey: [source, ...followers].some((ref) => ref.sensorChange !== void 0) ? "alarm.intrusion.body.withChanges" : "alarm.intrusion.body"
|
|
81529
81794
|
};
|
|
81530
81795
|
}
|
|
81531
81796
|
/**
|
|
@@ -81540,7 +81805,7 @@ function combinedAlarmText(kind, source) {
|
|
|
81540
81805
|
*/
|
|
81541
81806
|
function alarmTextKeys(transition, body) {
|
|
81542
81807
|
const { kind, source } = transition;
|
|
81543
|
-
const combined = combinedAlarmText(kind, source);
|
|
81808
|
+
const combined = combinedAlarmText(kind, source, transition.followers);
|
|
81544
81809
|
if (combined !== null) return combined;
|
|
81545
81810
|
const baseTitle = transition.mode !== void 0 ? `system.${kind}.title.${transition.mode}` : `system.${kind}.title`;
|
|
81546
81811
|
const titleKey = body?.titleKeySuffix !== void 0 ? `${baseTitle}.${body.titleKeySuffix}` : baseTitle;
|
|
@@ -87110,6 +87375,13 @@ var NC_STILL_WAIT_MAX_AGE_MS = 3e4;
|
|
|
87110
87375
|
function canStillGainMedia(entry) {
|
|
87111
87376
|
return entry.recordKind === "object-event";
|
|
87112
87377
|
}
|
|
87378
|
+
/** The variables that carry a finished cycle's run summary. */
|
|
87379
|
+
var CYCLE_SUMMARY_VARS = [
|
|
87380
|
+
"duration",
|
|
87381
|
+
"energy",
|
|
87382
|
+
"afterDuration",
|
|
87383
|
+
"usedEnergy"
|
|
87384
|
+
];
|
|
87113
87385
|
var NcDispatcher = class {
|
|
87114
87386
|
deps;
|
|
87115
87387
|
targetCache = null;
|
|
@@ -88013,6 +88285,7 @@ var NcDispatcher = class {
|
|
|
88013
88285
|
}
|
|
88014
88286
|
/** Rules already warned about a deprecated variable in this process — once each. */
|
|
88015
88287
|
deprecatedVarWarned = /* @__PURE__ */ new Set();
|
|
88288
|
+
cycleSummaryWarned = /* @__PURE__ */ new Set();
|
|
88016
88289
|
/**
|
|
88017
88290
|
* A rule template written by an older client may still say `{{camera}}`. It
|
|
88018
88291
|
* renders (the alias), and the rule is named once so the operator can edit it.
|
|
@@ -88032,6 +88305,27 @@ var NcDispatcher = class {
|
|
|
88032
88305
|
});
|
|
88033
88306
|
}
|
|
88034
88307
|
/**
|
|
88308
|
+
* A FINISHED-cycle row whose own template places none of the four cycle
|
|
88309
|
+
* variables delivers no run summary (D391: accepted, produced less). Said
|
|
88310
|
+
* once per rule; the operator's text is never touched.
|
|
88311
|
+
*/
|
|
88312
|
+
warnCycleSummaryDropped(entry) {
|
|
88313
|
+
const deviceId = entry.payload.subject.applianceCycle?.deviceId;
|
|
88314
|
+
const template = entry.payload.template;
|
|
88315
|
+
if (deviceId === void 0 || template === void 0) return;
|
|
88316
|
+
if (this.cycleSummaryWarned.has(entry.ruleId)) return;
|
|
88317
|
+
const used = new Set([...templatePlaceholders(template.title ?? ""), ...templatePlaceholders(template.body ?? "")]);
|
|
88318
|
+
if (CYCLE_SUMMARY_VARS.some((name) => used.has(name))) return;
|
|
88319
|
+
this.cycleSummaryWarned.add(entry.ruleId);
|
|
88320
|
+
this.deps.logger.warn("appliance cycle finished but the rule template places no run summary — the message carries no duration or energy", {
|
|
88321
|
+
tags: {
|
|
88322
|
+
deviceId,
|
|
88323
|
+
ruleId: entry.ruleId
|
|
88324
|
+
},
|
|
88325
|
+
meta: { placeholders: [...CYCLE_SUMMARY_VARS] }
|
|
88326
|
+
});
|
|
88327
|
+
}
|
|
88328
|
+
/**
|
|
88035
88329
|
* The facts of a finished appliance cycle; unknown on every other row and on
|
|
88036
88330
|
* any failure (a part that cannot be read is omitted, never a blocked send).
|
|
88037
88331
|
*/
|
|
@@ -88076,6 +88370,7 @@ var NcDispatcher = class {
|
|
|
88076
88370
|
const cycleFacts = await this.applianceCycleFacts(entry);
|
|
88077
88371
|
const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
|
|
88078
88372
|
this.warnDeprecatedVars(entry);
|
|
88373
|
+
this.warnCycleSummaryDropped(entry);
|
|
88079
88374
|
const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
|
|
88080
88375
|
const title = renderRowTitle(this.texts, entry, vars);
|
|
88081
88376
|
const authoredBody = entry.payload.template?.body;
|
|
@@ -89334,10 +89629,31 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone, cycle
|
|
|
89334
89629
|
latestVersion: typeof systemEvent?.data["latestVersion"] === "string" ? systemEvent.data["latestVersion"] : "",
|
|
89335
89630
|
...systemEvent?.deviceId !== void 0 ? { deviceName: String(systemEvent.deviceId) } : {},
|
|
89336
89631
|
...systemEvent?.nodeId !== void 0 && (systemEvent.kind === "node-online" || systemEvent.kind === "node-offline") ? { nodeName: systemEvent.nodeId } : {},
|
|
89632
|
+
...systemEvent?.alarmSource !== void 0 ? { changes: alarmChangesText(texts, systemEvent.alarmSource, systemEvent.alarmFollowers) } : {},
|
|
89337
89633
|
...systemEventParamsForTemplate(systemEvent?.textParams)
|
|
89338
89634
|
};
|
|
89339
89635
|
}
|
|
89340
89636
|
/**
|
|
89637
|
+
* `{{changes}}` of the combined alarm body: what each sensor of the incident did, in
|
|
89638
|
+
* arrival order and one phrase per device — "Laundry lock unlocked". The phrase is a
|
|
89639
|
+
* catalog key per kind and state (`alarm.change.<kind>.<state>`), then per state
|
|
89640
|
+
* (`alarm.change.<state>`), then the bare name: a state nobody worded never drops the device.
|
|
89641
|
+
*/
|
|
89642
|
+
function alarmChangesText(texts, source, followers) {
|
|
89643
|
+
const phrases = [];
|
|
89644
|
+
for (const ref of [source, ...followers ?? []]) {
|
|
89645
|
+
const change = ref.sensorChange;
|
|
89646
|
+
if (change === void 0) continue;
|
|
89647
|
+
const key = [`alarm.change.${change.kind}.${change.state}`, `alarm.change.${change.state}`].find((candidate) => texts.has(candidate));
|
|
89648
|
+
const phrase = key === void 0 ? change.name : texts.text({
|
|
89649
|
+
key,
|
|
89650
|
+
vars: { device: change.name }
|
|
89651
|
+
});
|
|
89652
|
+
if (!phrases.includes(phrase)) phrases.push(phrase);
|
|
89653
|
+
}
|
|
89654
|
+
return phrases.join(", ");
|
|
89655
|
+
}
|
|
89656
|
+
/**
|
|
89341
89657
|
* Which catalog key supplies the DEFAULT body for a row.
|
|
89342
89658
|
*
|
|
89343
89659
|
* A system event names its own at intake (the variant selection — profiled or
|
|
@@ -102058,7 +102374,10 @@ function buildProvider(host) {
|
|
|
102058
102374
|
catalog: [...NC_CONDITION_CATALOG],
|
|
102059
102375
|
taxonomy: NC_TAXONOMY
|
|
102060
102376
|
}),
|
|
102061
|
-
getTemplateCatalog: async () => ({
|
|
102377
|
+
getTemplateCatalog: async () => ({
|
|
102378
|
+
vars: servedTemplateVars(),
|
|
102379
|
+
defaults: servedTemplateDefaults(host.texts)
|
|
102380
|
+
}),
|
|
102062
102381
|
previewTemplate: async (input) => previewTemplate(host.texts, input),
|
|
102063
102382
|
getHistory: async ({ filter }) => {
|
|
102064
102383
|
const entries = await host.outbox.queryHistory({
|
|
@@ -105620,7 +105939,7 @@ function groupedAlarmPhaseKey(ruleId, trackId) {
|
|
|
105620
105939
|
* combined alarm notification would then arrive with no picture, which is the
|
|
105621
105940
|
* one thing it exists to fix.
|
|
105622
105941
|
*/
|
|
105623
|
-
function fireRefFor(rule, subject, kind) {
|
|
105942
|
+
function fireRefFor(rule, subject, kind, sensorName) {
|
|
105624
105943
|
const hasEventMedia = kind === "object-event" || kind === "package-event";
|
|
105625
105944
|
const className = subject.classNames[0];
|
|
105626
105945
|
const linkedOwners = subject.trackId === void 0 && subject.linkedStills !== void 0 ? linkedMediaOwners(subject.linkedStills, linkedCameraMode(rule.media)) : void 0;
|
|
@@ -105630,6 +105949,11 @@ function fireRefFor(rule, subject, kind) {
|
|
|
105630
105949
|
ruleName: rule.name,
|
|
105631
105950
|
deviceId: subject.deviceId,
|
|
105632
105951
|
...subject.sourceDeviceId !== void 0 ? { sourceDeviceId: subject.sourceDeviceId } : {},
|
|
105952
|
+
...subject.sensorKind !== void 0 && subject.sensorState !== void 0 && sensorName !== void 0 ? { sensorChange: {
|
|
105953
|
+
name: sensorName,
|
|
105954
|
+
kind: subject.sensorKind,
|
|
105955
|
+
state: subject.sensorState
|
|
105956
|
+
} } : {},
|
|
105633
105957
|
recordId: subject.recordId,
|
|
105634
105958
|
...trackId !== void 0 ? { trackId } : {},
|
|
105635
105959
|
...linkedOwners !== void 0 && linkedOwners.extraStills.length > 0 ? { extraStills: linkedOwners.extraStills.map((x) => ({ ...x })) } : {},
|
|
@@ -105641,6 +105965,12 @@ function fireRefFor(rule, subject, kind) {
|
|
|
105641
105965
|
...subject.confidence !== void 0 ? { confidence: subject.confidence } : {}
|
|
105642
105966
|
};
|
|
105643
105967
|
}
|
|
105968
|
+
/** The directory name of the sensor a match was about; absent for a camera subject. */
|
|
105969
|
+
function sensorNameOf(host, subject) {
|
|
105970
|
+
if (subject.source !== "sensor") return void 0;
|
|
105971
|
+
const name = host.deviceDirectory.get(subject.sourceDeviceId ?? subject.deviceId)?.name.trim();
|
|
105972
|
+
return name !== void 0 && name.length > 0 ? name : void 0;
|
|
105973
|
+
}
|
|
105644
105974
|
/**
|
|
105645
105975
|
* Run this rule's onTrigger / onTriggerOff bindings for this match, and —
|
|
105646
105976
|
* when the rule is gated on `armed_*` — auto-trigger the CamStack
|
|
@@ -105662,7 +105992,7 @@ function runRuleActions(host, rule, subject, kind, controls) {
|
|
|
105662
105992
|
deviceId: subject.deviceId,
|
|
105663
105993
|
bindings,
|
|
105664
105994
|
lookup: (id) => host.sequences.get(id),
|
|
105665
|
-
origin: fireRefFor(rule, subject, kind)
|
|
105995
|
+
origin: fireRefFor(rule, subject, kind, sensorNameOf(host, subject))
|
|
105666
105996
|
});
|
|
105667
105997
|
return phase;
|
|
105668
105998
|
}
|
|
@@ -105686,7 +106016,7 @@ function autoTriggerAlarmPanel(host, rule, subject, kind) {
|
|
|
105686
106016
|
});
|
|
105687
106017
|
return NO_ALARM_PHASE;
|
|
105688
106018
|
}
|
|
105689
|
-
const origin = fireRefFor(rule, subject, kind);
|
|
106019
|
+
const origin = fireRefFor(rule, subject, kind, sensorNameOf(host, subject));
|
|
105690
106020
|
const phase = panel.classifyRuleMatch(origin);
|
|
105691
106021
|
try {
|
|
105692
106022
|
panel.triggerFromRule(origin);
|
|
@@ -124995,6 +125325,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
124995
125325
|
this.packageDropDetector?.forgetDevice(data.deviceId);
|
|
124996
125326
|
this.packageScene?.forget(data.deviceId);
|
|
124997
125327
|
this.frameState.lastNonPackageActivityAt.delete(data.deviceId);
|
|
125328
|
+
this.frameState.lastPackageZoneMotionAt.delete(data.deviceId);
|
|
124998
125329
|
this.frameState.packageCollectors.forgetDevice(data.deviceId);
|
|
124999
125330
|
this.frameState.packageOversizeLogAt.delete(data.deviceId);
|
|
125000
125331
|
this.frameState.packageLookFrames.delete(data.deviceId);
|
|
@@ -125019,6 +125350,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
125019
125350
|
this.packageDropDetector?.forgetDevice(deviceId);
|
|
125020
125351
|
this.packageScene?.forget(deviceId);
|
|
125021
125352
|
this.frameState.lastNonPackageActivityAt.delete(deviceId);
|
|
125353
|
+
this.frameState.lastPackageZoneMotionAt.delete(deviceId);
|
|
125022
125354
|
this.frameState.packageCollectors.forgetDevice(deviceId);
|
|
125023
125355
|
this.frameState.packageOversizeLogAt.delete(deviceId);
|
|
125024
125356
|
this.frameState.packageLookFrames.delete(deviceId);
|
|
@@ -127591,6 +127923,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127591
127923
|
detected,
|
|
127592
127924
|
atMs: timestamp
|
|
127593
127925
|
});
|
|
127926
|
+
if (detected) await this.stampPackageZoneMotion(deviceId, payload, timestamp);
|
|
127594
127927
|
if (detected) this.eventStore?.noteMotionSample(deviceId, payload.regionCount, timestamp);
|
|
127595
127928
|
await this.motionEpisodes?.observe(deviceId, "analyzer", detected, timestamp, {
|
|
127596
127929
|
regionCount: payload.regionCount,
|
|
@@ -127609,6 +127942,28 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127609
127942
|
});
|
|
127610
127943
|
}
|
|
127611
127944
|
/**
|
|
127945
|
+
* Stamp analyzer motion that reached a package drop-off zone (D1003): the
|
|
127946
|
+
* courier who never became an L1 track still backs the OPEN of a candidate.
|
|
127947
|
+
* Onboard motion carries no regions or dimensions and never stamps.
|
|
127948
|
+
*/
|
|
127949
|
+
async stampPackageZoneMotion(deviceId, payload, timestamp) {
|
|
127950
|
+
const proxy = await this.ensureProxy(deviceId);
|
|
127951
|
+
const packageZoneIds = collectPackageZoneIds(proxy?.state.zoneRules.value?.package ?? []);
|
|
127952
|
+
if (packageZoneIds.size === 0) return;
|
|
127953
|
+
const zoneRect = packageZoneCropRect({
|
|
127954
|
+
zones: proxy?.state.zones.value?.zones ?? [],
|
|
127955
|
+
packageZoneIds,
|
|
127956
|
+
frameWidth: payload.frameWidth,
|
|
127957
|
+
frameHeight: payload.frameHeight
|
|
127958
|
+
});
|
|
127959
|
+
if (delivererMotionNearPackageZone({
|
|
127960
|
+
regions: payload.regions,
|
|
127961
|
+
frameWidth: payload.frameWidth,
|
|
127962
|
+
frameHeight: payload.frameHeight,
|
|
127963
|
+
zoneRect
|
|
127964
|
+
})) this.frameState.lastPackageZoneMotionAt.set(deviceId, timestamp);
|
|
127965
|
+
}
|
|
127966
|
+
/**
|
|
127612
127967
|
* Persist firmware-driven (onboard / device-activity) motion events into
|
|
127613
127968
|
* the SAME episode tracker as {@link handleMotionAnalysis}, keyed per
|
|
127614
127969
|
* `(deviceId, source)` so the two paths can never share — or fight over —
|
|
@@ -127941,6 +128296,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127941
128296
|
encodeJpeg: request.encodeJpeg
|
|
127942
128297
|
}, request.pin);
|
|
127943
128298
|
}
|
|
128299
|
+
zoneMotionStamp(deviceId) {
|
|
128300
|
+
const at = this.frameState.lastPackageZoneMotionAt.get(deviceId);
|
|
128301
|
+
return at === void 0 ? {} : { lastDelivererMotionAt: at };
|
|
128302
|
+
}
|
|
127944
128303
|
deliverPackageSceneSightings(delivery) {
|
|
127945
128304
|
const settings = this.packageDropSettingsFromCache(delivery.deviceId);
|
|
127946
128305
|
const lastActivityAt = this.frameState.lastNonPackageActivityAt.get(delivery.deviceId);
|
|
@@ -127954,6 +128313,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127954
128313
|
minDwellMs: settings.packageDropDwellSec * 1e3,
|
|
127955
128314
|
activityWindowMs: settings.packageDropActivityWindowSec * 1e3,
|
|
127956
128315
|
...lastActivityAt !== void 0 ? { lastActivityAt } : {},
|
|
128316
|
+
...this.zoneMotionStamp(delivery.deviceId),
|
|
127957
128317
|
looked: false
|
|
127958
128318
|
});
|
|
127959
128319
|
this.bridgePackageStillness({
|
|
@@ -128139,6 +128499,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128139
128499
|
minDwellMs: input.settings.packageDropDwellSec * 1e3,
|
|
128140
128500
|
activityWindowMs: input.settings.packageDropActivityWindowSec * 1e3,
|
|
128141
128501
|
...lastActivityAt !== void 0 ? { lastActivityAt } : {},
|
|
128502
|
+
...this.zoneMotionStamp(input.deviceId),
|
|
128142
128503
|
...lookEvidence
|
|
128143
128504
|
});
|
|
128144
128505
|
this.bridgePackageStillness({
|
|
@@ -128334,6 +128695,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128334
128695
|
});
|
|
128335
128696
|
}).catch(() => void 0);
|
|
128336
128697
|
}
|
|
128698
|
+
for (const c of outcome.continued) this.ctx.logger.info("package candidate continued by a sub-floor box", {
|
|
128699
|
+
tags,
|
|
128700
|
+
meta: {
|
|
128701
|
+
entryId: c.id,
|
|
128702
|
+
hits: c.hits,
|
|
128703
|
+
score: c.score,
|
|
128704
|
+
via: input.via
|
|
128705
|
+
}
|
|
128706
|
+
});
|
|
128337
128707
|
for (const u of outcome.earlyEvidenceUnbacked) this.ctx.logger.info("early pick-up evidence complete — nobody around, left to the TTL", {
|
|
128338
128708
|
tags,
|
|
128339
128709
|
meta: {
|
|
@@ -128361,6 +128731,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128361
128731
|
observedDwellMs,
|
|
128362
128732
|
requiredDwellMs,
|
|
128363
128733
|
activityBackedAtOpen: ex.activityBackedAtOpen,
|
|
128734
|
+
lastZoneMotionAt: this.frameState.lastPackageZoneMotionAt.get(input.deviceId) ?? null,
|
|
128364
128735
|
reasons
|
|
128365
128736
|
}
|
|
128366
128737
|
});
|
|
@@ -129134,6 +129505,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
129134
129505
|
}
|
|
129135
129506
|
const verdict = this.sensorTransitions.observe(data.deviceId, data.capName, { ...slice });
|
|
129136
129507
|
this.flushSensorTransitionDrops();
|
|
129508
|
+
if (verdict.changed) {
|
|
129509
|
+
const entries = Object.entries(slice);
|
|
129510
|
+
const field = (key) => entries.find(([k]) => k === key)?.[1];
|
|
129511
|
+
this.ctx.logger.info("sensor slice passed the transition gate", {
|
|
129512
|
+
tags: { deviceId: data.deviceId },
|
|
129513
|
+
meta: {
|
|
129514
|
+
capName: data.capName,
|
|
129515
|
+
state: field("state") ?? field("value") ?? field("entryOpen") ?? field("active") ?? null,
|
|
129516
|
+
lastChangedAt: field("lastChangedAt") ?? null,
|
|
129517
|
+
verdict: "changed"
|
|
129518
|
+
}
|
|
129519
|
+
});
|
|
129520
|
+
}
|
|
129137
129521
|
return verdict.changed;
|
|
129138
129522
|
}
|
|
129139
129523
|
/**
|