@camstack/addon-post-analysis 1.2.661 → 1.2.662

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.
Files changed (16) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/{clip-model-registry-RvEPUqvS.js → clip-model-registry-C3N1NpST.js} +191 -30
  3. package/dist/{clip-model-registry-CVajwmEk.mjs → clip-model-registry-ELaSS9jq.mjs} +191 -30
  4. package/dist/embedding-encoder/index.js +1 -1
  5. package/dist/embedding-encoder/index.mjs +1 -1
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CwKrui6c.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BvOlyiL9.mjs} +2 -2
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D4zkNcgf.mjs +26 -0
  9. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-vaatUWY6.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-DR75U6YM.mjs → hostInit-BjZchMMh.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +177 -5
  12. package/dist/pipeline-analytics/index.mjs +177 -5
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +2 -2
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BDKliSzv.mjs +0 -26
  16. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BGdtpHOa.mjs +0 -26
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_clip_model_registry = require("../clip-model-registry-RvEPUqvS.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-C3N1NpST.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -8950,6 +8950,17 @@ var en_default = {
8950
8950
  "detection.subject.identity": "{{label}}",
8951
8951
  "detection.subject.plate": "{{classNoun}} {{label}}",
8952
8952
  "detection.inZones": " in {{zones}}",
8953
+ "appliance.finished.title": "{{device}} finished",
8954
+ "appliance.finished.body": "{{device}} finished{{afterDuration}}{{usedEnergy}}",
8955
+ "appliance.finished.duration": " after {{duration}}",
8956
+ "appliance.finished.energy": " and used {{energy}}",
8957
+ "appliance.duration.minutes": {
8958
+ "one": "{{minutes}} minute",
8959
+ "other": "{{minutes}} minutes"
8960
+ },
8961
+ "appliance.duration.hours": "{{hours}} h",
8962
+ "appliance.duration.hoursMinutes": "{{hours}} h {{minutes}} min",
8963
+ "appliance.energy.value": "{{value}} kWh",
8953
8964
  "class.person": {
8954
8965
  "one": "person",
8955
8966
  "other": "people"
@@ -9356,6 +9367,17 @@ var it_default = {
9356
9367
  "detection.subject.identity": "{{label}}",
9357
9368
  "detection.subject.plate": "{{classNoun}} {{label}}",
9358
9369
  "detection.inZones": " in {{zones}}",
9370
+ "appliance.finished.title": "{{device}} ha terminato",
9371
+ "appliance.finished.body": "{{device}} ha terminato{{afterDuration}}{{usedEnergy}}",
9372
+ "appliance.finished.duration": " dopo {{duration}}",
9373
+ "appliance.finished.energy": " e ha consumato {{energy}}",
9374
+ "appliance.duration.minutes": {
9375
+ "one": "{{minutes}} minuto",
9376
+ "other": "{{minutes}} minuti"
9377
+ },
9378
+ "appliance.duration.hours": "{{hours}} h",
9379
+ "appliance.duration.hoursMinutes": "{{hours}} h {{minutes}} min",
9380
+ "appliance.energy.value": "{{value}} kWh",
9359
9381
  "class.person": {
9360
9382
  "one": "persona",
9361
9383
  "other": "persone"
@@ -68703,6 +68725,109 @@ function createAnalyticsWidgetsProvider() {
68703
68725
  ] };
68704
68726
  }
68705
68727
  //#endregion
68728
+ //#region src/notification-center/appliance-cycle.ts
68729
+ var NC_UNKNOWN_CYCLE_FACTS = {
68730
+ durationMs: null,
68731
+ energyKwh: null
68732
+ };
68733
+ var NC_EMPTY_CYCLE_VARS = {
68734
+ duration: "",
68735
+ energy: "",
68736
+ afterDuration: "",
68737
+ usedEnergy: ""
68738
+ };
68739
+ /** `52 minutes` / `1 h 12 min` / `2 h`; `''` below one minute (never "0 minutes"). */
68740
+ function durationText(texts, durationMs) {
68741
+ if (durationMs === null || !Number.isFinite(durationMs)) return "";
68742
+ const totalMinutes = Math.round(durationMs / 6e4);
68743
+ if (totalMinutes < 1) return "";
68744
+ if (totalMinutes < 60) return texts.text({
68745
+ key: "appliance.duration.minutes",
68746
+ count: totalMinutes,
68747
+ vars: { minutes: `${totalMinutes}` }
68748
+ });
68749
+ const hours = Math.floor(totalMinutes / 60);
68750
+ const minutes = totalMinutes % 60;
68751
+ return minutes === 0 ? texts.text({
68752
+ key: "appliance.duration.hours",
68753
+ vars: { hours: `${hours}` }
68754
+ }) : texts.text({
68755
+ key: "appliance.duration.hoursMinutes",
68756
+ vars: {
68757
+ hours: `${hours}`,
68758
+ minutes: `${minutes}`
68759
+ }
68760
+ });
68761
+ }
68762
+ /** `0,84 kWh` (it) / `0.84 kWh` (en); `''` when unmeasured. */
68763
+ function energyText(texts, energyKwh) {
68764
+ if (energyKwh === null || !Number.isFinite(energyKwh) || energyKwh < 0) return "";
68765
+ const value = energyKwh.toLocaleString(texts.language, {
68766
+ minimumFractionDigits: 2,
68767
+ maximumFractionDigits: 2
68768
+ });
68769
+ return texts.text({
68770
+ key: "appliance.energy.value",
68771
+ vars: { value }
68772
+ });
68773
+ }
68774
+ /** The four variables of a finished cycle, fragments rendered from scalars only. */
68775
+ function ncApplianceCycleVars(texts, facts) {
68776
+ const duration = durationText(texts, facts.durationMs);
68777
+ const energy = energyText(texts, facts.energyKwh);
68778
+ return {
68779
+ duration,
68780
+ energy,
68781
+ afterDuration: duration === "" ? "" : texts.text({
68782
+ key: "appliance.finished.duration",
68783
+ vars: { duration }
68784
+ }),
68785
+ usedEnergy: energy === "" ? "" : texts.text({
68786
+ key: "appliance.finished.energy",
68787
+ vars: { energy }
68788
+ })
68789
+ };
68790
+ }
68791
+ /**
68792
+ * The frozen reference a FINISHED appliance cycle row carries (`{}` for every
68793
+ * other row): kind `appliance-cycle`, edge `cleared`. The appliance is the
68794
+ * sensor itself — a linked camera is only where the row was attributed.
68795
+ */
68796
+ function applianceCycleOf(subject) {
68797
+ if (subject.sensorKind !== "appliance-cycle" || subject.sensorRaised !== false) return {};
68798
+ return { applianceCycle: { deviceId: subject.linkedSensorId ?? subject.sourceDeviceId ?? subject.deviceId } };
68799
+ }
68800
+ //#endregion
68801
+ //#region src/notification-center/appliance-cycle-runs.ts
68802
+ var CYCLE_FACTS_WAIT_MS = 3e3;
68803
+ var CYCLE_FACTS_POLL_MS = 250;
68804
+ /** A closed run belongs to this finish when it ended within this much of it. */
68805
+ var CYCLE_RUN_MATCH_MS = 10 * 6e4;
68806
+ function factsOfClosed(run, finishedAtMs) {
68807
+ if (run.endedAt === null || Math.abs(run.endedAt - finishedAtMs) > CYCLE_RUN_MATCH_MS) return NC_UNKNOWN_CYCLE_FACTS;
68808
+ return {
68809
+ durationMs: run.durationMs,
68810
+ energyKwh: run.detail.energyKwh
68811
+ };
68812
+ }
68813
+ function applianceCycleFactsFromRuns(ports) {
68814
+ return async ({ deviceId, finishedAtMs }) => {
68815
+ let waited = 0;
68816
+ for (;;) {
68817
+ const run = await ports.newestRun(deviceId);
68818
+ if (run === null) return NC_UNKNOWN_CYCLE_FACTS;
68819
+ if (run.state === "closed") return factsOfClosed(run, finishedAtMs);
68820
+ if (run.startedAt > finishedAtMs) return NC_UNKNOWN_CYCLE_FACTS;
68821
+ if (waited >= CYCLE_FACTS_WAIT_MS) return {
68822
+ durationMs: finishedAtMs - run.startedAt,
68823
+ energyKwh: null
68824
+ };
68825
+ await ports.sleep(CYCLE_FACTS_POLL_MS);
68826
+ waited += CYCLE_FACTS_POLL_MS;
68827
+ }
68828
+ };
68829
+ }
68830
+ //#endregion
68706
68831
  //#region src/pipeline-analytics/runtime/wire-subscriptions.ts
68707
68832
  /**
68708
68833
  * Every category the Notification Center's system-event intake can turn into a
@@ -70664,6 +70789,7 @@ Object.freeze({
70664
70789
  "page": 1,
70665
70790
  "pageSize": 50
70666
70791
  } },
70792
+ "device-runs": { "listRuns": { "limit": 50 } },
70667
70793
  "llm": { "upsertProfile": { "profile": { "__nested__": {
70668
70794
  "timeoutMs": 6e4,
70669
70795
  "connectTimeoutMs": 1e4,
@@ -87772,6 +87898,36 @@ var NcDispatcher = class {
87772
87898
  });
87773
87899
  }
87774
87900
  /**
87901
+ * The facts of a finished appliance cycle; unknown on every other row and on
87902
+ * any failure (a part that cannot be read is omitted, never a blocked send).
87903
+ */
87904
+ async applianceCycleFacts(entry) {
87905
+ const subject = entry.payload.subject;
87906
+ const resolve = this.deps.resolveApplianceCycleFacts;
87907
+ const deviceId = subject.applianceCycle?.deviceId;
87908
+ if (resolve === void 0 || deviceId === void 0) return NC_UNKNOWN_CYCLE_FACTS;
87909
+ try {
87910
+ const facts = await resolve({
87911
+ deviceId,
87912
+ finishedAtMs: subject.timestamp
87913
+ });
87914
+ if (facts.durationMs === null || facts.energyKwh === null) this.deps.logger.debug("appliance cycle part omitted — unknown", {
87915
+ tags: { deviceId },
87916
+ meta: {
87917
+ duration: facts.durationMs === null ? "unknown" : "known",
87918
+ energy: facts.energyKwh === null ? "unknown" : "known"
87919
+ }
87920
+ });
87921
+ return facts;
87922
+ } catch (err) {
87923
+ this.deps.logger.debug("appliance cycle facts failed — parts omitted", {
87924
+ tags: { deviceId },
87925
+ meta: { error: err instanceof Error ? err.message : String(err) }
87926
+ });
87927
+ return NC_UNKNOWN_CYCLE_FACTS;
87928
+ }
87929
+ }
87930
+ /**
87775
87931
  * Builds the notification AND returns the artefact ids its media resolve
87776
87932
  * indexed — see {@link withArtifactUrls}. A tuple rather than a field on
87777
87933
  * `NcNotificationInput`: the ids are internal (outbox provenance), never
@@ -87783,7 +87939,8 @@ var NcDispatcher = class {
87783
87939
  const deviceName = await notificationDeviceName(entry, this.deps.getDeviceName);
87784
87940
  const alarmSource = systemEvent?.alarmSource;
87785
87941
  const zoneLabels = systemEvent === void 0 ? await resolveZoneLabels(this.deps.getZoneNames, subject.deviceId, subject.zones) : alarmSource?.zones !== void 0 && alarmSource.zones.length > 0 ? await resolveZoneLabels(this.deps.getZoneNames, alarmSource.deviceId, alarmSource.zones) : [];
87786
- const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels);
87942
+ const cycleFacts = await this.applianceCycleFacts(entry);
87943
+ const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
87787
87944
  this.warnDeprecatedVars(entry);
87788
87945
  const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
87789
87946
  const title = renderRowTitle(this.texts, entry, vars);
@@ -88950,7 +89107,7 @@ function systemEventParamsForTemplate(params) {
88950
89107
  const { form: _form, attention: _attention, ...rest } = params;
88951
89108
  return rest;
88952
89109
  }
88953
- function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
89110
+ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone, cycle) {
88954
89111
  const subject = entry.payload.subject;
88955
89112
  const occupancy = subject.occupancy;
88956
89113
  const systemEvent = subject.systemEvent;
@@ -88986,6 +89143,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
88986
89143
  hitPercent: subject.audio?.hitPercent !== void 0 ? `${Math.round(subject.audio.hitPercent)}` : "",
88987
89144
  db: subject.audio?.peakDbfs !== void 0 ? `${Math.round(subject.audio.peakDbfs)}` : "",
88988
89145
  eventType: subject.eventType ?? "",
89146
+ ...cycle !== void 0 ? ncApplianceCycleVars(texts, cycle) : NC_EMPTY_CYCLE_VARS,
88989
89147
  rule: entry.payload.ruleName,
88990
89148
  count: occupancy !== void 0 ? `${occupancy.count}` : group !== void 0 && group.members.length > 1 ? `${group.members.length}` : "",
88991
89149
  capacity: occupancy !== void 0 ? `${occupancy.capacity}` : "",
@@ -89012,6 +89170,7 @@ function bodyKeyFor(entry) {
89012
89170
  const subject = entry.payload.subject;
89013
89171
  if (subject.systemEvent !== void 0) return subject.systemEvent.bodyKey ?? "";
89014
89172
  if (subject.occupancy !== void 0) return "occupancy.body";
89173
+ if (subject.applianceCycle !== void 0) return "appliance.finished.body";
89015
89174
  if (subject.packagePhase === "delivered" && subject.packagePossible === true) return "package.delivered.possible.body";
89016
89175
  if (subject.packagePhase !== void 0) return `package.${subject.packagePhase}.body`;
89017
89176
  return entry.recordKind === "track-end" ? "detection.body.trackEnd" : "detection.body";
@@ -89049,7 +89208,7 @@ function renderRowTitle(texts, entry, vars) {
89049
89208
  const systemEvent = entry.payload.subject.systemEvent;
89050
89209
  const intrusion = entry.payload.alarmIntrusion;
89051
89210
  const count = systemEvent?.textCount;
89052
- return renderRowText(texts, {
89211
+ const rendered = renderRowText(texts, {
89053
89212
  template: intrusion !== void 0 ? void 0 : entry.payload.template?.title,
89054
89213
  key: titleKeyFor(entry),
89055
89214
  vars: intrusion !== void 0 ? {
@@ -89059,11 +89218,13 @@ function renderRowTitle(texts, entry, vars) {
89059
89218
  ...count !== void 0 ? { count } : {},
89060
89219
  frozen: systemEvent?.title ?? entry.payload.ruleName
89061
89220
  });
89221
+ return titleKeyFor(entry) === "appliance.finished.title" && entry.payload.template?.title === void 0 ? ncSentenceCaseBody(rendered) : rendered;
89062
89222
  }
89063
89223
  /** The default TITLE key — same shape, same pre-catalog reasoning. */
89064
89224
  function titleKeyFor(entry) {
89065
89225
  const systemEvent = entry.payload.subject.systemEvent;
89066
89226
  if (systemEvent !== void 0) return systemEvent.titleKey ?? "";
89227
+ if (entry.payload.subject.applianceCycle !== void 0) return "appliance.finished.title";
89067
89228
  if (entry.payload.alarmIntrusion !== void 0) return "alarm.intrusion.title";
89068
89229
  const packagePhase = entry.payload.subject.packagePhase;
89069
89230
  if (packagePhase === "delivered" && entry.payload.subject.packagePossible === true) return "package.delivered.possible.title";
@@ -102135,6 +102296,7 @@ function buildEntries(host, rule, subject, kind, matchedOn, userTargets = [], or
102135
102296
  ...subject.audioWindow.peakDbfs !== void 0 ? { peakDbfs: subject.audioWindow.peakDbfs } : {}
102136
102297
  } } : {},
102137
102298
  ...subject.eventType !== void 0 ? { eventType: subject.eventType } : {},
102299
+ ...applianceCycleOf(subject),
102138
102300
  ...subject.systemEvent !== void 0 ? { systemEvent: subject.systemEvent } : {}
102139
102301
  }
102140
102302
  };
@@ -109872,6 +110034,7 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
109872
110034
  }));
109873
110035
  },
109874
110036
  getDeviceName,
110037
+ resolveApplianceCycleFacts: this.deps.resolveApplianceCycleFacts,
109875
110038
  getDeviceLocation,
109876
110039
  buildActions: (input) => this.notificationCenter?.mintButtons(input) ?? Promise.resolve([])
109877
110040
  },
@@ -123743,7 +123906,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
123743
123906
  buildTimelapsePorts: (api, stores) => this.buildTimelapsePorts(api, stores),
123744
123907
  buildSummaryPorts: (api, stores) => this.buildSummaryPorts(api, stores),
123745
123908
  wireAlarmPanel: (center) => this.wireAlarmPanel(center),
123746
- serveNcActionPlane: (center) => this.serveNcActionPlane(center)
123909
+ serveNcActionPlane: (center) => this.serveNcActionPlane(center),
123910
+ resolveApplianceCycleFacts: applianceCycleFactsFromRuns({
123911
+ newestRun: async (deviceId) => {
123912
+ return (await this.ctx.api.deviceRuns.listRuns.query({
123913
+ deviceId,
123914
+ limit: 1
123915
+ })).runs[0] ?? null;
123916
+ },
123917
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
123918
+ })
123747
123919
  });
123748
123920
  return this.notificationCenterBootInstance;
123749
123921
  }
@@ -1,4 +1,4 @@
1
- import { $ as MAX_TRACK_RELATIONS, $i as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, $n as bundledCamstack, $r as normalizeNcAction, $t as NcTemplateVarDescriptorSchema, A as DETECTION_MACRO_CLASSES, Aa as strictObject, Ai as sceneMonitorCapability, An as TrackPuritySchema, Ar as isAlertEpisodeOpen, At as NcAlarmConfigSchema, B as EXPORT_DENSE_MAX_RANGES, Bi as sunTimes, Bn as alertEpisodeKind, Br as isLabelSupplyStepId, Bt as NcRuleSchema, C as COCO_TO_MACRO, Ca as discriminatedUnion, Ci as retrainRevisionConflictMessage, Cn as SetTrackRelationInputSchema, Cr as eventKindLabel, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as object, Di as ruleDeviceCompatCapQueries, Dn as TimelapseRulePatchSchema, Dr as gifDeriveSpeed, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as number, Ei as ruleBelongsToAlarm$1, En as TimelapseRuleInputSchema, Er as failureContributionCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as unknown, Fi as storageOccupancyCapability, Fn as activateAlarmProfile, Fr as isDetectionMacroClass, Ft as NcConditionDescriptorSchema, G as LabelAttributionSchema, Gi as systemEventSummaryScheduleOf, Gn as applySecureOnArmEdit, Gr as isSystemDelivery, Gt as NcSnoozeInputSchema, H as FULL_IMAGE_BBOX, Hi as systemEventDeliveryProblems, Hn as applyBlockingEdit, Hr as isRetiredClearKey, Ht as NcRuleUpdateInputSchema, I as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Ia as DeviceType, Ii as subKindsOf, In as addonWidgetsSourceCapability, Ir as isDrawableSubjectPath, It as NcConditionsSchema, J as LockStateSchema, Ji as templateVarsFor, Jn as audioMetricsCapability, Jr as kebabToCamel, Jt as NcStateSummarySectionIdSchema, K as ListTrackRelationsInputSchema, Ki as systemEventSummarySplitOf, Kn as armRemovalReconcile, Kr as isSystemEventSummaryRule, Kt as NcSnoozeSchema, L as EVENT_DENSITY_BATCH_MAX, La as errMsg, Li as subjectPathSampleFromBox, Ln as alarmDeviceSecureOnArm, Lr as isEventKindLabelKey, Lt as NcRuleActionSchema, M as DeclaredDevices, Ma as templateLiteral, Mi as sensorRaisedOf, Mn as TrackRelationSchema, Mr as isAppLinkBase, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as tuple, Ni as servedTemplateVars, Nn as TrackSideSchema, Nr as isAudioClassName, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as partialRecord, Oi as ruleGroupingApplies, On as TimelapseRuleSchema, Or as gifFromMp4Args, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as union, Pi as startMoverUnderClearance, Pn as TrackSourceSchema, Pr as isDefaultAlarmProfileId, Pt as NcAlarmSettingsPatchSchema, Q as MAX_TRACK_PURITIES, Qi as zoneAnalyticsCapability, Qn as buildViewerLink, Qr as ncMediaPolicyError, Qt as NcTemplatePreviewSchema, R as EVENT_KIND_BY_CAP, Ra as EventCategory, Ri as subjectPathSvgPoints, Rn as alarmPanelCapability, Rr as isEventOwnerType, Rt as NcRuleInputSchema, S as BucketAccumulator, Sa as boolean, Sn as SetTrackPurityResultSchema, Sr as ensureAlarmProfiles, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as looseObject, Ti as ruleAutoTriggersAlarm, Tn as TRACK_RELATIONS_DEFAULT_LIMIT, Tr as faceGalleryCapability, Tt as NC_SNOOZE_MAX_MINUTES, U as FailureCounters, Ui as systemEventFamilyProblems, Un as applyModeDelaysEdits, Ur as isRootSuppliableStepId, Ut as NcScheduleSchema, V as FIRST_LEVEL_MACRO_CLASSES, Vi as syncActiveAlarmProfileSettings, Vn as alertEpisodePayload, Vr as isMediaOwnerType, Vt as NcRuleTargetSchema, W as LABEL_SUPPLY_MACRO, Wi as systemEventFilterApplies, Wn as applyNonBlockingEdit, Wr as isScheduleActive, Wt as NcSharedSequenceSchema, X as MAX_BIRTH_DECISION_RECORDS, Xi as viewerLinkBaseOf, Xn as buildEventKindDescriptor, Xr as mayWriteToLocation, Xt as NcTaxonomySchema, Y as MAX_ARCHIVED_DEBUG_NOTES, Yi as vectorDimFromBase64, Yn as audioModeOf, Yr as linkedStepOf, Yt as NcSystemEventKindSchema, Z as MAX_BIRTH_LATENCY_PROXY_MS, Zi as withSystemEventClasses, Zn as buildSubjectPath, Zr as memoizePinnedFfmpeg, Zt as NcTemplatePreviewInputSchema, _ as AlarmArmModeSchema, _i as resolveDeliveryParams, _n as SCENE_DEFAULT_UNCOVERED_POLICY, _t as NC_CONFIRM_DEFAULT_TIMEOUT_MS, a as CLIP_EMBEDDING_STEP_ID, aa as CamProfileSchema, ai as paletteResultSchema, an as RECORDING_SOURCE_CAMSTACK_ADDON, ar as coreBlockIdFromAddonId, at as NC_ALARM_DELAY_FIELDS, b as BaseDevice, ba as _enum, bi as resolveModeNotifyPolicy, bn as SceneMonitorSchema, bt as NC_DEFAULT_SNOOZE_MINUTES, c as readClusterModelPinStrict, ca as RECORD_QUERY_MAX, ci as pickFaceRescoreIntervalMinutes, cn as RetrainOriginSchema, cr as deleteAlarmProfile, ct as NC_ALARM_SYSTEM_EVENT_KINDS, d as APPEARANCE_BIN_COUNT, da as defineAddonMigrations, di as projectSubjectPath, dn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, dr as deriveRecordingMode, dt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, ea as canonicalHash, ei as normalizeNcCapTarget, en as OPS_LOG_DEFAULT_LIMIT, er as calendarEventLocalSpan, et as MOTION_CLOSE_AFTER_MS, f as APPEARANCE_BLOCKS, fa as defineCustomActions, fi as readDeviceStateFrom, fn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, fr as describeSystemEventDeliveryProblem, ft as NC_CLIP_POST_ROLL_DEFAULT_SEC, g as ActionTargetResolver, ga as nodePin, gi as resolveClipSearchMinScore, gn as SCENE_DEFAULT_QUIET_SECONDS, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as isDeviceScopedCap, hi as renameAlarmProfile, hn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, hr as deviceRemovalHookCapability, ht as NC_CONDITION_CATALOG, ia as CAP_ADVANCE_FIELDS, ii as paintHex, in as RECORDING_EXPORT_MAX_READ_BYTES, ir as consumableLowPercentOf, it as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, j as DETECTION_PIPELINE_CAP_NAME, ja as string, ji as secureActuationFor, jn as TrackRelationKindSchema, jr as isAlertEpisodeSeverity, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as record, ki as ruleKindOf, kn as TrackPlacementSchema, kr as isAlarmControllableCap, kt as NC_TEMPLATE_VARS, l as resolveClusterModelPin, la as createEvent, li as pipelineAnalyticsCapability, ln as RetrainStatusSchema, lr as deriveDetailCropRect, lt as NC_ALARM_TRIGGERED_MAX_SEC, m as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ma as hydrateSchema, mi as readTimelapseGeneratedAt, mn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, mr as deviceAttentionKind, mt as NC_CLIP_RETENTION_MAX_SEC, na as isSourceCap, ni as notificationRulesCapability, nn as PackageLlmVerdictSchema, nr as cloneAlarmSettings, nt as MotionTriggeredBySchema, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as InvalidRecordError, oi as parseAlertEpisodePayload, on as RebuildObjectEmbeddingsRefusalSchema, or as cosineSimilarity$1, ot as NC_ALARM_DELAY_MAX_SEC, p as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, pa as defineRecordKinds, pi as readMotionTriggeredBy, pn as SCENE_DEFAULT_ANCHOR_THRESHOLD, pr as describeSystemEventFamilyProblem, pt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, q as ListTrackRelationsResultSchema, qi as templateCarriesSummary, qn as assertTimelapseCadences, qr as isVectorIndexDimConflict, qt as NcSnoozeSuppressedSchema, ra as BaseAddon, ri as occupancyScope, rr as consumableFormOf, rt as NC_ALARM_DEFAULT_PROFILE_ID, s as FACE_EMBEDDING_STEP_ID, sa as RECORD_MAX_DATA_BYTES, sn as RetrainDismissedSchema, sr as createAlarmProfile, st as NC_ALARM_NOTIFY_FIELDS, t as BUILTIN_CLIP_MODELS, ta as evaluateSensorEdge, ti as normalizeNcMediaPolicy, tn as OpsLogEntrySchema, tr as classifyRetrainReview, tt as MediaFileKindEnum, u as ACTION_TARGET_INVALIDATION_EVENTS, ua as customAction, ui as plateGalleryCapability, un as RetrainVerdictSubjectSchema, ur as derivePostTriggeredAction, ut as NC_AUDIO_CONFIRM_HITS_DEFAULT, v as ArchivedDebugNoteSchema, va as sleep$1, vi as resolveLocationMode, vn as SCENE_DIVERGED, vr as embeddingRefusalOf, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as literal, wi as rootSupplyServes, wn as TIMELAPSE_DENSE_FLOOR_SEC, wr as evictionPolicyOfLocation, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as array, xi as resolveNcMediaImage, xn as SetTrackPurityInputSchema, xr as encodeVectorBase64, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as ZodObject, yi as resolveModeDelays, yn as SCENE_RESET_RECAPTURES, yt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, z as EVENT_OWNER_TYPES, zi as summarizeReview, zn as alertEpisodeId, zr as isKnownTemplateVar, zt as NcRulePatchSchema } from "../clip-model-registry-CVajwmEk.mjs";
1
+ import { $ as MAX_TRACK_RELATIONS, $i as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, $n as bundledCamstack, $r as normalizeNcAction, $t as NcTemplateVarDescriptorSchema, A as DETECTION_MACRO_CLASSES, Aa as strictObject, Ai as sceneMonitorCapability, An as TrackPuritySchema, Ar as isAlertEpisodeOpen, At as NcAlarmConfigSchema, B as EXPORT_DENSE_MAX_RANGES, Bi as sunTimes, Bn as alertEpisodeKind, Br as isLabelSupplyStepId, Bt as NcRuleSchema, C as COCO_TO_MACRO, Ca as discriminatedUnion, Ci as retrainRevisionConflictMessage, Cn as SetTrackRelationInputSchema, Cr as eventKindLabel, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as object, Di as ruleDeviceCompatCapQueries, Dn as TimelapseRulePatchSchema, Dr as gifDeriveSpeed, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as number, Ei as ruleBelongsToAlarm$1, En as TimelapseRuleInputSchema, Er as failureContributionCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as unknown, Fi as storageOccupancyCapability, Fn as activateAlarmProfile, Fr as isDetectionMacroClass, Ft as NcConditionDescriptorSchema, G as LabelAttributionSchema, Gi as systemEventSummaryScheduleOf, Gn as applySecureOnArmEdit, Gr as isSystemDelivery, Gt as NcSnoozeInputSchema, H as FULL_IMAGE_BBOX, Hi as systemEventDeliveryProblems, Hn as applyBlockingEdit, Hr as isRetiredClearKey, Ht as NcRuleUpdateInputSchema, I as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Ia as DeviceType, Ii as subKindsOf, In as addonWidgetsSourceCapability, Ir as isDrawableSubjectPath, It as NcConditionsSchema, J as LockStateSchema, Ji as templateVarsFor, Jn as audioMetricsCapability, Jr as kebabToCamel, Jt as NcStateSummarySectionIdSchema, K as ListTrackRelationsInputSchema, Ki as systemEventSummarySplitOf, Kn as armRemovalReconcile, Kr as isSystemEventSummaryRule, Kt as NcSnoozeSchema, L as EVENT_DENSITY_BATCH_MAX, La as errMsg, Li as subjectPathSampleFromBox, Ln as alarmDeviceSecureOnArm, Lr as isEventKindLabelKey, Lt as NcRuleActionSchema, M as DeclaredDevices, Ma as templateLiteral, Mi as sensorRaisedOf, Mn as TrackRelationSchema, Mr as isAppLinkBase, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as tuple, Ni as servedTemplateVars, Nn as TrackSideSchema, Nr as isAudioClassName, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as partialRecord, Oi as ruleGroupingApplies, On as TimelapseRuleSchema, Or as gifFromMp4Args, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as union, Pi as startMoverUnderClearance, Pn as TrackSourceSchema, Pr as isDefaultAlarmProfileId, Pt as NcAlarmSettingsPatchSchema, Q as MAX_TRACK_PURITIES, Qi as zoneAnalyticsCapability, Qn as buildViewerLink, Qr as ncMediaPolicyError, Qt as NcTemplatePreviewSchema, R as EVENT_KIND_BY_CAP, Ra as EventCategory, Ri as subjectPathSvgPoints, Rn as alarmPanelCapability, Rr as isEventOwnerType, Rt as NcRuleInputSchema, S as BucketAccumulator, Sa as boolean, Sn as SetTrackPurityResultSchema, Sr as ensureAlarmProfiles, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as looseObject, Ti as ruleAutoTriggersAlarm, Tn as TRACK_RELATIONS_DEFAULT_LIMIT, Tr as faceGalleryCapability, Tt as NC_SNOOZE_MAX_MINUTES, U as FailureCounters, Ui as systemEventFamilyProblems, Un as applyModeDelaysEdits, Ur as isRootSuppliableStepId, Ut as NcScheduleSchema, V as FIRST_LEVEL_MACRO_CLASSES, Vi as syncActiveAlarmProfileSettings, Vn as alertEpisodePayload, Vr as isMediaOwnerType, Vt as NcRuleTargetSchema, W as LABEL_SUPPLY_MACRO, Wi as systemEventFilterApplies, Wn as applyNonBlockingEdit, Wr as isScheduleActive, Wt as NcSharedSequenceSchema, X as MAX_BIRTH_DECISION_RECORDS, Xi as viewerLinkBaseOf, Xn as buildEventKindDescriptor, Xr as mayWriteToLocation, Xt as NcTaxonomySchema, Y as MAX_ARCHIVED_DEBUG_NOTES, Yi as vectorDimFromBase64, Yn as audioModeOf, Yr as linkedStepOf, Yt as NcSystemEventKindSchema, Z as MAX_BIRTH_LATENCY_PROXY_MS, Zi as withSystemEventClasses, Zn as buildSubjectPath, Zr as memoizePinnedFfmpeg, Zt as NcTemplatePreviewInputSchema, _ as AlarmArmModeSchema, _i as resolveDeliveryParams, _n as SCENE_DEFAULT_UNCOVERED_POLICY, _t as NC_CONFIRM_DEFAULT_TIMEOUT_MS, a as CLIP_EMBEDDING_STEP_ID, aa as CamProfileSchema, ai as paletteResultSchema, an as RECORDING_SOURCE_CAMSTACK_ADDON, ar as coreBlockIdFromAddonId, at as NC_ALARM_DELAY_FIELDS, b as BaseDevice, ba as _enum, bi as resolveModeNotifyPolicy, bn as SceneMonitorSchema, bt as NC_DEFAULT_SNOOZE_MINUTES, c as readClusterModelPinStrict, ca as RECORD_QUERY_MAX, ci as pickFaceRescoreIntervalMinutes, cn as RetrainOriginSchema, cr as deleteAlarmProfile, ct as NC_ALARM_SYSTEM_EVENT_KINDS, d as APPEARANCE_BIN_COUNT, da as defineAddonMigrations, di as projectSubjectPath, dn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, dr as deriveRecordingMode, dt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, ea as canonicalHash, ei as normalizeNcCapTarget, en as OPS_LOG_DEFAULT_LIMIT, er as calendarEventLocalSpan, et as MOTION_CLOSE_AFTER_MS, f as APPEARANCE_BLOCKS, fa as defineCustomActions, fi as readDeviceStateFrom, fn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, fr as describeSystemEventDeliveryProblem, ft as NC_CLIP_POST_ROLL_DEFAULT_SEC, g as ActionTargetResolver, ga as nodePin, gi as resolveClipSearchMinScore, gn as SCENE_DEFAULT_QUIET_SECONDS, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as isDeviceScopedCap, hi as renameAlarmProfile, hn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, hr as deviceRemovalHookCapability, ht as NC_CONDITION_CATALOG, ia as CAP_ADVANCE_FIELDS, ii as paintHex, in as RECORDING_EXPORT_MAX_READ_BYTES, ir as consumableLowPercentOf, it as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, j as DETECTION_PIPELINE_CAP_NAME, ja as string, ji as secureActuationFor, jn as TrackRelationKindSchema, jr as isAlertEpisodeSeverity, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as record, ki as ruleKindOf, kn as TrackPlacementSchema, kr as isAlarmControllableCap, kt as NC_TEMPLATE_VARS, l as resolveClusterModelPin, la as createEvent, li as pipelineAnalyticsCapability, ln as RetrainStatusSchema, lr as deriveDetailCropRect, lt as NC_ALARM_TRIGGERED_MAX_SEC, m as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ma as hydrateSchema, mi as readTimelapseGeneratedAt, mn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, mr as deviceAttentionKind, mt as NC_CLIP_RETENTION_MAX_SEC, na as isSourceCap, ni as notificationRulesCapability, nn as PackageLlmVerdictSchema, nr as cloneAlarmSettings, nt as MotionTriggeredBySchema, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as InvalidRecordError, oi as parseAlertEpisodePayload, on as RebuildObjectEmbeddingsRefusalSchema, or as cosineSimilarity$1, ot as NC_ALARM_DELAY_MAX_SEC, p as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, pa as defineRecordKinds, pi as readMotionTriggeredBy, pn as SCENE_DEFAULT_ANCHOR_THRESHOLD, pr as describeSystemEventFamilyProblem, pt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, q as ListTrackRelationsResultSchema, qi as templateCarriesSummary, qn as assertTimelapseCadences, qr as isVectorIndexDimConflict, qt as NcSnoozeSuppressedSchema, ra as BaseAddon, ri as occupancyScope, rr as consumableFormOf, rt as NC_ALARM_DEFAULT_PROFILE_ID, s as FACE_EMBEDDING_STEP_ID, sa as RECORD_MAX_DATA_BYTES, sn as RetrainDismissedSchema, sr as createAlarmProfile, st as NC_ALARM_NOTIFY_FIELDS, t as BUILTIN_CLIP_MODELS, ta as evaluateSensorEdge, ti as normalizeNcMediaPolicy, tn as OpsLogEntrySchema, tr as classifyRetrainReview, tt as MediaFileKindEnum, u as ACTION_TARGET_INVALIDATION_EVENTS, ua as customAction, ui as plateGalleryCapability, un as RetrainVerdictSubjectSchema, ur as derivePostTriggeredAction, ut as NC_AUDIO_CONFIRM_HITS_DEFAULT, v as ArchivedDebugNoteSchema, va as sleep$1, vi as resolveLocationMode, vn as SCENE_DIVERGED, vr as embeddingRefusalOf, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as literal, wi as rootSupplyServes, wn as TIMELAPSE_DENSE_FLOOR_SEC, wr as evictionPolicyOfLocation, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as array, xi as resolveNcMediaImage, xn as SetTrackPurityInputSchema, xr as encodeVectorBase64, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as ZodObject, yi as resolveModeDelays, yn as SCENE_RESET_RECAPTURES, yt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, z as EVENT_OWNER_TYPES, zi as summarizeReview, zn as alertEpisodeId, zr as isKnownTemplateVar, zt as NcRulePatchSchema } from "../clip-model-registry-ELaSS9jq.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";
@@ -8942,6 +8942,17 @@ var en_default = {
8942
8942
  "detection.subject.identity": "{{label}}",
8943
8943
  "detection.subject.plate": "{{classNoun}} {{label}}",
8944
8944
  "detection.inZones": " in {{zones}}",
8945
+ "appliance.finished.title": "{{device}} finished",
8946
+ "appliance.finished.body": "{{device}} finished{{afterDuration}}{{usedEnergy}}",
8947
+ "appliance.finished.duration": " after {{duration}}",
8948
+ "appliance.finished.energy": " and used {{energy}}",
8949
+ "appliance.duration.minutes": {
8950
+ "one": "{{minutes}} minute",
8951
+ "other": "{{minutes}} minutes"
8952
+ },
8953
+ "appliance.duration.hours": "{{hours}} h",
8954
+ "appliance.duration.hoursMinutes": "{{hours}} h {{minutes}} min",
8955
+ "appliance.energy.value": "{{value}} kWh",
8945
8956
  "class.person": {
8946
8957
  "one": "person",
8947
8958
  "other": "people"
@@ -9348,6 +9359,17 @@ var it_default = {
9348
9359
  "detection.subject.identity": "{{label}}",
9349
9360
  "detection.subject.plate": "{{classNoun}} {{label}}",
9350
9361
  "detection.inZones": " in {{zones}}",
9362
+ "appliance.finished.title": "{{device}} ha terminato",
9363
+ "appliance.finished.body": "{{device}} ha terminato{{afterDuration}}{{usedEnergy}}",
9364
+ "appliance.finished.duration": " dopo {{duration}}",
9365
+ "appliance.finished.energy": " e ha consumato {{energy}}",
9366
+ "appliance.duration.minutes": {
9367
+ "one": "{{minutes}} minuto",
9368
+ "other": "{{minutes}} minuti"
9369
+ },
9370
+ "appliance.duration.hours": "{{hours}} h",
9371
+ "appliance.duration.hoursMinutes": "{{hours}} h {{minutes}} min",
9372
+ "appliance.energy.value": "{{value}} kWh",
9351
9373
  "class.person": {
9352
9374
  "one": "persona",
9353
9375
  "other": "persone"
@@ -68608,6 +68630,109 @@ function createAnalyticsWidgetsProvider() {
68608
68630
  ] };
68609
68631
  }
68610
68632
  //#endregion
68633
+ //#region src/notification-center/appliance-cycle.ts
68634
+ var NC_UNKNOWN_CYCLE_FACTS = {
68635
+ durationMs: null,
68636
+ energyKwh: null
68637
+ };
68638
+ var NC_EMPTY_CYCLE_VARS = {
68639
+ duration: "",
68640
+ energy: "",
68641
+ afterDuration: "",
68642
+ usedEnergy: ""
68643
+ };
68644
+ /** `52 minutes` / `1 h 12 min` / `2 h`; `''` below one minute (never "0 minutes"). */
68645
+ function durationText(texts, durationMs) {
68646
+ if (durationMs === null || !Number.isFinite(durationMs)) return "";
68647
+ const totalMinutes = Math.round(durationMs / 6e4);
68648
+ if (totalMinutes < 1) return "";
68649
+ if (totalMinutes < 60) return texts.text({
68650
+ key: "appliance.duration.minutes",
68651
+ count: totalMinutes,
68652
+ vars: { minutes: `${totalMinutes}` }
68653
+ });
68654
+ const hours = Math.floor(totalMinutes / 60);
68655
+ const minutes = totalMinutes % 60;
68656
+ return minutes === 0 ? texts.text({
68657
+ key: "appliance.duration.hours",
68658
+ vars: { hours: `${hours}` }
68659
+ }) : texts.text({
68660
+ key: "appliance.duration.hoursMinutes",
68661
+ vars: {
68662
+ hours: `${hours}`,
68663
+ minutes: `${minutes}`
68664
+ }
68665
+ });
68666
+ }
68667
+ /** `0,84 kWh` (it) / `0.84 kWh` (en); `''` when unmeasured. */
68668
+ function energyText(texts, energyKwh) {
68669
+ if (energyKwh === null || !Number.isFinite(energyKwh) || energyKwh < 0) return "";
68670
+ const value = energyKwh.toLocaleString(texts.language, {
68671
+ minimumFractionDigits: 2,
68672
+ maximumFractionDigits: 2
68673
+ });
68674
+ return texts.text({
68675
+ key: "appliance.energy.value",
68676
+ vars: { value }
68677
+ });
68678
+ }
68679
+ /** The four variables of a finished cycle, fragments rendered from scalars only. */
68680
+ function ncApplianceCycleVars(texts, facts) {
68681
+ const duration = durationText(texts, facts.durationMs);
68682
+ const energy = energyText(texts, facts.energyKwh);
68683
+ return {
68684
+ duration,
68685
+ energy,
68686
+ afterDuration: duration === "" ? "" : texts.text({
68687
+ key: "appliance.finished.duration",
68688
+ vars: { duration }
68689
+ }),
68690
+ usedEnergy: energy === "" ? "" : texts.text({
68691
+ key: "appliance.finished.energy",
68692
+ vars: { energy }
68693
+ })
68694
+ };
68695
+ }
68696
+ /**
68697
+ * The frozen reference a FINISHED appliance cycle row carries (`{}` for every
68698
+ * other row): kind `appliance-cycle`, edge `cleared`. The appliance is the
68699
+ * sensor itself — a linked camera is only where the row was attributed.
68700
+ */
68701
+ function applianceCycleOf(subject) {
68702
+ if (subject.sensorKind !== "appliance-cycle" || subject.sensorRaised !== false) return {};
68703
+ return { applianceCycle: { deviceId: subject.linkedSensorId ?? subject.sourceDeviceId ?? subject.deviceId } };
68704
+ }
68705
+ //#endregion
68706
+ //#region src/notification-center/appliance-cycle-runs.ts
68707
+ var CYCLE_FACTS_WAIT_MS = 3e3;
68708
+ var CYCLE_FACTS_POLL_MS = 250;
68709
+ /** A closed run belongs to this finish when it ended within this much of it. */
68710
+ var CYCLE_RUN_MATCH_MS = 10 * 6e4;
68711
+ function factsOfClosed(run, finishedAtMs) {
68712
+ if (run.endedAt === null || Math.abs(run.endedAt - finishedAtMs) > CYCLE_RUN_MATCH_MS) return NC_UNKNOWN_CYCLE_FACTS;
68713
+ return {
68714
+ durationMs: run.durationMs,
68715
+ energyKwh: run.detail.energyKwh
68716
+ };
68717
+ }
68718
+ function applianceCycleFactsFromRuns(ports) {
68719
+ return async ({ deviceId, finishedAtMs }) => {
68720
+ let waited = 0;
68721
+ for (;;) {
68722
+ const run = await ports.newestRun(deviceId);
68723
+ if (run === null) return NC_UNKNOWN_CYCLE_FACTS;
68724
+ if (run.state === "closed") return factsOfClosed(run, finishedAtMs);
68725
+ if (run.startedAt > finishedAtMs) return NC_UNKNOWN_CYCLE_FACTS;
68726
+ if (waited >= CYCLE_FACTS_WAIT_MS) return {
68727
+ durationMs: finishedAtMs - run.startedAt,
68728
+ energyKwh: null
68729
+ };
68730
+ await ports.sleep(CYCLE_FACTS_POLL_MS);
68731
+ waited += CYCLE_FACTS_POLL_MS;
68732
+ }
68733
+ };
68734
+ }
68735
+ //#endregion
68611
68736
  //#region src/pipeline-analytics/runtime/wire-subscriptions.ts
68612
68737
  /**
68613
68738
  * Every category the Notification Center's system-event intake can turn into a
@@ -70569,6 +70694,7 @@ Object.freeze({
70569
70694
  "page": 1,
70570
70695
  "pageSize": 50
70571
70696
  } },
70697
+ "device-runs": { "listRuns": { "limit": 50 } },
70572
70698
  "llm": { "upsertProfile": { "profile": { "__nested__": {
70573
70699
  "timeoutMs": 6e4,
70574
70700
  "connectTimeoutMs": 1e4,
@@ -87677,6 +87803,36 @@ var NcDispatcher = class {
87677
87803
  });
87678
87804
  }
87679
87805
  /**
87806
+ * The facts of a finished appliance cycle; unknown on every other row and on
87807
+ * any failure (a part that cannot be read is omitted, never a blocked send).
87808
+ */
87809
+ async applianceCycleFacts(entry) {
87810
+ const subject = entry.payload.subject;
87811
+ const resolve = this.deps.resolveApplianceCycleFacts;
87812
+ const deviceId = subject.applianceCycle?.deviceId;
87813
+ if (resolve === void 0 || deviceId === void 0) return NC_UNKNOWN_CYCLE_FACTS;
87814
+ try {
87815
+ const facts = await resolve({
87816
+ deviceId,
87817
+ finishedAtMs: subject.timestamp
87818
+ });
87819
+ if (facts.durationMs === null || facts.energyKwh === null) this.deps.logger.debug("appliance cycle part omitted — unknown", {
87820
+ tags: { deviceId },
87821
+ meta: {
87822
+ duration: facts.durationMs === null ? "unknown" : "known",
87823
+ energy: facts.energyKwh === null ? "unknown" : "known"
87824
+ }
87825
+ });
87826
+ return facts;
87827
+ } catch (err) {
87828
+ this.deps.logger.debug("appliance cycle facts failed — parts omitted", {
87829
+ tags: { deviceId },
87830
+ meta: { error: err instanceof Error ? err.message : String(err) }
87831
+ });
87832
+ return NC_UNKNOWN_CYCLE_FACTS;
87833
+ }
87834
+ }
87835
+ /**
87680
87836
  * Builds the notification AND returns the artefact ids its media resolve
87681
87837
  * indexed — see {@link withArtifactUrls}. A tuple rather than a field on
87682
87838
  * `NcNotificationInput`: the ids are internal (outbox provenance), never
@@ -87688,7 +87844,8 @@ var NcDispatcher = class {
87688
87844
  const deviceName = await notificationDeviceName(entry, this.deps.getDeviceName);
87689
87845
  const alarmSource = systemEvent?.alarmSource;
87690
87846
  const zoneLabels = systemEvent === void 0 ? await resolveZoneLabels(this.deps.getZoneNames, subject.deviceId, subject.zones) : alarmSource?.zones !== void 0 && alarmSource.zones.length > 0 ? await resolveZoneLabels(this.deps.getZoneNames, alarmSource.deviceId, alarmSource.zones) : [];
87691
- const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels);
87847
+ const cycleFacts = await this.applianceCycleFacts(entry);
87848
+ const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
87692
87849
  this.warnDeprecatedVars(entry);
87693
87850
  const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
87694
87851
  const title = renderRowTitle(this.texts, entry, vars);
@@ -88855,7 +89012,7 @@ function systemEventParamsForTemplate(params) {
88855
89012
  const { form: _form, attention: _attention, ...rest } = params;
88856
89013
  return rest;
88857
89014
  }
88858
- function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
89015
+ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone, cycle) {
88859
89016
  const subject = entry.payload.subject;
88860
89017
  const occupancy = subject.occupancy;
88861
89018
  const systemEvent = subject.systemEvent;
@@ -88891,6 +89048,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
88891
89048
  hitPercent: subject.audio?.hitPercent !== void 0 ? `${Math.round(subject.audio.hitPercent)}` : "",
88892
89049
  db: subject.audio?.peakDbfs !== void 0 ? `${Math.round(subject.audio.peakDbfs)}` : "",
88893
89050
  eventType: subject.eventType ?? "",
89051
+ ...cycle !== void 0 ? ncApplianceCycleVars(texts, cycle) : NC_EMPTY_CYCLE_VARS,
88894
89052
  rule: entry.payload.ruleName,
88895
89053
  count: occupancy !== void 0 ? `${occupancy.count}` : group !== void 0 && group.members.length > 1 ? `${group.members.length}` : "",
88896
89054
  capacity: occupancy !== void 0 ? `${occupancy.capacity}` : "",
@@ -88917,6 +89075,7 @@ function bodyKeyFor(entry) {
88917
89075
  const subject = entry.payload.subject;
88918
89076
  if (subject.systemEvent !== void 0) return subject.systemEvent.bodyKey ?? "";
88919
89077
  if (subject.occupancy !== void 0) return "occupancy.body";
89078
+ if (subject.applianceCycle !== void 0) return "appliance.finished.body";
88920
89079
  if (subject.packagePhase === "delivered" && subject.packagePossible === true) return "package.delivered.possible.body";
88921
89080
  if (subject.packagePhase !== void 0) return `package.${subject.packagePhase}.body`;
88922
89081
  return entry.recordKind === "track-end" ? "detection.body.trackEnd" : "detection.body";
@@ -88954,7 +89113,7 @@ function renderRowTitle(texts, entry, vars) {
88954
89113
  const systemEvent = entry.payload.subject.systemEvent;
88955
89114
  const intrusion = entry.payload.alarmIntrusion;
88956
89115
  const count = systemEvent?.textCount;
88957
- return renderRowText(texts, {
89116
+ const rendered = renderRowText(texts, {
88958
89117
  template: intrusion !== void 0 ? void 0 : entry.payload.template?.title,
88959
89118
  key: titleKeyFor(entry),
88960
89119
  vars: intrusion !== void 0 ? {
@@ -88964,11 +89123,13 @@ function renderRowTitle(texts, entry, vars) {
88964
89123
  ...count !== void 0 ? { count } : {},
88965
89124
  frozen: systemEvent?.title ?? entry.payload.ruleName
88966
89125
  });
89126
+ return titleKeyFor(entry) === "appliance.finished.title" && entry.payload.template?.title === void 0 ? ncSentenceCaseBody(rendered) : rendered;
88967
89127
  }
88968
89128
  /** The default TITLE key — same shape, same pre-catalog reasoning. */
88969
89129
  function titleKeyFor(entry) {
88970
89130
  const systemEvent = entry.payload.subject.systemEvent;
88971
89131
  if (systemEvent !== void 0) return systemEvent.titleKey ?? "";
89132
+ if (entry.payload.subject.applianceCycle !== void 0) return "appliance.finished.title";
88972
89133
  if (entry.payload.alarmIntrusion !== void 0) return "alarm.intrusion.title";
88973
89134
  const packagePhase = entry.payload.subject.packagePhase;
88974
89135
  if (packagePhase === "delivered" && entry.payload.subject.packagePossible === true) return "package.delivered.possible.title";
@@ -102040,6 +102201,7 @@ function buildEntries(host, rule, subject, kind, matchedOn, userTargets = [], or
102040
102201
  ...subject.audioWindow.peakDbfs !== void 0 ? { peakDbfs: subject.audioWindow.peakDbfs } : {}
102041
102202
  } } : {},
102042
102203
  ...subject.eventType !== void 0 ? { eventType: subject.eventType } : {},
102204
+ ...applianceCycleOf(subject),
102043
102205
  ...subject.systemEvent !== void 0 ? { systemEvent: subject.systemEvent } : {}
102044
102206
  }
102045
102207
  };
@@ -109777,6 +109939,7 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
109777
109939
  }));
109778
109940
  },
109779
109941
  getDeviceName,
109942
+ resolveApplianceCycleFacts: this.deps.resolveApplianceCycleFacts,
109780
109943
  getDeviceLocation,
109781
109944
  buildActions: (input) => this.notificationCenter?.mintButtons(input) ?? Promise.resolve([])
109782
109945
  },
@@ -123648,7 +123811,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
123648
123811
  buildTimelapsePorts: (api, stores) => this.buildTimelapsePorts(api, stores),
123649
123812
  buildSummaryPorts: (api, stores) => this.buildSummaryPorts(api, stores),
123650
123813
  wireAlarmPanel: (center) => this.wireAlarmPanel(center),
123651
- serveNcActionPlane: (center) => this.serveNcActionPlane(center)
123814
+ serveNcActionPlane: (center) => this.serveNcActionPlane(center),
123815
+ resolveApplianceCycleFacts: applianceCycleFactsFromRuns({
123816
+ newestRun: async (deviceId) => {
123817
+ return (await this.ctx.api.deviceRuns.listRuns.query({
123818
+ deviceId,
123819
+ limit: 1
123820
+ })).runs[0] ?? null;
123821
+ },
123822
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
123823
+ })
123652
123824
  });
123653
123825
  return this.notificationCenterBootInstance;
123654
123826
  }
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CwKrui6c.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BvOlyiL9.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.661",
3
+ "version": "1.2.662",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",
@@ -138,7 +138,7 @@
138
138
  }
139
139
  ],
140
140
  "requires": {
141
- "server": ">=1.2.728"
141
+ "server": ">=1.2.729"
142
142
  }
143
143
  },
144
144
  "scripts": {