@camstack/addon-post-analysis 1.2.665 → 1.2.667
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 +21 -0
- package/dist/{clip-model-registry-nn_STiGt.js → clip-model-registry-D7cqhFK3.js} +758 -486
- package/dist/{clip-model-registry-DZTzG2p4.mjs → clip-model-registry-DpzRS8q3.mjs} +758 -486
- 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-Hy74pMMo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DmTIEp0m.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DAAkZRnK.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-znrgUrN1.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-BAYLsfoZ.mjs → hostInit-ODebWXu_.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +563 -122
- package/dist/pipeline-analytics/index.mjs +563 -122
- 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-BXTAZYaJ.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-Br6eWzp_.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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-DpzRS8q3.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",
|
|
@@ -59623,6 +59659,140 @@ async function promoteWithStill(input) {
|
|
|
59623
59659
|
});
|
|
59624
59660
|
}
|
|
59625
59661
|
//#endregion
|
|
59662
|
+
//#region src/pipeline-analytics/pipeline/key-frame-persist.ts
|
|
59663
|
+
/**
|
|
59664
|
+
* A box is written only when it can be USED — a degenerate rectangle, or one
|
|
59665
|
+
* measured on a degenerate frame, says nothing and would read back as a box at
|
|
59666
|
+
* the origin. Absent is the honest answer; the reader then pairs a snapshot,
|
|
59667
|
+
* exactly as it did before D510.
|
|
59668
|
+
*
|
|
59669
|
+
* Its result is spread into BOTH writes: the small companion is derived from
|
|
59670
|
+
* the very bytes of the native one, so it is the same scene at the same
|
|
59671
|
+
* instant and the box maps into it by ratio like any other scale of the frame.
|
|
59672
|
+
* A box on one row and not the other would make the summary's answer depend on
|
|
59673
|
+
* which row a reader happened to pick.
|
|
59674
|
+
*/
|
|
59675
|
+
function isStatableSubject(subject) {
|
|
59676
|
+
if (subject === void 0) return false;
|
|
59677
|
+
return subject.w > 0 && subject.h > 0 && subject.frameWidth > 0 && subject.frameHeight > 0;
|
|
59678
|
+
}
|
|
59679
|
+
/**
|
|
59680
|
+
* Read, derive and store one track's key frame + its web companion.
|
|
59681
|
+
*
|
|
59682
|
+
* PARCEL-OR-NOTHING: no parcel writes nothing and the caller's bounded retry
|
|
59683
|
+
* parks a later frame. The `keyFrame` write lands FIRST and its media id (D482)
|
|
59684
|
+
* is what the outcome carries — a `keyFrameSmall` failure afterwards must not
|
|
59685
|
+
* re-arm a retry that would re-shoot a key frame the rebuild can already read.
|
|
59686
|
+
*/
|
|
59687
|
+
async function persistTrackKeyFrame(deps, input) {
|
|
59688
|
+
const { deviceId, trackId, retry } = input;
|
|
59689
|
+
const drop = {
|
|
59690
|
+
tags: { deviceId },
|
|
59691
|
+
meta: {
|
|
59692
|
+
trackId,
|
|
59693
|
+
retry
|
|
59694
|
+
}
|
|
59695
|
+
};
|
|
59696
|
+
try {
|
|
59697
|
+
const parcel = await deps.fetchParkedKeyFrame(deviceId, trackId);
|
|
59698
|
+
if (parcel === null || parcel.jpeg.byteLength === 0) {
|
|
59699
|
+
deps.logger.error("key-frame parcel missing — nothing to write, retry parks a later frame", { ...drop });
|
|
59700
|
+
return { kind: "parcel-missing" };
|
|
59701
|
+
}
|
|
59702
|
+
const subjectBox = isStatableSubject(parcel.subject) ? { subjectBox: parcel.subject } : {};
|
|
59703
|
+
const keyFrameMediaId = await deps.media.putReplacing({
|
|
59704
|
+
deviceId,
|
|
59705
|
+
ownerKind: "track",
|
|
59706
|
+
ownerId: trackId,
|
|
59707
|
+
kind: "keyFrame",
|
|
59708
|
+
timestamp: parcel.timestamp,
|
|
59709
|
+
data: parcel.jpeg,
|
|
59710
|
+
...subjectBox
|
|
59711
|
+
});
|
|
59712
|
+
try {
|
|
59713
|
+
const keyFrameSmall = await deps.deriveSmall(parcel.jpeg);
|
|
59714
|
+
await deps.media.putReplacing({
|
|
59715
|
+
deviceId,
|
|
59716
|
+
ownerKind: "track",
|
|
59717
|
+
ownerId: trackId,
|
|
59718
|
+
kind: "keyFrameSmall",
|
|
59719
|
+
timestamp: parcel.timestamp,
|
|
59720
|
+
data: keyFrameSmall,
|
|
59721
|
+
...subjectBox
|
|
59722
|
+
});
|
|
59723
|
+
} catch (err) {
|
|
59724
|
+
deps.logger.warn("keyFrameSmall write failed — the native keyFrame is stored", {
|
|
59725
|
+
...drop,
|
|
59726
|
+
meta: {
|
|
59727
|
+
...drop.meta,
|
|
59728
|
+
nodeId: parcel.nodeId,
|
|
59729
|
+
error: errMsg(err)
|
|
59730
|
+
}
|
|
59731
|
+
});
|
|
59732
|
+
}
|
|
59733
|
+
deps.logger.debug("key frame written from the parked parcel", {
|
|
59734
|
+
...drop,
|
|
59735
|
+
meta: {
|
|
59736
|
+
...drop.meta,
|
|
59737
|
+
nodeId: parcel.nodeId,
|
|
59738
|
+
width: parcel.width,
|
|
59739
|
+
height: parcel.height,
|
|
59740
|
+
bytes: parcel.jpeg.byteLength,
|
|
59741
|
+
parkedTimestamp: parcel.timestamp
|
|
59742
|
+
}
|
|
59743
|
+
});
|
|
59744
|
+
return {
|
|
59745
|
+
kind: "landed",
|
|
59746
|
+
keyFrameMediaId
|
|
59747
|
+
};
|
|
59748
|
+
} catch (err) {
|
|
59749
|
+
const error = errMsg(err);
|
|
59750
|
+
deps.logger.warn("key-frame write failed", {
|
|
59751
|
+
...drop,
|
|
59752
|
+
meta: {
|
|
59753
|
+
...drop.meta,
|
|
59754
|
+
error
|
|
59755
|
+
}
|
|
59756
|
+
});
|
|
59757
|
+
return {
|
|
59758
|
+
kind: "error",
|
|
59759
|
+
error
|
|
59760
|
+
};
|
|
59761
|
+
}
|
|
59762
|
+
}
|
|
59763
|
+
/**
|
|
59764
|
+
* Write one parked frame under an OBJECT event (`object:<id>`), once.
|
|
59765
|
+
*
|
|
59766
|
+
* A package delivery has no track row owning a key frame, and the notification
|
|
59767
|
+
* ladder reads only `track:` and `object:` owners — so without this the
|
|
59768
|
+
* delivery ships text-only. `null` = no parcel to write (said at the caller).
|
|
59769
|
+
* `putReplacing` makes a repeat the same row, never a second one.
|
|
59770
|
+
*/
|
|
59771
|
+
async function persistObjectFrame(deps, input) {
|
|
59772
|
+
const { deviceId, trackId, objectId, timestamp } = input;
|
|
59773
|
+
const parcel = await deps.fetchParkedKeyFrame(deviceId, trackId);
|
|
59774
|
+
if (parcel === null || parcel.jpeg.byteLength === 0) {
|
|
59775
|
+
deps.logger.warn("object frame parcel missing — nothing to write", {
|
|
59776
|
+
tags: { deviceId },
|
|
59777
|
+
meta: {
|
|
59778
|
+
trackId,
|
|
59779
|
+
objectId
|
|
59780
|
+
}
|
|
59781
|
+
});
|
|
59782
|
+
return null;
|
|
59783
|
+
}
|
|
59784
|
+
const subjectBox = isStatableSubject(parcel.subject) ? { subjectBox: parcel.subject } : {};
|
|
59785
|
+
return deps.media.putReplacing({
|
|
59786
|
+
deviceId,
|
|
59787
|
+
ownerKind: "object",
|
|
59788
|
+
ownerId: objectId,
|
|
59789
|
+
kind: "keyFrame",
|
|
59790
|
+
timestamp,
|
|
59791
|
+
data: parcel.jpeg,
|
|
59792
|
+
...subjectBox
|
|
59793
|
+
});
|
|
59794
|
+
}
|
|
59795
|
+
//#endregion
|
|
59626
59796
|
//#region src/pipeline-analytics/pipeline/person-over-vehicle.ts
|
|
59627
59797
|
/**
|
|
59628
59798
|
* How much of the PERSON box must sit inside a vehicle box to be counted.
|
|
@@ -71030,6 +71200,12 @@ Object.freeze({
|
|
|
71030
71200
|
"idleStopMinutes": 30
|
|
71031
71201
|
} } }
|
|
71032
71202
|
},
|
|
71203
|
+
"media-player": {
|
|
71204
|
+
"channelDown": { "steps": 1 },
|
|
71205
|
+
"channelUp": { "steps": 1 },
|
|
71206
|
+
"volumeDown": { "steps": 1 },
|
|
71207
|
+
"volumeUp": { "steps": 1 }
|
|
71208
|
+
},
|
|
71033
71209
|
"mqtt-broker": { "startEmbeddedBroker": {
|
|
71034
71210
|
"port": 1883,
|
|
71035
71211
|
"allowAnonymous": false
|
|
@@ -71396,8 +71572,8 @@ Object.freeze({
|
|
|
71396
71572
|
* ## Caps with no boolean level pass through untouched
|
|
71397
71573
|
*
|
|
71398
71574
|
* `EVENT_KIND_BY_CAP` covers sixteen caps; `SOURCE_CAP_ACTIVE_FIELD` covers the
|
|
71399
|
-
* ten that have a boolean whose edge means something. `button`, `presence
|
|
71400
|
-
*
|
|
71575
|
+
* ten that have a boolean whose edge means something. `button`, `presence`
|
|
71576
|
+
* and `event-emitter` are NOT levels (`enum-sensor` is a stampless word level, see `UNSTAMPED_WORD_FIELD`) — their ARRIVAL is the
|
|
71401
71577
|
* event, and three presses in a row are three markers. Gating them would
|
|
71402
71578
|
* re-ship the failure of 2026-08-08, when camera 101's press produced a row, a
|
|
71403
71579
|
* ring, a matched rule and no marker. (`doorbell` is not a level either, but
|
|
@@ -71523,6 +71699,24 @@ var WORD_LEVEL_FIELD = {
|
|
|
71523
71699
|
* running for an hour at boot did not just start).
|
|
71524
71700
|
*/
|
|
71525
71701
|
var BOOLEAN_LEVEL_FIELD = { "running-state": "running" };
|
|
71702
|
+
/**
|
|
71703
|
+
* The device-history series key of a level cap's boolean — `cap:<cap>.<field>`,
|
|
71704
|
+
* the shape of `capSeriesKey` (system builtin, not importable from an addon;
|
|
71705
|
+
* the wiring spec pins the literal). `null` for a cap with no boolean level.
|
|
71706
|
+
*/
|
|
71707
|
+
function levelSeriesKey(capName) {
|
|
71708
|
+
const field = BOOLEAN_LEVEL_FIELD[capName];
|
|
71709
|
+
return field === void 0 ? null : `cap:${capName}.${field}`;
|
|
71710
|
+
}
|
|
71711
|
+
/**
|
|
71712
|
+
* Caps whose level is a WORD with NO change stamp: cap → field. `enum-sensor`
|
|
71713
|
+
* carries `{ value, lastFetchedAt }`, and `lastFetchedAt` moves on every
|
|
71714
|
+
* provider refresh, so it says nothing about when the word changed. Gated by
|
|
71715
|
+
* the word alone: the first sighting seeds, only a different word passes, and
|
|
71716
|
+
* a null / missing value is dropped and never remembered (planning#396: such
|
|
71717
|
+
* slices passed as `changed` many times a minute and could match an NC rule).
|
|
71718
|
+
*/
|
|
71719
|
+
var UNSTAMPED_WORD_FIELD = { "enum-sensor": "value" };
|
|
71526
71720
|
/** The word a word-level cap rests in — its `false`, for the first-sighting rule. */
|
|
71527
71721
|
var WORD_LEVEL_INACTIVE = {
|
|
71528
71722
|
cover: "closed",
|
|
@@ -71541,6 +71735,7 @@ var WORD_LEVEL_INACTIVE = {
|
|
|
71541
71735
|
* post-analysis runner, D28).
|
|
71542
71736
|
*/
|
|
71543
71737
|
var ADVANCE_FIELD = CAP_ADVANCE_FIELDS;
|
|
71738
|
+
var DEFAULT_SEED_WAIT_MS = 2e3;
|
|
71544
71739
|
/** Default drop-summary window: one line per device per ten minutes, at most. */
|
|
71545
71740
|
var DEFAULT_DROP_SUMMARY_WINDOW_MS = 6e5;
|
|
71546
71741
|
var SensorTransitionGate = class {
|
|
@@ -71552,6 +71747,14 @@ var SensorTransitionGate = class {
|
|
|
71552
71747
|
lastStamps = /* @__PURE__ */ new Map();
|
|
71553
71748
|
/** Drops since the window opened, keyed by `${deviceId}:${capName}:${reason}`. */
|
|
71554
71749
|
drops = /* @__PURE__ */ new Map();
|
|
71750
|
+
/** Seed reads started, by key; a settled one stays so a seed is read once. */
|
|
71751
|
+
seeds = /* @__PURE__ */ new Map();
|
|
71752
|
+
seedsSettled = /* @__PURE__ */ new Set();
|
|
71753
|
+
/** Seeds whose wait ran out: decided as unseeded from then on. */
|
|
71754
|
+
seedsAbandoned = /* @__PURE__ */ new Set();
|
|
71755
|
+
readSeed;
|
|
71756
|
+
onSeedFailed;
|
|
71757
|
+
seedWaitMs;
|
|
71555
71758
|
windowStartedAt;
|
|
71556
71759
|
startedAtMs;
|
|
71557
71760
|
now;
|
|
@@ -71563,6 +71766,55 @@ var SensorTransitionGate = class {
|
|
|
71563
71766
|
this.freshnessMs = options.firstSightingFreshnessMs ?? DEFAULT_FIRST_SIGHTING_FRESHNESS_MS;
|
|
71564
71767
|
this.summaryWindowMs = options.summaryWindowMs ?? DEFAULT_DROP_SUMMARY_WINDOW_MS;
|
|
71565
71768
|
this.windowStartedAt = options.now();
|
|
71769
|
+
this.readSeed = options.readSeed;
|
|
71770
|
+
this.onSeedFailed = options.onSeedFailed;
|
|
71771
|
+
this.seedWaitMs = options.seedWaitMs ?? DEFAULT_SEED_WAIT_MS;
|
|
71772
|
+
}
|
|
71773
|
+
/**
|
|
71774
|
+
* {@link observe}, plus: a level cap's first boolean `false` whose persisted
|
|
71775
|
+
* prior is still being read waits for that read, bounded by `seedWaitMs`, and
|
|
71776
|
+
* is decided again. This is the only wait on the path, and it is on a read
|
|
71777
|
+
* that was started at the FIRST sighting of the device (a null at boot, long
|
|
71778
|
+
* before the boolean) — never one begun by the event itself unless the
|
|
71779
|
+
* boolean really is the first thing seen. A read that fails or times out
|
|
71780
|
+
* decides as unseeded, i.e. exactly as before the seed existed (D49).
|
|
71781
|
+
*/
|
|
71782
|
+
async observeSettled(deviceId, capName, slice) {
|
|
71783
|
+
const first = this.observe(deviceId, capName, slice);
|
|
71784
|
+
if (first.changed || first.reason !== "seed-pending") return first;
|
|
71785
|
+
const key = `${deviceId}:${capName}`;
|
|
71786
|
+
const pending = this.seeds.get(key);
|
|
71787
|
+
if (pending !== void 0) {
|
|
71788
|
+
let timer;
|
|
71789
|
+
const timeout = new Promise((resolve) => {
|
|
71790
|
+
timer = setTimeout(resolve, this.seedWaitMs);
|
|
71791
|
+
});
|
|
71792
|
+
await Promise.race([pending, timeout]);
|
|
71793
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
71794
|
+
if (!this.seedsSettled.has(key)) this.seedsAbandoned.add(key);
|
|
71795
|
+
}
|
|
71796
|
+
return this.observe(deviceId, capName, slice);
|
|
71797
|
+
}
|
|
71798
|
+
/** Start the one seed read of a level cap's key; never awaited here. */
|
|
71799
|
+
ensureSeed(deviceId, capName) {
|
|
71800
|
+
const read = this.readSeed;
|
|
71801
|
+
if (read === void 0) return;
|
|
71802
|
+
const key = `${deviceId}:${capName}`;
|
|
71803
|
+
if (this.seeds.has(key)) return;
|
|
71804
|
+
const settle = () => {
|
|
71805
|
+
this.seedsSettled.add(key);
|
|
71806
|
+
};
|
|
71807
|
+
const started = read(deviceId, capName).then((seed) => {
|
|
71808
|
+
if (seed !== null && !this.lastValues.has(key)) this.lastValues.set(key, seed.value);
|
|
71809
|
+
settle();
|
|
71810
|
+
}, (error) => {
|
|
71811
|
+
this.onSeedFailed?.(deviceId, capName, error);
|
|
71812
|
+
settle();
|
|
71813
|
+
});
|
|
71814
|
+
this.seeds.set(key, started);
|
|
71815
|
+
}
|
|
71816
|
+
seedPending(key) {
|
|
71817
|
+
return this.seeds.has(key) && !this.seedsSettled.has(key) && !this.seedsAbandoned.has(key);
|
|
71566
71818
|
}
|
|
71567
71819
|
/**
|
|
71568
71820
|
* Feed one `DeviceStateChanged` slice for a cap `EVENT_KIND_BY_CAP` maps.
|
|
@@ -71574,7 +71826,10 @@ var SensorTransitionGate = class {
|
|
|
71574
71826
|
* a boolean in either direction, or a word-level cap's word changing.
|
|
71575
71827
|
*/
|
|
71576
71828
|
observe(deviceId, capName, slice) {
|
|
71829
|
+
if (BOOLEAN_LEVEL_FIELD[capName] !== void 0) this.ensureSeed(deviceId, capName);
|
|
71577
71830
|
if (slice["lastChangedAt"] === 0) return this.drop(deviceId, capName, "cold-slice");
|
|
71831
|
+
const unstampedField = UNSTAMPED_WORD_FIELD[capName];
|
|
71832
|
+
if (unstampedField !== void 0) return this.observeUnstampedWord(deviceId, capName, slice, unstampedField);
|
|
71578
71833
|
const wordField = WORD_LEVEL_FIELD[capName];
|
|
71579
71834
|
if (wordField !== void 0) return this.observeWord(deviceId, capName, slice, wordField);
|
|
71580
71835
|
const levelField = BOOLEAN_LEVEL_FIELD[capName];
|
|
@@ -71627,6 +71882,10 @@ var SensorTransitionGate = class {
|
|
|
71627
71882
|
if (typeof raw !== "boolean") return this.drop(deviceId, capName, "non-boolean-value");
|
|
71628
71883
|
const key = `${deviceId}:${capName}`;
|
|
71629
71884
|
const prior = this.lastValues.get(key);
|
|
71885
|
+
if (prior === void 0 && !raw && this.seedPending(key)) return {
|
|
71886
|
+
changed: false,
|
|
71887
|
+
reason: "seed-pending"
|
|
71888
|
+
};
|
|
71630
71889
|
this.lastValues.set(key, raw);
|
|
71631
71890
|
if (prior === void 0) {
|
|
71632
71891
|
if (!raw) return this.drop(deviceId, capName, "baseline-seeded-inactive");
|
|
@@ -71645,11 +71904,25 @@ var SensorTransitionGate = class {
|
|
|
71645
71904
|
observeWord(deviceId, capName, slice, wordField) {
|
|
71646
71905
|
const raw = slice[wordField];
|
|
71647
71906
|
if (typeof raw !== "string" || raw.length === 0) return this.drop(deviceId, capName, "non-word-value");
|
|
71907
|
+
const stamp = slice["lastChangedAt"];
|
|
71908
|
+
if (typeof stamp !== "number" || !Number.isFinite(stamp) || stamp <= 0) return this.drop(deviceId, capName, "cold-slice");
|
|
71648
71909
|
const key = `${deviceId}:${capName}`;
|
|
71649
71910
|
const prior = this.lastWords.get(key);
|
|
71650
71911
|
this.lastWords.set(key, raw);
|
|
71651
71912
|
if (prior === void 0) return this.firstWordSighting(deviceId, capName, slice, raw);
|
|
71652
71913
|
if (prior === raw) return this.drop(deviceId, capName, "no-change");
|
|
71914
|
+
if (stamp < this.startedAtMs) return this.drop(deviceId, capName, "baseline-seeded-stale-active");
|
|
71915
|
+
return { changed: true };
|
|
71916
|
+
}
|
|
71917
|
+
/** A word-level cap with no change stamp: only a DIFFERENT word is a transition. */
|
|
71918
|
+
observeUnstampedWord(deviceId, capName, slice, field) {
|
|
71919
|
+
const raw = slice[field];
|
|
71920
|
+
if (typeof raw !== "string" || raw.length === 0) return this.drop(deviceId, capName, "non-word-value");
|
|
71921
|
+
const key = `${deviceId}:${capName}`;
|
|
71922
|
+
const prior = this.lastWords.get(key);
|
|
71923
|
+
this.lastWords.set(key, raw);
|
|
71924
|
+
if (prior === void 0) return this.drop(deviceId, capName, "baseline-seeded-inactive");
|
|
71925
|
+
if (prior === raw) return this.drop(deviceId, capName, "no-change");
|
|
71653
71926
|
return { changed: true };
|
|
71654
71927
|
}
|
|
71655
71928
|
/**
|
|
@@ -81725,11 +81998,11 @@ function unique(values) {
|
|
|
81725
81998
|
* therefore keeps the alarm's own sentence, which is also what every row
|
|
81726
81999
|
* enqueued before this deploy carries.
|
|
81727
82000
|
*/
|
|
81728
|
-
function combinedAlarmText(kind, source) {
|
|
82001
|
+
function combinedAlarmText(kind, source, followers = []) {
|
|
81729
82002
|
if (kind !== "alarm-triggered" || source === void 0) return null;
|
|
81730
82003
|
return {
|
|
81731
82004
|
titleKey: source.className !== void 0 ? "alarm.combined.title" : "system.alarm-triggered.title",
|
|
81732
|
-
bodyKey: "alarm.intrusion.body"
|
|
82005
|
+
bodyKey: [source, ...followers].some((ref) => ref.sensorChange !== void 0) ? "alarm.intrusion.body.withChanges" : "alarm.intrusion.body"
|
|
81733
82006
|
};
|
|
81734
82007
|
}
|
|
81735
82008
|
/**
|
|
@@ -81744,7 +82017,7 @@ function combinedAlarmText(kind, source) {
|
|
|
81744
82017
|
*/
|
|
81745
82018
|
function alarmTextKeys(transition, body) {
|
|
81746
82019
|
const { kind, source } = transition;
|
|
81747
|
-
const combined = combinedAlarmText(kind, source);
|
|
82020
|
+
const combined = combinedAlarmText(kind, source, transition.followers);
|
|
81748
82021
|
if (combined !== null) return combined;
|
|
81749
82022
|
const baseTitle = transition.mode !== void 0 ? `system.${kind}.title.${transition.mode}` : `system.${kind}.title`;
|
|
81750
82023
|
const titleKey = body?.titleKeySuffix !== void 0 ? `${baseTitle}.${body.titleKeySuffix}` : baseTitle;
|
|
@@ -85451,6 +85724,15 @@ function isLinkedStillId(id) {
|
|
|
85451
85724
|
var NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS = 15e3;
|
|
85452
85725
|
var NC_LINKED_WAKE_POLL_MS = 1e3;
|
|
85453
85726
|
var NC_LINKED_WAKE_MIN_GAP_MS = 6e4;
|
|
85727
|
+
/**
|
|
85728
|
+
* D60 — a tile the freshness read calls stale is RE-POLLED, unforced, until the
|
|
85729
|
+
* wrapper hands back a frame from the event or this bound is spent. The wrapper
|
|
85730
|
+
* settles a soft miss with its old cached hit; one more ask, a moment later,
|
|
85731
|
+
* usually finds the refreshed one. The bound sits inside the dispatcher's still
|
|
85732
|
+
* deadline (`NC_STILL_WAIT_MS`, 3 s): the row waits for it through `pendingUntil`.
|
|
85733
|
+
*/
|
|
85734
|
+
var NC_LINKED_STALE_POLL_MS = 500;
|
|
85735
|
+
var NC_LINKED_STALE_MAX_POLLS = 5;
|
|
85454
85736
|
/** Per camera — the marker's own bound (`SENSOR_MARKER_SNAPSHOT_TIMEOUT_MS`). */
|
|
85455
85737
|
var SENSOR_LINKED_CAPTURE_TIMEOUT_MS = 2500;
|
|
85456
85738
|
/** The capture-free wrapper-state read, per camera. It runs IN PARALLEL with
|
|
@@ -85460,6 +85742,8 @@ var SENSOR_LINKED_CAPTURE_TIMEOUT_MS = 2500;
|
|
|
85460
85742
|
* photograph). A read that misses the bound or fails is UNKNOWN and keeps the
|
|
85461
85743
|
* tile (D49). */
|
|
85462
85744
|
var SENSOR_LINKED_STATE_READ_TIMEOUT_MS = SENSOR_LINKED_CAPTURE_TIMEOUT_MS;
|
|
85745
|
+
/** The battery-slice read behind the unknown-state wake: cheap, and bounded. */
|
|
85746
|
+
var SENSOR_LINKED_BATTERY_READ_TIMEOUT_MS = 1e3;
|
|
85463
85747
|
/** The one cache-only freshness read for the set — one hop, after the
|
|
85464
85748
|
* captures. A read that misses it keeps every tile and says `freshness
|
|
85465
85749
|
* unverified` (D49). */
|
|
@@ -85565,13 +85849,13 @@ var NcLinkedStillShelf = class {
|
|
|
85565
85849
|
startedAt,
|
|
85566
85850
|
options
|
|
85567
85851
|
};
|
|
85568
|
-
let
|
|
85852
|
+
let judged;
|
|
85569
85853
|
try {
|
|
85570
|
-
results = await Promise.all(stills.cameraIds.map((id) => this.captureOne(id, ctx)));
|
|
85854
|
+
const results = await Promise.all(stills.cameraIds.map((id) => this.captureOne(id, ctx)));
|
|
85855
|
+
judged = await this.repollStale(ctx, atMs, await this.judgeFreshness(stills.sensorId, results, atMs));
|
|
85571
85856
|
} finally {
|
|
85572
85857
|
this.pendingWakes.delete(setId);
|
|
85573
85858
|
}
|
|
85574
|
-
const judged = await this.judgeFreshness(stills.sensorId, results, atMs);
|
|
85575
85859
|
for (const r of judged) this.logTile(stills.sensorId, r);
|
|
85576
85860
|
const landed = judged.filter(isLandedTile);
|
|
85577
85861
|
const composed = await this.compose(stills.sensorId, landed, atMs);
|
|
@@ -85618,6 +85902,16 @@ var NcLinkedStillShelf = class {
|
|
|
85618
85902
|
if (verdict.reason === "waking" && ctx.options.wakeSleeping) return this.awaitWaking(cameraId, ctx);
|
|
85619
85903
|
if (verdict.reason === "unreachable" && ctx.options.wakeSleeping) return excluded("wake-skipped:unreachable");
|
|
85620
85904
|
if (verdict.reason !== null) return excluded(verdict.reason);
|
|
85905
|
+
if (verdict.unknown !== null && capture.outcome === "empty" && ctx.options.wakeSleeping && await this.isBatteryCamera(cameraId)) {
|
|
85906
|
+
this.deps.logger.info("linked camera state unknown and capture empty — waking as a battery camera", {
|
|
85907
|
+
tags: { deviceId: cameraId },
|
|
85908
|
+
meta: {
|
|
85909
|
+
sensorId: ctx.sensorId,
|
|
85910
|
+
stateUnknown: verdict.unknown
|
|
85911
|
+
}
|
|
85912
|
+
});
|
|
85913
|
+
return this.wakeOne(cameraId, ctx);
|
|
85914
|
+
}
|
|
85621
85915
|
return {
|
|
85622
85916
|
cameraId,
|
|
85623
85917
|
...capture,
|
|
@@ -85625,6 +85919,20 @@ var NcLinkedStillShelf = class {
|
|
|
85625
85919
|
tookMs: took()
|
|
85626
85920
|
};
|
|
85627
85921
|
}
|
|
85922
|
+
/** Only a camera KNOWN to be a battery camera is woken on an unknown state. */
|
|
85923
|
+
async isBatteryCamera(cameraId) {
|
|
85924
|
+
const read = this.deps.isBatteryCamera;
|
|
85925
|
+
if (read === void 0) return false;
|
|
85926
|
+
try {
|
|
85927
|
+
return await withTimeout$2(read(cameraId), SENSOR_LINKED_BATTERY_READ_TIMEOUT_MS, "battery read") === true;
|
|
85928
|
+
} catch (err) {
|
|
85929
|
+
this.deps.logger.warn("linked camera battery read failed — not woken on an unknown state", {
|
|
85930
|
+
tags: { deviceId: cameraId },
|
|
85931
|
+
meta: { error: String(err) }
|
|
85932
|
+
});
|
|
85933
|
+
return false;
|
|
85934
|
+
}
|
|
85935
|
+
}
|
|
85628
85936
|
/** D998: one forced capture, by the rule's leave, at most once per gap per camera. */
|
|
85629
85937
|
async wakeOne(cameraId, ctx) {
|
|
85630
85938
|
const took = () => this.deps.now() - ctx.startedAt;
|
|
@@ -85686,8 +85994,46 @@ var NcLinkedStillShelf = class {
|
|
|
85686
85994
|
};
|
|
85687
85995
|
}
|
|
85688
85996
|
markPending(ctx) {
|
|
85689
|
-
|
|
85690
|
-
|
|
85997
|
+
this.markPendingUntil(ctx.setId, ctx.startedAt + NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS + NC_LINKED_WAKE_POLL_MS);
|
|
85998
|
+
}
|
|
85999
|
+
markPendingUntil(setId, until) {
|
|
86000
|
+
this.pendingWakes.set(setId, Math.max(this.pendingWakes.get(setId) ?? 0, until));
|
|
86001
|
+
}
|
|
86002
|
+
/**
|
|
86003
|
+
* D60: a `stale` tile asks again (unforced — a poll never wakes) until its
|
|
86004
|
+
* frame is from the event or the bound is spent; the row's still-wait is held
|
|
86005
|
+
* open meanwhile. A camera that never freshens stays `stale`, said on its line.
|
|
86006
|
+
*/
|
|
86007
|
+
async repollStale(ctx, atMs, judged) {
|
|
86008
|
+
let current = judged;
|
|
86009
|
+
if (!current.some((r) => r.outcome === "stale")) return current;
|
|
86010
|
+
this.markPendingUntil(ctx.setId, this.deps.now() + NC_LINKED_STALE_POLL_MS * 6);
|
|
86011
|
+
for (let i = 0; i < NC_LINKED_STALE_MAX_POLLS; i++) {
|
|
86012
|
+
const waiting = current.filter((r) => r.outcome === "stale").map((r) => r.cameraId);
|
|
86013
|
+
if (waiting.length === 0) break;
|
|
86014
|
+
await new Promise((resolve) => setTimeout(resolve, NC_LINKED_STALE_POLL_MS));
|
|
86015
|
+
const landedAgain = (await Promise.all(waiting.map(async (cameraId) => {
|
|
86016
|
+
const capture = await this.captureStill(cameraId, false, SENSOR_LINKED_CAPTURE_TIMEOUT_MS);
|
|
86017
|
+
return capture.outcome === "landed" && capture.jpeg !== void 0 ? {
|
|
86018
|
+
cameraId,
|
|
86019
|
+
outcome: "landed",
|
|
86020
|
+
jpeg: capture.jpeg,
|
|
86021
|
+
tookMs: this.deps.now() - ctx.startedAt
|
|
86022
|
+
} : null;
|
|
86023
|
+
}))).filter((r) => r !== null);
|
|
86024
|
+
if (landedAgain.length === 0) continue;
|
|
86025
|
+
const rejudged = await this.judgeFreshness(ctx.sensorId, landedAgain, atMs);
|
|
86026
|
+
const fresh = new Map(rejudged.filter((r) => r.outcome === "landed").map((r) => [r.cameraId, r]));
|
|
86027
|
+
current = current.map((r) => fresh.get(r.cameraId) ?? r);
|
|
86028
|
+
for (const cameraId of fresh.keys()) this.deps.logger.info("linked camera frame was stale — a re-poll brought one from the event", {
|
|
86029
|
+
tags: { deviceId: cameraId },
|
|
86030
|
+
meta: {
|
|
86031
|
+
sensorId: ctx.sensorId,
|
|
86032
|
+
polls: i + 1
|
|
86033
|
+
}
|
|
86034
|
+
});
|
|
86035
|
+
}
|
|
86036
|
+
return current;
|
|
85691
86037
|
}
|
|
85692
86038
|
/** A wake that brought nothing back names itself, not the generic `empty`. */
|
|
85693
86039
|
asWakeResult(capture) {
|
|
@@ -85756,9 +86102,17 @@ var NcLinkedStillShelf = class {
|
|
|
85756
86102
|
unknown: null
|
|
85757
86103
|
};
|
|
85758
86104
|
} catch (err) {
|
|
86105
|
+
const timedOut = err instanceof LinkedReadTimeout;
|
|
86106
|
+
this.deps.logger.warn("linked camera snapshot state read failed — state unknown", {
|
|
86107
|
+
tags: { deviceId: cameraId },
|
|
86108
|
+
meta: {
|
|
86109
|
+
error: String(err),
|
|
86110
|
+
timedOut
|
|
86111
|
+
}
|
|
86112
|
+
});
|
|
85759
86113
|
return {
|
|
85760
86114
|
reason: null,
|
|
85761
|
-
unknown:
|
|
86115
|
+
unknown: timedOut ? "timeout" : "error"
|
|
85762
86116
|
};
|
|
85763
86117
|
}
|
|
85764
86118
|
}
|
|
@@ -89568,10 +89922,31 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone, cycle
|
|
|
89568
89922
|
latestVersion: typeof systemEvent?.data["latestVersion"] === "string" ? systemEvent.data["latestVersion"] : "",
|
|
89569
89923
|
...systemEvent?.deviceId !== void 0 ? { deviceName: String(systemEvent.deviceId) } : {},
|
|
89570
89924
|
...systemEvent?.nodeId !== void 0 && (systemEvent.kind === "node-online" || systemEvent.kind === "node-offline") ? { nodeName: systemEvent.nodeId } : {},
|
|
89925
|
+
...systemEvent?.alarmSource !== void 0 ? { changes: alarmChangesText(texts, systemEvent.alarmSource, systemEvent.alarmFollowers) } : {},
|
|
89571
89926
|
...systemEventParamsForTemplate(systemEvent?.textParams)
|
|
89572
89927
|
};
|
|
89573
89928
|
}
|
|
89574
89929
|
/**
|
|
89930
|
+
* `{{changes}}` of the combined alarm body: what each sensor of the incident did, in
|
|
89931
|
+
* arrival order and one phrase per device — "Laundry lock unlocked". The phrase is a
|
|
89932
|
+
* catalog key per kind and state (`alarm.change.<kind>.<state>`), then per state
|
|
89933
|
+
* (`alarm.change.<state>`), then the bare name: a state nobody worded never drops the device.
|
|
89934
|
+
*/
|
|
89935
|
+
function alarmChangesText(texts, source, followers) {
|
|
89936
|
+
const phrases = [];
|
|
89937
|
+
for (const ref of [source, ...followers ?? []]) {
|
|
89938
|
+
const change = ref.sensorChange;
|
|
89939
|
+
if (change === void 0) continue;
|
|
89940
|
+
const key = [`alarm.change.${change.kind}.${change.state}`, `alarm.change.${change.state}`].find((candidate) => texts.has(candidate));
|
|
89941
|
+
const phrase = key === void 0 ? change.name : texts.text({
|
|
89942
|
+
key,
|
|
89943
|
+
vars: { device: change.name }
|
|
89944
|
+
});
|
|
89945
|
+
if (!phrases.includes(phrase)) phrases.push(phrase);
|
|
89946
|
+
}
|
|
89947
|
+
return phrases.join(", ");
|
|
89948
|
+
}
|
|
89949
|
+
/**
|
|
89575
89950
|
* Which catalog key supplies the DEFAULT body for a row.
|
|
89576
89951
|
*
|
|
89577
89952
|
* A system event names its own at intake (the variant selection — profiled or
|
|
@@ -105857,7 +106232,7 @@ function groupedAlarmPhaseKey(ruleId, trackId) {
|
|
|
105857
106232
|
* combined alarm notification would then arrive with no picture, which is the
|
|
105858
106233
|
* one thing it exists to fix.
|
|
105859
106234
|
*/
|
|
105860
|
-
function fireRefFor(rule, subject, kind) {
|
|
106235
|
+
function fireRefFor(rule, subject, kind, sensorName) {
|
|
105861
106236
|
const hasEventMedia = kind === "object-event" || kind === "package-event";
|
|
105862
106237
|
const className = subject.classNames[0];
|
|
105863
106238
|
const linkedOwners = subject.trackId === void 0 && subject.linkedStills !== void 0 ? linkedMediaOwners(subject.linkedStills, linkedCameraMode(rule.media)) : void 0;
|
|
@@ -105867,6 +106242,11 @@ function fireRefFor(rule, subject, kind) {
|
|
|
105867
106242
|
ruleName: rule.name,
|
|
105868
106243
|
deviceId: subject.deviceId,
|
|
105869
106244
|
...subject.sourceDeviceId !== void 0 ? { sourceDeviceId: subject.sourceDeviceId } : {},
|
|
106245
|
+
...subject.sensorKind !== void 0 && subject.sensorState !== void 0 && sensorName !== void 0 ? { sensorChange: {
|
|
106246
|
+
name: sensorName,
|
|
106247
|
+
kind: subject.sensorKind,
|
|
106248
|
+
state: subject.sensorState
|
|
106249
|
+
} } : {},
|
|
105870
106250
|
recordId: subject.recordId,
|
|
105871
106251
|
...trackId !== void 0 ? { trackId } : {},
|
|
105872
106252
|
...linkedOwners !== void 0 && linkedOwners.extraStills.length > 0 ? { extraStills: linkedOwners.extraStills.map((x) => ({ ...x })) } : {},
|
|
@@ -105878,6 +106258,12 @@ function fireRefFor(rule, subject, kind) {
|
|
|
105878
106258
|
...subject.confidence !== void 0 ? { confidence: subject.confidence } : {}
|
|
105879
106259
|
};
|
|
105880
106260
|
}
|
|
106261
|
+
/** The directory name of the sensor a match was about; absent for a camera subject. */
|
|
106262
|
+
function sensorNameOf(host, subject) {
|
|
106263
|
+
if (subject.source !== "sensor") return void 0;
|
|
106264
|
+
const name = host.deviceDirectory.get(subject.sourceDeviceId ?? subject.deviceId)?.name.trim();
|
|
106265
|
+
return name !== void 0 && name.length > 0 ? name : void 0;
|
|
106266
|
+
}
|
|
105881
106267
|
/**
|
|
105882
106268
|
* Run this rule's onTrigger / onTriggerOff bindings for this match, and —
|
|
105883
106269
|
* when the rule is gated on `armed_*` — auto-trigger the CamStack
|
|
@@ -105899,7 +106285,7 @@ function runRuleActions(host, rule, subject, kind, controls) {
|
|
|
105899
106285
|
deviceId: subject.deviceId,
|
|
105900
106286
|
bindings,
|
|
105901
106287
|
lookup: (id) => host.sequences.get(id),
|
|
105902
|
-
origin: fireRefFor(rule, subject, kind)
|
|
106288
|
+
origin: fireRefFor(rule, subject, kind, sensorNameOf(host, subject))
|
|
105903
106289
|
});
|
|
105904
106290
|
return phase;
|
|
105905
106291
|
}
|
|
@@ -105923,7 +106309,7 @@ function autoTriggerAlarmPanel(host, rule, subject, kind) {
|
|
|
105923
106309
|
});
|
|
105924
106310
|
return NO_ALARM_PHASE;
|
|
105925
106311
|
}
|
|
105926
|
-
const origin = fireRefFor(rule, subject, kind);
|
|
106312
|
+
const origin = fireRefFor(rule, subject, kind, sensorNameOf(host, subject));
|
|
105927
106313
|
const phase = panel.classifyRuleMatch(origin);
|
|
105928
106314
|
try {
|
|
105929
106315
|
panel.triggerFromRule(origin);
|
|
@@ -107368,6 +107754,7 @@ var NotificationCenter = class {
|
|
|
107368
107754
|
this.linkedStills = getSnapshot !== void 0 ? new NcLinkedStillShelf({
|
|
107369
107755
|
getSnapshot,
|
|
107370
107756
|
...deps.getSnapshotState !== void 0 ? { getSnapshotState: deps.getSnapshotState } : {},
|
|
107757
|
+
...deps.isBatteryCamera !== void 0 ? { isBatteryCamera: deps.isBatteryCamera } : {},
|
|
107371
107758
|
...deps.getFrameCapturedAt !== void 0 ? { getFrameCapturedAt: deps.getFrameCapturedAt } : {},
|
|
107372
107759
|
isOnline: (id) => this.deviceDirectory.get(id)?.online,
|
|
107373
107760
|
logger: this.logger.child("linked-stills"),
|
|
@@ -110139,6 +110526,7 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
110139
110526
|
now: () => Date.now()
|
|
110140
110527
|
}),
|
|
110141
110528
|
...buildLinkedSnapshotReads(api.snapshot),
|
|
110529
|
+
isBatteryCamera: async (deviceId) => await api.battery.getStatus.query({ deviceId }) !== null,
|
|
110142
110530
|
identityIdsByName: async () => {
|
|
110143
110531
|
const byName = /* @__PURE__ */ new Map();
|
|
110144
110532
|
const identities = await stores.identityStore.listIdentities();
|
|
@@ -114336,6 +114724,12 @@ function deliveredIdempotencyKey(entryId) {
|
|
|
114336
114724
|
function pickedUpIdempotencyKey(entryId) {
|
|
114337
114725
|
return `pa-pkg-${entryId}-pickedup`;
|
|
114338
114726
|
}
|
|
114727
|
+
/**
|
|
114728
|
+
* How long a delivery waits for its still to be written before it ships
|
|
114729
|
+
* without one. Under the notification still poll (`NC_STILL_WAIT_MS`, 3 s) so
|
|
114730
|
+
* a slow store never holds the alert longer than the dispatcher would.
|
|
114731
|
+
*/
|
|
114732
|
+
var PACKAGE_FRAME_PERSIST_BOUND_MS = 2e3;
|
|
114339
114733
|
var PackageDropDetector = class {
|
|
114340
114734
|
deps;
|
|
114341
114735
|
/** Per-device mirror of the last LOGGED value of the `packageDropEnabled`
|
|
@@ -114349,6 +114743,62 @@ var PackageDropDetector = class {
|
|
|
114349
114743
|
* LLM gate (D931). `null` means the delivery is dropped (no, or no yes).
|
|
114350
114744
|
* An empty object means the rule did not ask. A verdict is spread onto the row.
|
|
114351
114745
|
*/
|
|
114746
|
+
/**
|
|
114747
|
+
* The delivery's still, written once under `object:<rowId>` when the entry
|
|
114748
|
+
* had no key frame. Never throws and never waits past
|
|
114749
|
+
* {@link PACKAGE_FRAME_PERSIST_BOUND_MS}: a delivery without a picture still
|
|
114750
|
+
* ships, and says why (D391).
|
|
114751
|
+
*/
|
|
114752
|
+
async persistDeliveryStill(entry, rowId, timestamp) {
|
|
114753
|
+
const persist = this.deps.persistDeliveryFrame;
|
|
114754
|
+
if (persist === void 0) return void 0;
|
|
114755
|
+
const tags = { deviceId: entry.deviceId };
|
|
114756
|
+
const meta = {
|
|
114757
|
+
entryId: entry.id,
|
|
114758
|
+
rowId
|
|
114759
|
+
};
|
|
114760
|
+
let timer;
|
|
114761
|
+
try {
|
|
114762
|
+
const bound = new Promise((resolve) => {
|
|
114763
|
+
timer = setTimeout(() => resolve("timeout"), PACKAGE_FRAME_PERSIST_BOUND_MS);
|
|
114764
|
+
});
|
|
114765
|
+
const written = await Promise.race([persist({
|
|
114766
|
+
deviceId: entry.deviceId,
|
|
114767
|
+
trackId: entry.sourceTrackId ?? entry.id,
|
|
114768
|
+
rowId,
|
|
114769
|
+
timestamp
|
|
114770
|
+
}), bound]);
|
|
114771
|
+
if (written === "timeout") {
|
|
114772
|
+
this.deps.logger.warn("package delivery still not written in time — shipping without it", {
|
|
114773
|
+
tags,
|
|
114774
|
+
meta: {
|
|
114775
|
+
...meta,
|
|
114776
|
+
boundMs: PACKAGE_FRAME_PERSIST_BOUND_MS
|
|
114777
|
+
}
|
|
114778
|
+
});
|
|
114779
|
+
return;
|
|
114780
|
+
}
|
|
114781
|
+
if (written === null) {
|
|
114782
|
+
this.deps.logger.warn("package delivery has no frame to write — shipping without a still", {
|
|
114783
|
+
tags,
|
|
114784
|
+
meta
|
|
114785
|
+
});
|
|
114786
|
+
return;
|
|
114787
|
+
}
|
|
114788
|
+
return written;
|
|
114789
|
+
} catch (err) {
|
|
114790
|
+
this.deps.logger.warn("package delivery still write failed — shipping without it", {
|
|
114791
|
+
tags,
|
|
114792
|
+
meta: {
|
|
114793
|
+
...meta,
|
|
114794
|
+
error: errMsg(err)
|
|
114795
|
+
}
|
|
114796
|
+
});
|
|
114797
|
+
return;
|
|
114798
|
+
} finally {
|
|
114799
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
114800
|
+
}
|
|
114801
|
+
}
|
|
114352
114802
|
async llmFields(entry, rules, hitZones, tags) {
|
|
114353
114803
|
if (!rules.some((rule) => rule.confirmWithLlm === true && rule.zoneIds.some((id) => hitZones.includes(id)))) return {};
|
|
114354
114804
|
const confirm = this.deps.confirmPackage;
|
|
@@ -114610,6 +115060,7 @@ var PackageDropDetector = class {
|
|
|
114610
115060
|
});
|
|
114611
115061
|
return;
|
|
114612
115062
|
}
|
|
115063
|
+
const keyFrameMediaId = entry.keyFrameMediaId ?? await this.persistDeliveryStill(entry, rowId, rowTimestamp);
|
|
114613
115064
|
const ev = {
|
|
114614
115065
|
...draft,
|
|
114615
115066
|
id: rowId
|
|
@@ -114621,7 +115072,7 @@ var PackageDropDetector = class {
|
|
|
114621
115072
|
eventId: String(rowId),
|
|
114622
115073
|
className: entry.className,
|
|
114623
115074
|
zoneIds: hitZones,
|
|
114624
|
-
...
|
|
115075
|
+
...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {},
|
|
114625
115076
|
bbox: {
|
|
114626
115077
|
x: entry.bbox.x,
|
|
114627
115078
|
y: entry.bbox.y,
|
|
@@ -118184,6 +118635,7 @@ var BuildStores = class extends BuildStoresBase {
|
|
|
118184
118635
|
resolveSettings: (deviceId) => this.resolveDevicePackageDropSettings(deviceId),
|
|
118185
118636
|
resolveZoneMembershipMinOverlap: async (deviceId) => (await this.resolveDeviceTrackingSettings(deviceId)).zoneMembershipMinOverlap,
|
|
118186
118637
|
confirmPackage: (input) => this.deps.confirmPackage(input),
|
|
118638
|
+
persistDeliveryFrame: (request) => this.deps.persistPackageDeliveryFrame(request),
|
|
118187
118639
|
onLlmSilence: (edge) => {
|
|
118188
118640
|
const deviceId = edge.phase === "silent" ? edge.note.deviceId : edge.deviceId;
|
|
118189
118641
|
this.ctx.eventBus.emit({
|
|
@@ -120305,108 +120757,6 @@ async function ensureTrackHierarchyMedia(deps, input) {
|
|
|
120305
120757
|
await Promise.all(tasks);
|
|
120306
120758
|
}
|
|
120307
120759
|
//#endregion
|
|
120308
|
-
//#region src/pipeline-analytics/pipeline/key-frame-persist.ts
|
|
120309
|
-
/**
|
|
120310
|
-
* A box is written only when it can be USED — a degenerate rectangle, or one
|
|
120311
|
-
* measured on a degenerate frame, says nothing and would read back as a box at
|
|
120312
|
-
* the origin. Absent is the honest answer; the reader then pairs a snapshot,
|
|
120313
|
-
* exactly as it did before D510.
|
|
120314
|
-
*
|
|
120315
|
-
* Its result is spread into BOTH writes: the small companion is derived from
|
|
120316
|
-
* the very bytes of the native one, so it is the same scene at the same
|
|
120317
|
-
* instant and the box maps into it by ratio like any other scale of the frame.
|
|
120318
|
-
* A box on one row and not the other would make the summary's answer depend on
|
|
120319
|
-
* which row a reader happened to pick.
|
|
120320
|
-
*/
|
|
120321
|
-
function isStatableSubject(subject) {
|
|
120322
|
-
if (subject === void 0) return false;
|
|
120323
|
-
return subject.w > 0 && subject.h > 0 && subject.frameWidth > 0 && subject.frameHeight > 0;
|
|
120324
|
-
}
|
|
120325
|
-
/**
|
|
120326
|
-
* Read, derive and store one track's key frame + its web companion.
|
|
120327
|
-
*
|
|
120328
|
-
* PARCEL-OR-NOTHING: no parcel writes nothing and the caller's bounded retry
|
|
120329
|
-
* parks a later frame. The `keyFrame` write lands FIRST and its media id (D482)
|
|
120330
|
-
* is what the outcome carries — a `keyFrameSmall` failure afterwards must not
|
|
120331
|
-
* re-arm a retry that would re-shoot a key frame the rebuild can already read.
|
|
120332
|
-
*/
|
|
120333
|
-
async function persistTrackKeyFrame(deps, input) {
|
|
120334
|
-
const { deviceId, trackId, retry } = input;
|
|
120335
|
-
const drop = {
|
|
120336
|
-
tags: { deviceId },
|
|
120337
|
-
meta: {
|
|
120338
|
-
trackId,
|
|
120339
|
-
retry
|
|
120340
|
-
}
|
|
120341
|
-
};
|
|
120342
|
-
try {
|
|
120343
|
-
const parcel = await deps.fetchParkedKeyFrame(deviceId, trackId);
|
|
120344
|
-
if (parcel === null || parcel.jpeg.byteLength === 0) {
|
|
120345
|
-
deps.logger.error("key-frame parcel missing — nothing to write, retry parks a later frame", { ...drop });
|
|
120346
|
-
return { kind: "parcel-missing" };
|
|
120347
|
-
}
|
|
120348
|
-
const subjectBox = isStatableSubject(parcel.subject) ? { subjectBox: parcel.subject } : {};
|
|
120349
|
-
const keyFrameMediaId = await deps.media.putReplacing({
|
|
120350
|
-
deviceId,
|
|
120351
|
-
ownerKind: "track",
|
|
120352
|
-
ownerId: trackId,
|
|
120353
|
-
kind: "keyFrame",
|
|
120354
|
-
timestamp: parcel.timestamp,
|
|
120355
|
-
data: parcel.jpeg,
|
|
120356
|
-
...subjectBox
|
|
120357
|
-
});
|
|
120358
|
-
try {
|
|
120359
|
-
const keyFrameSmall = await deps.deriveSmall(parcel.jpeg);
|
|
120360
|
-
await deps.media.putReplacing({
|
|
120361
|
-
deviceId,
|
|
120362
|
-
ownerKind: "track",
|
|
120363
|
-
ownerId: trackId,
|
|
120364
|
-
kind: "keyFrameSmall",
|
|
120365
|
-
timestamp: parcel.timestamp,
|
|
120366
|
-
data: keyFrameSmall,
|
|
120367
|
-
...subjectBox
|
|
120368
|
-
});
|
|
120369
|
-
} catch (err) {
|
|
120370
|
-
deps.logger.warn("keyFrameSmall write failed — the native keyFrame is stored", {
|
|
120371
|
-
...drop,
|
|
120372
|
-
meta: {
|
|
120373
|
-
...drop.meta,
|
|
120374
|
-
nodeId: parcel.nodeId,
|
|
120375
|
-
error: errMsg(err)
|
|
120376
|
-
}
|
|
120377
|
-
});
|
|
120378
|
-
}
|
|
120379
|
-
deps.logger.debug("key frame written from the parked parcel", {
|
|
120380
|
-
...drop,
|
|
120381
|
-
meta: {
|
|
120382
|
-
...drop.meta,
|
|
120383
|
-
nodeId: parcel.nodeId,
|
|
120384
|
-
width: parcel.width,
|
|
120385
|
-
height: parcel.height,
|
|
120386
|
-
bytes: parcel.jpeg.byteLength,
|
|
120387
|
-
parkedTimestamp: parcel.timestamp
|
|
120388
|
-
}
|
|
120389
|
-
});
|
|
120390
|
-
return {
|
|
120391
|
-
kind: "landed",
|
|
120392
|
-
keyFrameMediaId
|
|
120393
|
-
};
|
|
120394
|
-
} catch (err) {
|
|
120395
|
-
const error = errMsg(err);
|
|
120396
|
-
deps.logger.warn("key-frame write failed", {
|
|
120397
|
-
...drop,
|
|
120398
|
-
meta: {
|
|
120399
|
-
...drop.meta,
|
|
120400
|
-
error
|
|
120401
|
-
}
|
|
120402
|
-
});
|
|
120403
|
-
return {
|
|
120404
|
-
kind: "error",
|
|
120405
|
-
error
|
|
120406
|
-
};
|
|
120407
|
-
}
|
|
120408
|
-
}
|
|
120409
|
-
//#endregion
|
|
120410
120760
|
//#region src/pipeline-analytics/pipeline/key-frame-retry.ts
|
|
120411
120761
|
/** Retry-path attempts allowed per track. The operator's number. */
|
|
120412
120762
|
var KEY_FRAME_MAX_RETRIES = 3;
|
|
@@ -121990,6 +122340,8 @@ var DEFAULT_ASSIST_MIN_SCORE = .3;
|
|
|
121990
122340
|
* cannot page the whole table into memory to build a manifest.
|
|
121991
122341
|
*/
|
|
121992
122342
|
var RETRAIN_EXPORT_MAX = 2e4;
|
|
122343
|
+
/** How far back a restart looks for a level cap's last persisted boolean. */
|
|
122344
|
+
var LEVEL_SEED_WINDOW_MS = 48 * 36e5;
|
|
121993
122345
|
/**
|
|
121994
122346
|
* Pipeline Analytics addon — subscribes to `PipelineInferenceResult` +
|
|
121995
122347
|
* `PipelineAudioInferenceResult` on the bus, refines raw detections
|
|
@@ -122265,7 +122617,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
122265
122617
|
*/
|
|
122266
122618
|
sensorTransitions = new SensorTransitionGate({
|
|
122267
122619
|
startedAtMs: Date.now(),
|
|
122268
|
-
now: () => Date.now()
|
|
122620
|
+
now: () => Date.now(),
|
|
122621
|
+
readSeed: (deviceId, capName) => this.readPersistedLevel(deviceId, capName),
|
|
122622
|
+
onSeedFailed: (deviceId, capName, error) => this.ctx.logger.warn("level seed read failed — gate starts unseeded", {
|
|
122623
|
+
tags: { deviceId },
|
|
122624
|
+
meta: {
|
|
122625
|
+
capName,
|
|
122626
|
+
error: errMsg(error)
|
|
122627
|
+
}
|
|
122628
|
+
})
|
|
122269
122629
|
});
|
|
122270
122630
|
/** `camera-index-unknown` drops, summarised per window — see the file. */
|
|
122271
122631
|
cameraIndexUnknownDrops = new CameraIndexUnknownDrops({ nowMs: Date.now() });
|
|
@@ -124077,6 +124437,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
124077
124437
|
this.packageDropDetector = value;
|
|
124078
124438
|
},
|
|
124079
124439
|
confirmPackage: (input) => this.confirmPackageDelivery(input),
|
|
124440
|
+
persistPackageDeliveryFrame: (request) => this.persistPackageDeliveryFrame(request),
|
|
124080
124441
|
stationaryEvidence: () => this.stationaryEvidence,
|
|
124081
124442
|
setStationaryEvidence: (value) => {
|
|
124082
124443
|
this.stationaryEvidence = value;
|
|
@@ -128232,6 +128593,44 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
128232
128593
|
via: "frame"
|
|
128233
128594
|
});
|
|
128234
128595
|
}
|
|
128596
|
+
/**
|
|
128597
|
+
* #383: a delivery whose entry had no key frame writes the source track's
|
|
128598
|
+
* parked frame under the object row it just created — the one owner the
|
|
128599
|
+
* notification ladder reads for a package event.
|
|
128600
|
+
*/
|
|
128601
|
+
async persistPackageDeliveryFrame(request) {
|
|
128602
|
+
const mediaStore = this.mediaStore;
|
|
128603
|
+
if (mediaStore === null) return null;
|
|
128604
|
+
return persistObjectFrame({
|
|
128605
|
+
fetchParkedKeyFrame: async (deviceId, trackId) => {
|
|
128606
|
+
const parcel = await this.mediaCapture.fetchParkedParcel(trackId, "keyFrame");
|
|
128607
|
+
if (parcel === null || parcel.info.deviceId !== deviceId) return null;
|
|
128608
|
+
const subject = parcel.info.subject;
|
|
128609
|
+
return {
|
|
128610
|
+
jpeg: parcel.jpeg,
|
|
128611
|
+
width: parcel.width,
|
|
128612
|
+
height: parcel.height,
|
|
128613
|
+
timestamp: parcel.timestamp,
|
|
128614
|
+
nodeId: parcel.info.nodeId,
|
|
128615
|
+
subject: {
|
|
128616
|
+
x: subject.bbox.x,
|
|
128617
|
+
y: subject.bbox.y,
|
|
128618
|
+
w: subject.bbox.w,
|
|
128619
|
+
h: subject.bbox.h,
|
|
128620
|
+
frameWidth: subject.frameWidth,
|
|
128621
|
+
frameHeight: subject.frameHeight
|
|
128622
|
+
}
|
|
128623
|
+
};
|
|
128624
|
+
},
|
|
128625
|
+
media: mediaStore,
|
|
128626
|
+
logger: this.ctx.logger
|
|
128627
|
+
}, {
|
|
128628
|
+
deviceId: request.deviceId,
|
|
128629
|
+
trackId: request.trackId,
|
|
128630
|
+
objectId: String(request.rowId),
|
|
128631
|
+
timestamp: request.timestamp
|
|
128632
|
+
});
|
|
128633
|
+
}
|
|
128235
128634
|
async confirmPackageDelivery(input) {
|
|
128236
128635
|
const crops = this.packageScene?.cropsFor(input.deviceId);
|
|
128237
128636
|
return confirmPackageWithLlm({
|
|
@@ -129233,7 +129632,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
129233
129632
|
const store = this.sensorEventStore;
|
|
129234
129633
|
const cache = this.linkedCamerasCache;
|
|
129235
129634
|
if (store === null || cache === null) return;
|
|
129236
|
-
if (!this.sensorSliceCarriedATransition(data)) return;
|
|
129635
|
+
if (!await this.sensorSliceCarriedATransition(data)) return;
|
|
129237
129636
|
const cameras = await resolveSensorNotificationCameras(cache, data).catch((err) => {
|
|
129238
129637
|
this.ctx.logger.debug("linked-camera resolution failed — evaluated per row", {
|
|
129239
129638
|
tags: { deviceId: data.deviceId },
|
|
@@ -129401,7 +129800,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
129401
129800
|
* The drops are SUMMARISED, never logged one by one: this path carries every
|
|
129402
129801
|
* camera's native `motion` slice at motion rate.
|
|
129403
129802
|
*/
|
|
129404
|
-
sensorSliceCarriedATransition(data) {
|
|
129803
|
+
async sensorSliceCarriedATransition(data) {
|
|
129405
129804
|
const slice = data.slice;
|
|
129406
129805
|
if (typeof slice !== "object" || slice === null || Array.isArray(slice)) {
|
|
129407
129806
|
this.ctx.logger.debug("sensor state change carried no slice — dropped", {
|
|
@@ -129410,11 +129809,53 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
129410
129809
|
});
|
|
129411
129810
|
return false;
|
|
129412
129811
|
}
|
|
129413
|
-
const verdict = this.sensorTransitions.
|
|
129812
|
+
const verdict = await this.sensorTransitions.observeSettled(data.deviceId, data.capName, { ...slice });
|
|
129414
129813
|
this.flushSensorTransitionDrops();
|
|
129814
|
+
if (verdict.changed) {
|
|
129815
|
+
const entries = Object.entries(slice);
|
|
129816
|
+
const field = (key) => entries.find(([k]) => k === key)?.[1];
|
|
129817
|
+
this.ctx.logger.info("sensor slice passed the transition gate", {
|
|
129818
|
+
tags: { deviceId: data.deviceId },
|
|
129819
|
+
meta: {
|
|
129820
|
+
capName: data.capName,
|
|
129821
|
+
state: field("state") ?? field("value") ?? field("entryOpen") ?? field("active") ?? null,
|
|
129822
|
+
lastChangedAt: field("lastChangedAt") ?? null,
|
|
129823
|
+
verdict: "changed"
|
|
129824
|
+
}
|
|
129825
|
+
});
|
|
129826
|
+
}
|
|
129415
129827
|
return verdict.changed;
|
|
129416
129828
|
}
|
|
129417
129829
|
/**
|
|
129830
|
+
* The last persisted boolean of a level cap, from device-history — the
|
|
129831
|
+
* gate's seed after a restart. Off the event path (the gate starts it at the
|
|
129832
|
+
* first sighting). A cap that declares no history field, or a series with no
|
|
129833
|
+
* point, answers `null`.
|
|
129834
|
+
*/
|
|
129835
|
+
async readPersistedLevel(deviceId, capName) {
|
|
129836
|
+
const key = levelSeriesKey(capName);
|
|
129837
|
+
if (key === null) return null;
|
|
129838
|
+
const to = Date.now();
|
|
129839
|
+
const result = await this.ctx.api.deviceHistory.query.query({
|
|
129840
|
+
deviceId,
|
|
129841
|
+
key,
|
|
129842
|
+
from: to - LEVEL_SEED_WINDOW_MS,
|
|
129843
|
+
to,
|
|
129844
|
+
resolution: "raw",
|
|
129845
|
+
maxPoints: 200
|
|
129846
|
+
});
|
|
129847
|
+
const latest = result.series.flatMap((s) => s.points).reduce((best, p) => best === null || p.t > best.t ? p : best, null);
|
|
129848
|
+
if (latest !== null && latest.last !== null) return {
|
|
129849
|
+
value: latest.last >= .5,
|
|
129850
|
+
since: latest.t
|
|
129851
|
+
};
|
|
129852
|
+
const held = result.series.flatMap((s) => s.before === null ? [] : [s.before]).reduce((best, b) => best === null || b.t > best.t ? b : best, null);
|
|
129853
|
+
return held !== null && held.value !== null ? {
|
|
129854
|
+
value: held.value >= .5,
|
|
129855
|
+
since: held.t
|
|
129856
|
+
} : null;
|
|
129857
|
+
}
|
|
129858
|
+
/**
|
|
129418
129859
|
* One `info` line per (device, cap, reason) per window, or none.
|
|
129419
129860
|
*
|
|
129420
129861
|
* A branch that drops work says so — but this one drops ~0.6 events/s
|