@camstack/addon-post-analysis 1.2.608 → 1.2.609

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.
@@ -22347,6 +22347,41 @@ function audioDbfsByBucket(events, since, bucketMs) {
22347
22347
  }
22348
22348
  return out;
22349
22349
  }
22350
+ /**
22351
+ * PEAK dBFS per bucket index from device-history points (D431 follow-up).
22352
+ *
22353
+ * The bar answers "was it loud here", so a bucket reports the loudest window
22354
+ * in it. A raw point has `max === value`; a rollup row's `max` is the loudest
22355
+ * point it covers, which a mean would flatten. A bucket with no point is
22356
+ * absent — never zero. A rollup row also credits a held value across a quiet
22357
+ * gap, so its max can echo an earlier measured peak.
22358
+ */
22359
+ function audioPeakByBucket(points, since, bucketMs) {
22360
+ const out = /* @__PURE__ */ new Map();
22361
+ if (bucketMs <= 0) return out;
22362
+ for (const p of points) {
22363
+ if (!Number.isFinite(p.max)) continue;
22364
+ const idx = Math.floor((p.t - since) / bucketMs);
22365
+ if (idx < 0) continue;
22366
+ const prev = out.get(idx);
22367
+ if (prev === void 0 || p.max > prev) out.set(idx, p.max);
22368
+ }
22369
+ return out;
22370
+ }
22371
+ //#endregion
22372
+ //#region src/pipeline-analytics/store/analytics-history-keys.ts
22373
+ /** Declared addon series keys (D850). */
22374
+ var ANALYTICS_HISTORY_BUCKET_SERIES = {
22375
+ motion: "addon:pipeline-analytics/motion",
22376
+ "audio-dbfs": "addon:pipeline-analytics/audio-dbfs",
22377
+ occupancy: "addon:pipeline-analytics/occupancy"
22378
+ };
22379
+ var ANALYTICS_AUDIO_LEVEL_KEY = "addon:pipeline-analytics/audio-level";
22380
+ /** Map the public cap enum to the device-history key (read path). */
22381
+ function historyKeyForLtsSeries(series) {
22382
+ if (series === "battery") return "cap:battery.percentage";
22383
+ return ANALYTICS_HISTORY_BUCKET_SERIES[series];
22384
+ }
22350
22385
  //#endregion
22351
22386
  //#region src/pipeline-analytics/store/event-store.ts
22352
22387
  /**
@@ -22719,6 +22754,24 @@ function eventRowId(logger, operation, deviceId, key) {
22719
22754
  });
22720
22755
  return null;
22721
22756
  }
22757
+ var RAW_RETENTION_MS = 7 * 864e5;
22758
+ var FIVE_MIN_RETENTION_MS = 14 * 864e5;
22759
+ var FIVE_MIN_MS = 3e5;
22760
+ var HOUR_MS$2 = 36e5;
22761
+ /** The producer writes one raw point per window of this length. */
22762
+ var AUDIO_LEVEL_POINT_MS = 1e4;
22763
+ var HISTORY_MAX_POINTS = 5e3;
22764
+ /**
22765
+ * Pick the device-history tier for a density window: raw while the buckets are
22766
+ * finer than the 5m rollup, the window is inside raw retention and its points
22767
+ * fit one query; the 5m rollup while inside its retention; else 1h.
22768
+ */
22769
+ function audioLevelResolution(q, now) {
22770
+ const rawFits = Math.ceil((q.until - q.since) / AUDIO_LEVEL_POINT_MS) <= HISTORY_MAX_POINTS;
22771
+ if (q.bucketMs < FIVE_MIN_MS && rawFits && q.since >= now - RAW_RETENTION_MS) return "raw";
22772
+ if (q.bucketMs < HOUR_MS$2 && q.since >= now - FIVE_MIN_RETENTION_MS) return "5m";
22773
+ return "1h";
22774
+ }
22722
22775
  var EventStore = class EventStore {
22723
22776
  store;
22724
22777
  logger;
@@ -22726,6 +22779,10 @@ var EventStore = class EventStore {
22726
22779
  audioLedger;
22727
22780
  media;
22728
22781
  onMotionSample;
22782
+ audioLevelHistory;
22783
+ nowFn;
22784
+ /** Cameras whose history read already failed once (log once per camera, D315). */
22785
+ levelFallbackLogged = /* @__PURE__ */ new Set();
22729
22786
  constructor(deps) {
22730
22787
  this.store = deps.store;
22731
22788
  this.logger = deps.logger;
@@ -22734,6 +22791,8 @@ var EventStore = class EventStore {
22734
22791
  this.audioLedger = deps.audioLedger ?? wireAudioEventLedger(wiring);
22735
22792
  this.media = deps.media;
22736
22793
  this.onMotionSample = deps.onMotionSample;
22794
+ this.audioLevelHistory = deps.audioLevelHistory;
22795
+ this.nowFn = deps.now ?? (() => Date.now());
22737
22796
  }
22738
22797
  static async declareTracklessEventCollections(store) {
22739
22798
  await store.declareCollection.mutate({
@@ -23462,8 +23521,8 @@ var EventStore = class EventStore {
23462
23521
  const m = motionMap.get(i) ?? 0;
23463
23522
  const o = objectMap.get(i) ?? 0;
23464
23523
  const a = audioMap.get(i) ?? 0;
23465
- if (m + o + a === 0) continue;
23466
23524
  const dbfs = audioDbfsMap.get(i);
23525
+ if (m + o + a === 0 && dbfs === void 0) continue;
23467
23526
  out.push({
23468
23527
  bucketStart: q.since + i * q.bucketMs,
23469
23528
  motion: m,
@@ -23484,6 +23543,28 @@ var EventStore = class EventStore {
23484
23543
  * missing bar is the honest rendering of "not measured" (D431).
23485
23544
  */
23486
23545
  async audioDbfsPerBucket(q) {
23546
+ const history = this.audioLevelHistory;
23547
+ if (history !== void 0) try {
23548
+ const resolution = audioLevelResolution(q, this.nowFn());
23549
+ const points = (await history({
23550
+ deviceId: q.deviceId,
23551
+ key: ANALYTICS_AUDIO_LEVEL_KEY,
23552
+ scope: "",
23553
+ from: q.since,
23554
+ to: q.until,
23555
+ resolution,
23556
+ maxPoints: HISTORY_MAX_POINTS
23557
+ })).series.flatMap((s) => s.points);
23558
+ if (points.length > 0) return audioPeakByBucket(points, q.since, q.bucketMs);
23559
+ } catch (err) {
23560
+ if (!this.levelFallbackLogged.has(q.deviceId)) {
23561
+ this.levelFallbackLogged.add(q.deviceId);
23562
+ this.logger.warn("density: audio level history unreadable — falling back to audio events", {
23563
+ tags: { deviceId: q.deviceId },
23564
+ meta: { error: String(err) }
23565
+ });
23566
+ }
23567
+ }
23487
23568
  try {
23488
23569
  return audioDbfsByBucket((await this.queryAudio({
23489
23570
  deviceId: q.deviceId,
@@ -111555,20 +111636,6 @@ var MigrationMarkerStore = class MigrationMarkerStore {
111555
111636
  };
111556
111637
  };
111557
111638
  //#endregion
111558
- //#region src/pipeline-analytics/store/analytics-history-keys.ts
111559
- /** Declared addon series keys (D850). */
111560
- var ANALYTICS_HISTORY_BUCKET_SERIES = {
111561
- motion: "addon:pipeline-analytics/motion",
111562
- "audio-dbfs": "addon:pipeline-analytics/audio-dbfs",
111563
- occupancy: "addon:pipeline-analytics/occupancy"
111564
- };
111565
- var ANALYTICS_AUDIO_LEVEL_KEY = "addon:pipeline-analytics/audio-level";
111566
- /** Map the public cap enum to the device-history key (read path). */
111567
- function historyKeyForLtsSeries(series) {
111568
- if (series === "battery") return "cap:battery.percentage";
111569
- return ANALYTICS_HISTORY_BUCKET_SERIES[series];
111570
- }
111571
- //#endregion
111572
111639
  //#region src/pipeline-analytics/store/analytics-history-producer.ts
111573
111640
  var BUCKET_SERIES_META = [
111574
111641
  {
@@ -111590,6 +111657,10 @@ var AUDIO_LEVEL_SERIES = {
111590
111657
  displayName: "Audio level (dBFS)",
111591
111658
  unit: "dBFS"
111592
111659
  };
111660
+ /** One audio level point per window; equals device-history's 1 point / 10 s record bound. */
111661
+ var AUDIO_LEVEL_WINDOW_MS = 1e4;
111662
+ /** Stored for a measured-silent window (−∞ dBFS cannot be a history value). */
111663
+ var AUDIO_LEVEL_SILENT_DBFS = -120;
111593
111664
  function refusalCounts(result) {
111594
111665
  const counts = {};
111595
111666
  for (const r of result.refused) counts[r.reason] = (counts[r.reason] ?? 0) + r.count;
@@ -111601,6 +111672,7 @@ var AnalyticsHistoryProducer = class {
111601
111672
  logger;
111602
111673
  nowFn;
111603
111674
  declared = false;
111675
+ levelWindows = /* @__PURE__ */ new Map();
111604
111676
  constructor(deps) {
111605
111677
  this.deviceHistory = deps.deviceHistory;
111606
111678
  this.logger = deps.logger;
@@ -111633,6 +111705,43 @@ var AnalyticsHistoryProducer = class {
111633
111705
  deviceId
111634
111706
  }, scope ?? "", value, at ?? this.nowFn());
111635
111707
  }
111708
+ /**
111709
+ * Feed EVERY analysed audio sample. One point per window of
111710
+ * {@link AUDIO_LEVEL_WINDOW_MS} is written, stamped at the window's first
111711
+ * sample, carrying the window's PEAK dBFS: a 2 s spike inside a stable window
111712
+ * is not averaged away, and stable audio still yields a point each window
111713
+ * (continuity over dedup — the level bar draws a continuous line).
111714
+ *
111715
+ * Measured silence (−∞) is stored at {@link AUDIO_LEVEL_SILENT_DBFS}: it is a
111716
+ * measurement, not an absence (D431); NaN carries no claim and is dropped.
111717
+ * A window closes on the first sample past its end, so no timer is needed and
111718
+ * points are always at least a window apart (the history's 1 point / 10 s
111719
+ * bound).
111720
+ */
111721
+ observeAudioLevel(deviceId, dbfs, at) {
111722
+ if (Number.isNaN(dbfs)) return;
111723
+ const value = Number.isFinite(dbfs) ? dbfs : AUDIO_LEVEL_SILENT_DBFS;
111724
+ const win = this.levelWindows.get(deviceId);
111725
+ if (win === void 0) {
111726
+ this.levelWindows.set(deviceId, {
111727
+ start: at,
111728
+ peak: value
111729
+ });
111730
+ return;
111731
+ }
111732
+ if (at - win.start < AUDIO_LEVEL_WINDOW_MS) {
111733
+ if (value > win.peak) this.levelWindows.set(deviceId, {
111734
+ start: win.start,
111735
+ peak: value
111736
+ });
111737
+ return;
111738
+ }
111739
+ this.levelWindows.set(deviceId, {
111740
+ start: at,
111741
+ peak: value
111742
+ });
111743
+ this.recordAudioLevel(deviceId, win.peak, win.start);
111744
+ }
111636
111745
  recordAudioLevel(deviceId, dbfs, at) {
111637
111746
  const record = this.deviceHistory.record;
111638
111747
  if (record === void 0) return;
@@ -116595,6 +116704,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
116595
116704
  const eventStore = new EventStore({
116596
116705
  store: api.settingsStore,
116597
116706
  logger: logger.child("EventStore"),
116707
+ audioLevelHistory: (input) => api.deviceHistory.query.query(input),
116598
116708
  motionLedger: wireMotionEventLedger(eventLedgerWiring),
116599
116709
  audioLedger: wireAudioEventLedger(eventLedgerWiring),
116600
116710
  onMotionSample: (deviceId, regionCount, at) => lts.note("motion", deviceId, regionCount, at),
@@ -122070,6 +122180,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
122070
122180
  score: topClassification.score
122071
122181
  } : {}
122072
122182
  }, this.audioMarkerConfig(settings));
122183
+ if (level !== void 0) this.lts?.observeAudioLevel(deviceId, level.dbfs, timestamp);
122073
122184
  this.notificationCenter?.observeAudio(deviceId, ncAudioSampleFromFrame(frame, timestamp, settings));
122074
122185
  const route = classifyAudioFrame(topClassification ? {
122075
122186
  className: topClassification.macroClass,
@@ -122145,8 +122256,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
122145
122256
  });
122146
122257
  this.levelStateByDevice.set(deviceId, res.nextState);
122147
122258
  if (!res.emit) return;
122148
- const dbfs = level?.dbfs ?? Number.NEGATIVE_INFINITY;
122149
- this.lts?.recordAudioLevel(deviceId, dbfs, timestamp);
122150
122259
  }
122151
122260
  /**
122152
122261
  * Persist motion-analysis bus events into the EPISODE tracker (D475): a
@@ -22339,6 +22339,41 @@ function audioDbfsByBucket(events, since, bucketMs) {
22339
22339
  }
22340
22340
  return out;
22341
22341
  }
22342
+ /**
22343
+ * PEAK dBFS per bucket index from device-history points (D431 follow-up).
22344
+ *
22345
+ * The bar answers "was it loud here", so a bucket reports the loudest window
22346
+ * in it. A raw point has `max === value`; a rollup row's `max` is the loudest
22347
+ * point it covers, which a mean would flatten. A bucket with no point is
22348
+ * absent — never zero. A rollup row also credits a held value across a quiet
22349
+ * gap, so its max can echo an earlier measured peak.
22350
+ */
22351
+ function audioPeakByBucket(points, since, bucketMs) {
22352
+ const out = /* @__PURE__ */ new Map();
22353
+ if (bucketMs <= 0) return out;
22354
+ for (const p of points) {
22355
+ if (!Number.isFinite(p.max)) continue;
22356
+ const idx = Math.floor((p.t - since) / bucketMs);
22357
+ if (idx < 0) continue;
22358
+ const prev = out.get(idx);
22359
+ if (prev === void 0 || p.max > prev) out.set(idx, p.max);
22360
+ }
22361
+ return out;
22362
+ }
22363
+ //#endregion
22364
+ //#region src/pipeline-analytics/store/analytics-history-keys.ts
22365
+ /** Declared addon series keys (D850). */
22366
+ var ANALYTICS_HISTORY_BUCKET_SERIES = {
22367
+ motion: "addon:pipeline-analytics/motion",
22368
+ "audio-dbfs": "addon:pipeline-analytics/audio-dbfs",
22369
+ occupancy: "addon:pipeline-analytics/occupancy"
22370
+ };
22371
+ var ANALYTICS_AUDIO_LEVEL_KEY = "addon:pipeline-analytics/audio-level";
22372
+ /** Map the public cap enum to the device-history key (read path). */
22373
+ function historyKeyForLtsSeries(series) {
22374
+ if (series === "battery") return "cap:battery.percentage";
22375
+ return ANALYTICS_HISTORY_BUCKET_SERIES[series];
22376
+ }
22342
22377
  //#endregion
22343
22378
  //#region src/pipeline-analytics/store/event-store.ts
22344
22379
  /**
@@ -22711,6 +22746,24 @@ function eventRowId(logger, operation, deviceId, key) {
22711
22746
  });
22712
22747
  return null;
22713
22748
  }
22749
+ var RAW_RETENTION_MS = 7 * 864e5;
22750
+ var FIVE_MIN_RETENTION_MS = 14 * 864e5;
22751
+ var FIVE_MIN_MS = 3e5;
22752
+ var HOUR_MS$2 = 36e5;
22753
+ /** The producer writes one raw point per window of this length. */
22754
+ var AUDIO_LEVEL_POINT_MS = 1e4;
22755
+ var HISTORY_MAX_POINTS = 5e3;
22756
+ /**
22757
+ * Pick the device-history tier for a density window: raw while the buckets are
22758
+ * finer than the 5m rollup, the window is inside raw retention and its points
22759
+ * fit one query; the 5m rollup while inside its retention; else 1h.
22760
+ */
22761
+ function audioLevelResolution(q, now) {
22762
+ const rawFits = Math.ceil((q.until - q.since) / AUDIO_LEVEL_POINT_MS) <= HISTORY_MAX_POINTS;
22763
+ if (q.bucketMs < FIVE_MIN_MS && rawFits && q.since >= now - RAW_RETENTION_MS) return "raw";
22764
+ if (q.bucketMs < HOUR_MS$2 && q.since >= now - FIVE_MIN_RETENTION_MS) return "5m";
22765
+ return "1h";
22766
+ }
22714
22767
  var EventStore = class EventStore {
22715
22768
  store;
22716
22769
  logger;
@@ -22718,6 +22771,10 @@ var EventStore = class EventStore {
22718
22771
  audioLedger;
22719
22772
  media;
22720
22773
  onMotionSample;
22774
+ audioLevelHistory;
22775
+ nowFn;
22776
+ /** Cameras whose history read already failed once (log once per camera, D315). */
22777
+ levelFallbackLogged = /* @__PURE__ */ new Set();
22721
22778
  constructor(deps) {
22722
22779
  this.store = deps.store;
22723
22780
  this.logger = deps.logger;
@@ -22726,6 +22783,8 @@ var EventStore = class EventStore {
22726
22783
  this.audioLedger = deps.audioLedger ?? wireAudioEventLedger(wiring);
22727
22784
  this.media = deps.media;
22728
22785
  this.onMotionSample = deps.onMotionSample;
22786
+ this.audioLevelHistory = deps.audioLevelHistory;
22787
+ this.nowFn = deps.now ?? (() => Date.now());
22729
22788
  }
22730
22789
  static async declareTracklessEventCollections(store) {
22731
22790
  await store.declareCollection.mutate({
@@ -23454,8 +23513,8 @@ var EventStore = class EventStore {
23454
23513
  const m = motionMap.get(i) ?? 0;
23455
23514
  const o = objectMap.get(i) ?? 0;
23456
23515
  const a = audioMap.get(i) ?? 0;
23457
- if (m + o + a === 0) continue;
23458
23516
  const dbfs = audioDbfsMap.get(i);
23517
+ if (m + o + a === 0 && dbfs === void 0) continue;
23459
23518
  out.push({
23460
23519
  bucketStart: q.since + i * q.bucketMs,
23461
23520
  motion: m,
@@ -23476,6 +23535,28 @@ var EventStore = class EventStore {
23476
23535
  * missing bar is the honest rendering of "not measured" (D431).
23477
23536
  */
23478
23537
  async audioDbfsPerBucket(q) {
23538
+ const history = this.audioLevelHistory;
23539
+ if (history !== void 0) try {
23540
+ const resolution = audioLevelResolution(q, this.nowFn());
23541
+ const points = (await history({
23542
+ deviceId: q.deviceId,
23543
+ key: ANALYTICS_AUDIO_LEVEL_KEY,
23544
+ scope: "",
23545
+ from: q.since,
23546
+ to: q.until,
23547
+ resolution,
23548
+ maxPoints: HISTORY_MAX_POINTS
23549
+ })).series.flatMap((s) => s.points);
23550
+ if (points.length > 0) return audioPeakByBucket(points, q.since, q.bucketMs);
23551
+ } catch (err) {
23552
+ if (!this.levelFallbackLogged.has(q.deviceId)) {
23553
+ this.levelFallbackLogged.add(q.deviceId);
23554
+ this.logger.warn("density: audio level history unreadable — falling back to audio events", {
23555
+ tags: { deviceId: q.deviceId },
23556
+ meta: { error: String(err) }
23557
+ });
23558
+ }
23559
+ }
23479
23560
  try {
23480
23561
  return audioDbfsByBucket((await this.queryAudio({
23481
23562
  deviceId: q.deviceId,
@@ -111460,20 +111541,6 @@ var MigrationMarkerStore = class MigrationMarkerStore {
111460
111541
  };
111461
111542
  };
111462
111543
  //#endregion
111463
- //#region src/pipeline-analytics/store/analytics-history-keys.ts
111464
- /** Declared addon series keys (D850). */
111465
- var ANALYTICS_HISTORY_BUCKET_SERIES = {
111466
- motion: "addon:pipeline-analytics/motion",
111467
- "audio-dbfs": "addon:pipeline-analytics/audio-dbfs",
111468
- occupancy: "addon:pipeline-analytics/occupancy"
111469
- };
111470
- var ANALYTICS_AUDIO_LEVEL_KEY = "addon:pipeline-analytics/audio-level";
111471
- /** Map the public cap enum to the device-history key (read path). */
111472
- function historyKeyForLtsSeries(series) {
111473
- if (series === "battery") return "cap:battery.percentage";
111474
- return ANALYTICS_HISTORY_BUCKET_SERIES[series];
111475
- }
111476
- //#endregion
111477
111544
  //#region src/pipeline-analytics/store/analytics-history-producer.ts
111478
111545
  var BUCKET_SERIES_META = [
111479
111546
  {
@@ -111495,6 +111562,10 @@ var AUDIO_LEVEL_SERIES = {
111495
111562
  displayName: "Audio level (dBFS)",
111496
111563
  unit: "dBFS"
111497
111564
  };
111565
+ /** One audio level point per window; equals device-history's 1 point / 10 s record bound. */
111566
+ var AUDIO_LEVEL_WINDOW_MS = 1e4;
111567
+ /** Stored for a measured-silent window (−∞ dBFS cannot be a history value). */
111568
+ var AUDIO_LEVEL_SILENT_DBFS = -120;
111498
111569
  function refusalCounts(result) {
111499
111570
  const counts = {};
111500
111571
  for (const r of result.refused) counts[r.reason] = (counts[r.reason] ?? 0) + r.count;
@@ -111506,6 +111577,7 @@ var AnalyticsHistoryProducer = class {
111506
111577
  logger;
111507
111578
  nowFn;
111508
111579
  declared = false;
111580
+ levelWindows = /* @__PURE__ */ new Map();
111509
111581
  constructor(deps) {
111510
111582
  this.deviceHistory = deps.deviceHistory;
111511
111583
  this.logger = deps.logger;
@@ -111538,6 +111610,43 @@ var AnalyticsHistoryProducer = class {
111538
111610
  deviceId
111539
111611
  }, scope ?? "", value, at ?? this.nowFn());
111540
111612
  }
111613
+ /**
111614
+ * Feed EVERY analysed audio sample. One point per window of
111615
+ * {@link AUDIO_LEVEL_WINDOW_MS} is written, stamped at the window's first
111616
+ * sample, carrying the window's PEAK dBFS: a 2 s spike inside a stable window
111617
+ * is not averaged away, and stable audio still yields a point each window
111618
+ * (continuity over dedup — the level bar draws a continuous line).
111619
+ *
111620
+ * Measured silence (−∞) is stored at {@link AUDIO_LEVEL_SILENT_DBFS}: it is a
111621
+ * measurement, not an absence (D431); NaN carries no claim and is dropped.
111622
+ * A window closes on the first sample past its end, so no timer is needed and
111623
+ * points are always at least a window apart (the history's 1 point / 10 s
111624
+ * bound).
111625
+ */
111626
+ observeAudioLevel(deviceId, dbfs, at) {
111627
+ if (Number.isNaN(dbfs)) return;
111628
+ const value = Number.isFinite(dbfs) ? dbfs : AUDIO_LEVEL_SILENT_DBFS;
111629
+ const win = this.levelWindows.get(deviceId);
111630
+ if (win === void 0) {
111631
+ this.levelWindows.set(deviceId, {
111632
+ start: at,
111633
+ peak: value
111634
+ });
111635
+ return;
111636
+ }
111637
+ if (at - win.start < AUDIO_LEVEL_WINDOW_MS) {
111638
+ if (value > win.peak) this.levelWindows.set(deviceId, {
111639
+ start: win.start,
111640
+ peak: value
111641
+ });
111642
+ return;
111643
+ }
111644
+ this.levelWindows.set(deviceId, {
111645
+ start: at,
111646
+ peak: value
111647
+ });
111648
+ this.recordAudioLevel(deviceId, win.peak, win.start);
111649
+ }
111541
111650
  recordAudioLevel(deviceId, dbfs, at) {
111542
111651
  const record = this.deviceHistory.record;
111543
111652
  if (record === void 0) return;
@@ -116500,6 +116609,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
116500
116609
  const eventStore = new EventStore({
116501
116610
  store: api.settingsStore,
116502
116611
  logger: logger.child("EventStore"),
116612
+ audioLevelHistory: (input) => api.deviceHistory.query.query(input),
116503
116613
  motionLedger: wireMotionEventLedger(eventLedgerWiring),
116504
116614
  audioLedger: wireAudioEventLedger(eventLedgerWiring),
116505
116615
  onMotionSample: (deviceId, regionCount, at) => lts.note("motion", deviceId, regionCount, at),
@@ -121975,6 +122085,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
121975
122085
  score: topClassification.score
121976
122086
  } : {}
121977
122087
  }, this.audioMarkerConfig(settings));
122088
+ if (level !== void 0) this.lts?.observeAudioLevel(deviceId, level.dbfs, timestamp);
121978
122089
  this.notificationCenter?.observeAudio(deviceId, ncAudioSampleFromFrame(frame, timestamp, settings));
121979
122090
  const route = classifyAudioFrame(topClassification ? {
121980
122091
  className: topClassification.macroClass,
@@ -122050,8 +122161,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
122050
122161
  });
122051
122162
  this.levelStateByDevice.set(deviceId, res.nextState);
122052
122163
  if (!res.emit) return;
122053
- const dbfs = level?.dbfs ?? Number.NEGATIVE_INFINITY;
122054
- this.lts?.recordAudioLevel(deviceId, dbfs, timestamp);
122055
122164
  }
122056
122165
  /**
122057
122166
  * Persist motion-analysis bus events into the EPISODE tracker (D475): a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.608",
3
+ "version": "1.2.609",
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",