@camstack/addon-post-analysis 1.2.631 → 1.2.633

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 (15) hide show
  1. package/dist/{clip-model-registry-DMmf5o2X.mjs → clip-model-registry-CCbbEBYS.mjs} +493 -75
  2. package/dist/{clip-model-registry-BgsEOah6.js → clip-model-registry-CeNmplcH.js} +510 -74
  3. package/dist/embedding-encoder/index.js +1 -1
  4. package/dist/embedding-encoder/index.mjs +1 -1
  5. package/dist/pipeline-analytics/_stub.js +2 -2
  6. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DkE3t99L.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-ArpdWsMY.mjs} +2 -2
  7. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BQq3WNOE.mjs +26 -0
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-MOlfkem8.mjs +26 -0
  9. package/dist/pipeline-analytics/{hostInit-JabXipp8.mjs → hostInit-g5HpKRU7.mjs} +2 -2
  10. package/dist/pipeline-analytics/index.js +150 -23
  11. package/dist/pipeline-analytics/index.mjs +150 -23
  12. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  13. package/package.json +1 -1
  14. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CTux2q1V.mjs +0 -26
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DdbV-R1z.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-BgsEOah6.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-CeNmplcH.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");
@@ -25,6 +25,66 @@ let node_http = require("node:http");
25
25
  let node_https = require("node:https");
26
26
  let node_timers_promises = require("node:timers/promises");
27
27
  let node_buffer = require("node:buffer");
28
+ //#region src/shared/display-palette-painter.ts
29
+ var REFRESH_MS = 3e4;
30
+ function isRecord$5(value) {
31
+ return typeof value === "object" && value !== null;
32
+ }
33
+ function isPaletteQueryApi(value) {
34
+ if (!("displayPalette" in value)) return false;
35
+ const cap = value.displayPalette;
36
+ if (!isRecord$5(cap) || !("getPalette" in cap)) return false;
37
+ const method = cap.getPalette;
38
+ if (!isRecord$5(method) || typeof method.query !== "function") return false;
39
+ return true;
40
+ }
41
+ /** `getPalette` when the cap is mounted. Otherwise a rejection the painter logs. */
42
+ function readDisplayPalette(api) {
43
+ if (!isPaletteQueryApi(api)) return Promise.reject(/* @__PURE__ */ new Error("display-palette capability is not mounted"));
44
+ return api.displayPalette.getPalette.query();
45
+ }
46
+ function createDisplayPalettePainter(read, logger) {
47
+ let ready = require_clip_model_registry.bundledCamstack();
48
+ let announced = false;
49
+ let refreshing = false;
50
+ let fetchedAt = 0;
51
+ const say = (reason) => {
52
+ if (announced) return;
53
+ announced = true;
54
+ logger.warn("painting the bundled CamStack preset", { meta: { reason } });
55
+ };
56
+ const refresh = () => {
57
+ refreshing = true;
58
+ read().then((value) => {
59
+ const parsed = require_clip_model_registry.paletteResultSchema.safeParse(value);
60
+ if (!parsed.success) {
61
+ ready = require_clip_model_registry.bundledCamstack();
62
+ say("palette read did not match the schema");
63
+ return;
64
+ }
65
+ if (parsed.data.status === "unknown") {
66
+ ready = require_clip_model_registry.bundledCamstack();
67
+ say(parsed.data.reason);
68
+ return;
69
+ }
70
+ ready = parsed.data;
71
+ announced = false;
72
+ }).catch((err) => {
73
+ ready = require_clip_model_registry.bundledCamstack();
74
+ say(err instanceof Error ? err.message : String(err));
75
+ }).finally(() => {
76
+ fetchedAt = Date.now();
77
+ refreshing = false;
78
+ });
79
+ };
80
+ refresh();
81
+ const paint = (kind) => {
82
+ if (Date.now() - fetchedAt >= REFRESH_MS && !refreshing) refresh();
83
+ return require_clip_model_registry.paintHex(ready.swatches, ready.fallbacks, kind);
84
+ };
85
+ return { paint };
86
+ }
87
+ //#endregion
28
88
  //#region src/notification-center/alarm/alarm-mode-coverage.ts
29
89
  /**
30
90
  * Whether the panel just BECAME armed, in the sense a person means.
@@ -69174,6 +69234,7 @@ function isUsableBbox(bbox) {
69174
69234
  }
69175
69235
  //#endregion
69176
69236
  //#region src/pipeline-analytics/services/event-media-dispatcher.ts
69237
+ var BUNDLED_PALETTE$1 = require_clip_model_registry.bundledCamstack();
69177
69238
  /**
69178
69239
  * Long-side cap (px) for the boxed timeline tiles — `firstFrame`, `snapshot`,
69179
69240
  * `lastFrame`. These are the low-res "timeline filmstrip" images: a native full
@@ -69496,7 +69557,7 @@ var EventMediaDispatcher = class {
69496
69557
  lastFrameWritten: false
69497
69558
  };
69498
69559
  let scene = null;
69499
- if (sn.appendSnapshot || sn.rollingLastFrame) scene = await this.boxedTimelineFrame(deviceId, frameHandle, frameData, fw, fh, sn.trackId, sn.bbox, sn.label);
69560
+ if (sn.appendSnapshot || sn.rollingLastFrame) scene = await this.boxedTimelineFrame(deviceId, frameHandle, frameData, fw, fh, sn.trackId, sn.bbox, sn.label, sn.className);
69500
69561
  let stored = null;
69501
69562
  if (sn.appendSnapshot && scene) {
69502
69563
  await this.writeSceneTwin(deviceId, sn.trackId, sn.timestamp, scene.clean, "append");
@@ -69713,7 +69774,17 @@ var EventMediaDispatcher = class {
69713
69774
  * second fetch and no second decode (D654). `null` only if a sharp encode
69714
69775
  * throws.
69715
69776
  */
69716
- async boxedTimelineFrame(deviceId, frameHandle, frameData, fw, fh, ownerId, bbox, label) {
69777
+ /** Stroke a timeline box in the class's palette colour. No class keeps the drawer's default. */
69778
+ paletteBox(box, className) {
69779
+ if (className === void 0 || className.length === 0) return box;
69780
+ const paint = this.deps.paintClass;
69781
+ const color = paint !== void 0 ? paint(className) : require_clip_model_registry.paintHex(BUNDLED_PALETTE$1.swatches, BUNDLED_PALETTE$1.fallbacks, className);
69782
+ return {
69783
+ ...box,
69784
+ color
69785
+ };
69786
+ }
69787
+ async boxedTimelineFrame(deviceId, frameHandle, frameData, fw, fh, ownerId, bbox, label, className) {
69717
69788
  let nf = null;
69718
69789
  let nfJpeg = null;
69719
69790
  let fetchThrew = false;
@@ -69733,7 +69804,7 @@ var EventMediaDispatcher = class {
69733
69804
  }
69734
69805
  try {
69735
69806
  if (nfJpeg && nfJpeg.width > 0 && nfJpeg.height > 0) {
69736
- const box = scaleBox(bbox, fw, fh, nfJpeg.width, nfJpeg.height, label);
69807
+ const box = this.paletteBox(scaleBox(bbox, fw, fh, nfJpeg.width, nfJpeg.height, label), className);
69737
69808
  const opts = { quality: MEDIA_QUALITY };
69738
69809
  const [boxed, clean] = await Promise.all([drawBoxedJpeg(nfJpeg.jpeg, nfJpeg.width, nfJpeg.height, [box], opts), drawBoxedJpeg(nfJpeg.jpeg, nfJpeg.width, nfJpeg.height, [], opts)]);
69739
69810
  return {
@@ -69742,7 +69813,7 @@ var EventMediaDispatcher = class {
69742
69813
  };
69743
69814
  }
69744
69815
  if (nf && nf.format === "rgb" && nf.width > 0 && nf.height > 0) {
69745
- const box = scaleBox(bbox, fw, fh, nf.width, nf.height, label);
69816
+ const box = this.paletteBox(scaleBox(bbox, fw, fh, nf.width, nf.height, label), className);
69746
69817
  const opts = { quality: MEDIA_QUALITY };
69747
69818
  const [boxed, clean] = await Promise.all([drawBoxedFrame(nf.data, nf.width, nf.height, [box], opts), drawBoxedFrame(nf.data, nf.width, nf.height, [], opts)]);
69748
69819
  return {
@@ -69758,10 +69829,10 @@ var EventMediaDispatcher = class {
69758
69829
  shmId: frameHandle.shmId
69759
69830
  }
69760
69831
  });
69761
- const box = {
69832
+ const box = this.paletteBox({
69762
69833
  ...bbox,
69763
69834
  ...label ? { label } : {}
69764
- };
69835
+ }, className);
69765
69836
  const opts = {
69766
69837
  quality: MEDIA_QUALITY,
69767
69838
  maxWidth: SNAPSHOT_MAX_WIDTH
@@ -69834,7 +69905,7 @@ var EventMediaDispatcher = class {
69834
69905
  }
69835
69906
  }
69836
69907
  async writeTrackFrame(deviceId, frameHandle, frameData, fw, fh, tf) {
69837
- const scene = await this.boxedTimelineFrame(deviceId, frameHandle, frameData, fw, fh, tf.trackId, tf.bbox, tf.label);
69908
+ const scene = await this.boxedTimelineFrame(deviceId, frameHandle, frameData, fw, fh, tf.trackId, tf.bbox, tf.label, tf.className);
69838
69909
  if (!scene) return false;
69839
69910
  await this.writeSceneTwin(deviceId, tf.trackId, tf.timestamp, scene.clean, "append");
69840
69911
  try {
@@ -82522,8 +82593,9 @@ function unmeasuredEpisode(input) {
82522
82593
  });
82523
82594
  const parts = parseConsumableSubject(input.subject);
82524
82595
  if (parts !== null && parts.legacy) {
82525
- const attention = input.detail === "full" ? "full" : "low";
82526
- const form = providerFormFromDetail(input.detail);
82596
+ const parsed = parseProviderVerdictDetail(input.detail);
82597
+ const attention = parsed?.attention ?? "low";
82598
+ const form = parsed?.form ?? "tank";
82527
82599
  return incomingFromProviderConsumableBand({
82528
82600
  facts,
82529
82601
  deviceId,
@@ -82552,9 +82624,43 @@ function unmeasuredEpisode(input) {
82552
82624
  });
82553
82625
  return null;
82554
82626
  }
82555
- function providerFormFromDetail(detail) {
82556
- if (detail === "full") return "bin";
82557
- return "tank";
82627
+ var PROVIDER_DETAIL_FORMS = [
82628
+ "part",
82629
+ "tank",
82630
+ "product",
82631
+ "bin"
82632
+ ];
82633
+ function isConsumableForm(value) {
82634
+ return PROVIDER_DETAIL_FORMS.some((known) => known === value);
82635
+ }
82636
+ /** Durable `detail` for a provider-verdict episode: band plus form (D934). */
82637
+ function providerVerdictDetail(band, form) {
82638
+ return `${band}:${form}`;
82639
+ }
82640
+ function parseProviderVerdictDetail(detail) {
82641
+ if (detail === "low") return {
82642
+ attention: "low",
82643
+ form: "tank"
82644
+ };
82645
+ if (detail === "full") return {
82646
+ attention: "full",
82647
+ form: "bin"
82648
+ };
82649
+ if (detail === void 0) return null;
82650
+ const sep = detail.indexOf(":");
82651
+ if (sep < 1) return null;
82652
+ const attention = detail.slice(0, sep);
82653
+ const form = detail.slice(sep + 1);
82654
+ if (attention !== "low" && attention !== "full") return null;
82655
+ if (!isConsumableForm(form)) return null;
82656
+ return {
82657
+ attention,
82658
+ form
82659
+ };
82660
+ }
82661
+ /** True when the row is a provider-verdict consumable, not a threshold episode. */
82662
+ function isProviderVerdictDetail(detail) {
82663
+ return parseProviderVerdictDetail(detail) !== null;
82558
82664
  }
82559
82665
  function openAlertOf(row, input) {
82560
82666
  const parts = parseConsumableSubject(row.subject);
@@ -82823,7 +82929,7 @@ var NcDeviceAlerts = class {
82823
82929
  },
82824
82930
  attention,
82825
82931
  identity: this.deps.lookup(deviceId)
82826
- }), band === "normal" ? void 0 : band);
82932
+ }), band === "normal" ? void 0 : providerVerdictDetail(band, sliceItem.form));
82827
82933
  }
82828
82934
  /**
82829
82935
  * Has this provider band held long enough to move its episode? A band that
@@ -83023,6 +83129,7 @@ var NcDeviceAlerts = class {
83023
83129
  for (const row of this.deps.rows.all()) {
83024
83130
  const parts = parseConsumableSubject(row.subject);
83025
83131
  if (parts === null || thresholds.includes(parts.thresholdPercent)) continue;
83132
+ if (parts.legacy && isProviderVerdictDetail(row.detail)) continue;
83026
83133
  absent.add(parts.thresholdPercent);
83027
83134
  if (this.absentThresholds.has(parts.thresholdPercent)) this.dropRow(row.subject, "retired — no rule judges consumables at this threshold (D642)");
83028
83135
  }
@@ -86902,6 +87009,14 @@ function toPixels(box, width, height) {
86902
87009
  h: Math.max(1, Math.min(Math.round(box.h * height), height - y))
86903
87010
  };
86904
87011
  }
87012
+ var BUNDLED_PALETTE = require_clip_model_registry.bundledCamstack();
87013
+ function bundledPaint(kind) {
87014
+ return require_clip_model_registry.paintHex(BUNDLED_PALETTE.swatches, BUNDLED_PALETTE.fallbacks, kind);
87015
+ }
87016
+ function boxStroke(box, paint) {
87017
+ if (box?.className !== void 0 && box.className.length > 0) return (paint ?? bundledPaint)(box.className);
87018
+ return box?.role === "other" ? NC_IMAGE_OTHER_COLOR : NC_IMAGE_SUBJECT_COLOR;
87019
+ }
86905
87020
  /** {@link notificationOverlay}'s SVG alone. */
86906
87021
  function notificationOverlaySvg(input) {
86907
87022
  return notificationOverlay(input).svg;
@@ -86923,9 +87038,10 @@ function notificationOverlay(input) {
86923
87038
  const points = zone.map((p) => `${String(clampPx(p.x * width, width))},${String(clampPx(p.y * height, height))}`).join(" ");
86924
87039
  parts.push(`<polygon data-role="zone" points="${points}" fill="${NC_IMAGE_ZONE_COLOR}" fill-opacity="0.18" stroke="${NC_IMAGE_ZONE_COLOR}" stroke-width="${String(strokes.other)}" stroke-dasharray="${String(strokes.subject * 2)},${String(strokes.subject)}"/>`);
86925
87040
  }
87041
+ const pathColor = boxStroke(input.boxes.find((b) => b.role === "subject"), input.paintClass);
86926
87042
  for (const run of input.paths ?? []) {
86927
87043
  if (run.length < 2) continue;
86928
- parts.push(`<polyline data-role="subject-path" points="${require_clip_model_registry.subjectPathSvgPoints(run, width, height)}" fill="none" stroke="${NC_IMAGE_SUBJECT_COLOR}" stroke-width="${String(strokes.subject)}" stroke-linejoin="round" stroke-linecap="round" stroke-opacity="0.9"/>`);
87044
+ parts.push(`<polyline data-role="subject-path" points="${require_clip_model_registry.subjectPathSvgPoints(run, width, height)}" fill="none" stroke="${pathColor}" stroke-width="${String(strokes.subject)}" stroke-linejoin="round" stroke-linecap="round" stroke-opacity="0.9"/>`);
86929
87045
  }
86930
87046
  const ordered = [...input.boxes.filter((b) => b.role === "other"), ...input.boxes.filter((b) => b.role === "subject")];
86931
87047
  const layout = layoutBoxLabels({
@@ -86937,7 +87053,7 @@ function notificationOverlay(input) {
86937
87053
  const plates = [];
86938
87054
  ordered.forEach((box, i) => {
86939
87055
  const subject = box.role === "subject";
86940
- const color = subject ? NC_IMAGE_SUBJECT_COLOR : NC_IMAGE_OTHER_COLOR;
87056
+ const color = boxStroke(box, input.paintClass);
86941
87057
  const stroke = subject ? strokes.subject : strokes.other;
86942
87058
  parts.push(`<rect data-role="${box.role}" x="${String(box.x)}" y="${String(box.y)}" width="${String(box.w)}" height="${String(box.h)}" fill="none" stroke="${color}" stroke-width="${String(stroke)}"/>`);
86943
87059
  const plate = layout.placements[i];
@@ -87106,11 +87222,13 @@ async function renderNotificationImage(req) {
87106
87222
  ...toPixels(o, outW, outH),
87107
87223
  role: "other",
87108
87224
  label: o.label,
87109
- ...o.confidence !== void 0 ? { score: o.confidence } : {}
87225
+ ...o.confidence !== void 0 ? { score: o.confidence } : {},
87226
+ ...o.className !== void 0 ? { className: o.className } : {}
87110
87227
  })), ...req.subject !== null ? [{
87111
87228
  ...toPixels(req.subject, outW, outH),
87112
87229
  role: "subject",
87113
- label: req.subject.label
87230
+ label: req.subject.label,
87231
+ ...req.subject.className !== void 0 ? { className: req.subject.className } : {}
87114
87232
  }] : []] : [];
87115
87233
  const zones = req.mode === "crop" ? [] : req.zones;
87116
87234
  const paths = require_clip_model_registry.projectSubjectPath(req.path ?? [], {
@@ -87131,7 +87249,8 @@ async function renderNotificationImage(req) {
87131
87249
  height: outH,
87132
87250
  boxes,
87133
87251
  zones,
87134
- paths
87252
+ paths,
87253
+ ...req.paintClass !== void 0 ? { paintClass: req.paintClass } : {}
87135
87254
  });
87136
87255
  if (boxes.length > 0) labels = overlay.labels;
87137
87256
  pipeline = pipeline.composite([{
@@ -87218,7 +87337,8 @@ var NcNotificationImageStage = class {
87218
87337
  const path = withPath ? this.subjectPath(input, trail) : [];
87219
87338
  const subject = subjectBox !== null ? {
87220
87339
  ...subjectBox,
87221
- label: imageLabelText(payload.subject.label ?? payload.subject.className, payload.subject.confidence)
87340
+ label: imageLabelText(payload.subject.label ?? payload.subject.className, payload.subject.confidence),
87341
+ className: payload.subject.className
87222
87342
  } : null;
87223
87343
  const othersDrawn = mode === "scene-boxes" ? otherTracks(await this.tracksAt(camera, source.timestamp), subjectTrackId, subjectBox, payload.subject.className) : [];
87224
87344
  if (mode === "scene-boxes") this.deps.logger.info("scene-boxes: other boxes drawn", {
@@ -87233,6 +87353,7 @@ var NcNotificationImageStage = class {
87233
87353
  const others = mode === "scene-boxes" ? othersDrawn.map((t) => ({
87234
87354
  ...t.bbox,
87235
87355
  label: imageLabelText(t.label ?? t.className, t.confidence),
87356
+ className: t.className,
87236
87357
  ...t.confidence !== void 0 ? { confidence: t.confidence } : {}
87237
87358
  })) : [];
87238
87359
  if (mode !== "crop" && input.withSubject && payload.mediaShowTriggerZone === true && payload.matchedZoneIds === void 0) this.deps.logger.info("trigger zone not drawn — the row never froze which zone fired", {
@@ -87251,7 +87372,8 @@ var NcNotificationImageStage = class {
87251
87372
  subject,
87252
87373
  others,
87253
87374
  zones,
87254
- ...path.length > 0 ? { path } : {}
87375
+ ...path.length > 0 ? { path } : {},
87376
+ ...this.deps.paintClass !== void 0 ? { paintClass: this.deps.paintClass } : {}
87255
87377
  });
87256
87378
  const reason = image.rendered === mode ? void 0 : mode === "crop" ? "no-subject-box" : "no-tracks-at-instant";
87257
87379
  if (reason !== void 0) this.reportFallback(input, mode, reason);
@@ -87584,7 +87706,8 @@ var NcDispatcher = class {
87584
87706
  now: this.now,
87585
87707
  ...deps.getSubjectTrail !== void 0 ? { getSubjectTrail: deps.getSubjectTrail } : {},
87586
87708
  ...deps.getTracksAt !== void 0 ? { getTracksAt: deps.getTracksAt } : {},
87587
- ...deps.getZonePolygons !== void 0 ? { getZonePolygons: deps.getZonePolygons } : {}
87709
+ ...deps.getZonePolygons !== void 0 ? { getZonePolygons: deps.getZonePolygons } : {},
87710
+ ...deps.paintClass !== void 0 ? { paintClass: deps.paintClass } : {}
87588
87711
  });
87589
87712
  }
87590
87713
  /**
@@ -109939,6 +110062,7 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
109939
110062
  y: pt.y
109940
110063
  })));
109941
110064
  },
110065
+ paintClass: createDisplayPalettePainter(() => readDisplayPalette(api), logger.child("DisplayPalette")).paint,
109942
110066
  renderEventMedia: (req) => this.renderEventMedia(req),
109943
110067
  cutRecordedClip: (req) => this.cutNotificationRecordedClip(req),
109944
110068
  deriveGifFromClip: (req) => deriveRecordedGif(buildRecordedGifPorts({
@@ -120490,7 +120614,8 @@ var SnapshotTargetPlanner = class {
120490
120614
  trackId: t.trackId,
120491
120615
  timestamp,
120492
120616
  bbox: firstFrameRetryBbox(t.bbox, this.trackStore?.peekActive(t.trackId)?.positions[0]?.bbox),
120493
- ...patchDisplayLabel(t.labelPatch) !== void 0 ? { label: patchDisplayLabel(t.labelPatch) } : {}
120617
+ ...patchDisplayLabel(t.labelPatch) !== void 0 ? { label: patchDisplayLabel(t.labelPatch) } : {},
120618
+ className: t.className
120494
120619
  });
120495
120620
  }
120496
120621
  return retries;
@@ -120688,6 +120813,7 @@ var SnapshotTargetPlanner = class {
120688
120813
  timestamp,
120689
120814
  bbox: { ...t.bbox },
120690
120815
  ...patchDisplayLabel(t.labelPatch) !== void 0 ? { label: patchDisplayLabel(t.labelPatch) } : {},
120816
+ className: t.className,
120691
120817
  appendSnapshot: plan.appendSnapshot,
120692
120818
  rollingLastFrame,
120693
120819
  bestThumbnail,
@@ -123119,6 +123245,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
123119
123245
  getParkedSubjectSlab: (deviceId, trackId) => this.mediaCapture.fetchParkedSubjectSlab(deviceId, trackId),
123120
123246
  mediaStore: stores.mediaStore,
123121
123247
  logger: logger.child("EventMediaDispatcher"),
123248
+ paintClass: createDisplayPalettePainter(() => readDisplayPalette(api), logger.child("DisplayPalette")).paint,
123122
123249
  onSubjectCropMiss: (deviceId, trackId, reason) => this.residents.notePreviewCaptureMiss(deviceId, trackId, `thumbnail:${reason}`)
123123
123250
  });
123124
123251
  this.buildRecognizers(api, logger, stores, transport);