@camstack/addon-export-alexa 1.1.26 → 1.1.27

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.
@@ -7848,6 +7848,160 @@ function pickClosestResolution(entries, target) {
7848
7848
  }
7849
7849
  return bestAbove?.entry ?? bestBelow?.entry;
7850
7850
  }
7851
+ var COCO_TO_MACRO = {
7852
+ mapping: {
7853
+ person: "person",
7854
+ bicycle: "vehicle",
7855
+ car: "vehicle",
7856
+ motorcycle: "vehicle",
7857
+ airplane: "vehicle",
7858
+ bus: "vehicle",
7859
+ train: "vehicle",
7860
+ truck: "vehicle",
7861
+ boat: "vehicle",
7862
+ bird: "animal",
7863
+ cat: "animal",
7864
+ dog: "animal",
7865
+ horse: "animal",
7866
+ sheep: "animal",
7867
+ cow: "animal",
7868
+ elephant: "animal",
7869
+ bear: "animal",
7870
+ zebra: "animal",
7871
+ giraffe: "animal",
7872
+ suitcase: "package",
7873
+ backpack: "package",
7874
+ handbag: "package"
7875
+ },
7876
+ preserveOriginal: false
7877
+ };
7878
+ var AUDIO_MACRO_LABELS = [
7879
+ {
7880
+ id: "speech",
7881
+ name: "Speech",
7882
+ icon: "🗣️"
7883
+ },
7884
+ {
7885
+ id: "scream",
7886
+ name: "Scream / Shout",
7887
+ icon: "😱"
7888
+ },
7889
+ {
7890
+ id: "crying",
7891
+ name: "Crying / Baby",
7892
+ icon: "😢"
7893
+ },
7894
+ {
7895
+ id: "laughter",
7896
+ name: "Laughter",
7897
+ icon: "😂"
7898
+ },
7899
+ {
7900
+ id: "music",
7901
+ name: "Music",
7902
+ icon: "🎵"
7903
+ },
7904
+ {
7905
+ id: "dog",
7906
+ name: "Dog",
7907
+ icon: "🐕"
7908
+ },
7909
+ {
7910
+ id: "cat",
7911
+ name: "Cat",
7912
+ icon: "🐈"
7913
+ },
7914
+ {
7915
+ id: "bird",
7916
+ name: "Bird",
7917
+ icon: "🐦"
7918
+ },
7919
+ {
7920
+ id: "animal",
7921
+ name: "Animal (other)",
7922
+ icon: "🐾"
7923
+ },
7924
+ {
7925
+ id: "alarm",
7926
+ name: "Alarm / Siren",
7927
+ icon: "🚨"
7928
+ },
7929
+ {
7930
+ id: "doorbell",
7931
+ name: "Doorbell / Knock",
7932
+ icon: "🔔"
7933
+ },
7934
+ {
7935
+ id: "glass_breaking",
7936
+ name: "Glass Breaking",
7937
+ icon: "💥"
7938
+ },
7939
+ {
7940
+ id: "gunshot",
7941
+ name: "Gunshot / Explosion",
7942
+ icon: "💣"
7943
+ },
7944
+ {
7945
+ id: "vehicle",
7946
+ name: "Vehicle",
7947
+ icon: "🚗"
7948
+ },
7949
+ {
7950
+ id: "siren",
7951
+ name: "Emergency Siren",
7952
+ icon: "🚑"
7953
+ },
7954
+ {
7955
+ id: "fire",
7956
+ name: "Fire / Smoke",
7957
+ icon: "🔥"
7958
+ },
7959
+ {
7960
+ id: "water",
7961
+ name: "Water",
7962
+ icon: "💧"
7963
+ },
7964
+ {
7965
+ id: "wind",
7966
+ name: "Wind / Weather",
7967
+ icon: "🌬️"
7968
+ },
7969
+ {
7970
+ id: "door",
7971
+ name: "Door",
7972
+ icon: "🚪"
7973
+ },
7974
+ {
7975
+ id: "footsteps",
7976
+ name: "Footsteps",
7977
+ icon: "👣"
7978
+ },
7979
+ {
7980
+ id: "crowd",
7981
+ name: "Crowd / Chatter",
7982
+ icon: "👥"
7983
+ },
7984
+ {
7985
+ id: "telephone",
7986
+ name: "Telephone",
7987
+ icon: "📞"
7988
+ },
7989
+ {
7990
+ id: "engine",
7991
+ name: "Engine / Motor",
7992
+ icon: "⚙️"
7993
+ },
7994
+ {
7995
+ id: "tools",
7996
+ name: "Tools / Construction",
7997
+ icon: "🔨"
7998
+ },
7999
+ {
8000
+ id: "silence",
8001
+ name: "Silence",
8002
+ icon: "🤫"
8003
+ }
8004
+ ];
7851
8005
  var YAMNET_TO_MACRO = {
7852
8006
  mapping: {
7853
8007
  Speech: "speech",
@@ -8114,6 +8268,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8114
8268
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8115
8269
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8116
8270
  /**
8271
+ * Unified event-kind taxonomy — THE single source of truth for
8272
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8273
+ * icon }`.
8274
+ *
8275
+ * This dictionary folds together what used to be scattered across four
8276
+ * copies:
8277
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8278
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8279
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8280
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8281
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8282
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8283
+ *
8284
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8285
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8286
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8287
+ * color or an icon: they read this dictionary (server descriptors carry the
8288
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8289
+ *
8290
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8291
+ */
8292
+ var TAXONOMY_COLORS = {
8293
+ motion: "#f59e0b",
8294
+ audio: "#06b6d4",
8295
+ person: "#22c55e",
8296
+ vehicle: "#3b82f6",
8297
+ animal: "#f97316",
8298
+ package: "#a855f7",
8299
+ sensor: "#8b5cf6",
8300
+ control: "#10b981",
8301
+ genericDetection: "#64748b"
8302
+ };
8303
+ var DETECTION_SUB_COLORS = {
8304
+ car: "#f59e0b",
8305
+ truck: "#d97706",
8306
+ bus: "#b45309",
8307
+ motorcycle: "#eab308",
8308
+ bicycle: "#ca8a04",
8309
+ airplane: "#60a5fa",
8310
+ boat: "#2563eb",
8311
+ train: "#1d4ed8",
8312
+ bird: "#14b8a6",
8313
+ dog: "#84cc16",
8314
+ cat: "#f97316",
8315
+ horse: "#a16207",
8316
+ sheep: "#a3a3a3",
8317
+ cow: "#78716c",
8318
+ elephant: "#6b7280",
8319
+ bear: "#7c2d12",
8320
+ zebra: "#404040",
8321
+ giraffe: "#d4a373"
8322
+ };
8323
+ function titleCase(id) {
8324
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8325
+ }
8326
+ var entries = /* @__PURE__ */ new Map();
8327
+ function macro(kind, category, color, iconId, label) {
8328
+ entries.set(kind, {
8329
+ kind,
8330
+ parentKind: null,
8331
+ level: "macro",
8332
+ category,
8333
+ color,
8334
+ iconId,
8335
+ labelKey: `eventKind.${kind}`,
8336
+ label
8337
+ });
8338
+ }
8339
+ function sub(kind, parentKind, category, color, iconId, label) {
8340
+ entries.set(kind, {
8341
+ kind,
8342
+ parentKind,
8343
+ level: "sub",
8344
+ category,
8345
+ color,
8346
+ iconId,
8347
+ labelKey: `eventKind.${kind}`,
8348
+ label
8349
+ });
8350
+ }
8351
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8352
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8353
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8354
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8355
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8356
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8357
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8358
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8359
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8360
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8361
+ if (entries.has(cocoClass)) continue;
8362
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8363
+ }
8364
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8365
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8366
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8367
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8368
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8369
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8370
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8371
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8372
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8373
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8374
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8375
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8376
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8377
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8378
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8379
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8380
+ sub("button", "control", "control", "#10b981", "button", "Button");
8381
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8382
+ for (const l of AUDIO_MACRO_LABELS) {
8383
+ const kind = `audio-${l.id}`;
8384
+ if (entries.has(kind)) continue;
8385
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8386
+ }
8387
+ /** The complete taxonomy dictionary, keyed by kind. */
8388
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8389
+ /**
8117
8390
  * Error types for the safe expression engine. Two distinct classes so callers
8118
8391
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8119
8392
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -16134,17 +16407,30 @@ var EventKindCategorySchema = _enum([
16134
16407
  "audio",
16135
16408
  "detection",
16136
16409
  "sensor",
16410
+ "control",
16137
16411
  "custom",
16138
16412
  "package"
16139
16413
  ]);
16414
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16415
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
16140
16416
  var EventKindDescriptorSchema = object({
16141
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16417
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
16142
16418
  kind: string(),
16419
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16420
+ labelKey: string(),
16421
+ /** English fallback label (kept for clients that don't translate). */
16143
16422
  label: string(),
16144
16423
  /** Hex color for timeline/legend rendering. */
16145
16424
  color: string(),
16425
+ /** Dictionary id → lucide component on the UI side. */
16426
+ iconId: string(),
16427
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
16146
16428
  icon: EventKindIconSchema,
16147
16429
  category: EventKindCategorySchema,
16430
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16431
+ parentKind: string().nullable(),
16432
+ /** Derived from `parentKind`, explicit for the client tree. */
16433
+ level: EventKindLevelSchema,
16148
16434
  /** Which cap + device contributes this kind. For built-ins the camera
16149
16435
  * itself; for sensor kinds the LINKED source device. */
16150
16436
  source: object({
@@ -16217,11 +16503,21 @@ var TrackAudioLabelSchema = object({
16217
16503
  firstAt: number(),
16218
16504
  lastAt: number()
16219
16505
  });
16506
+ /**
16507
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16508
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16509
+ * linked sensor/control state change (no positions; carries a snapshot). The
16510
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16511
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16512
+ */
16513
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16220
16514
  var TrackSchema = object({
16221
16515
  trackId: string(),
16222
16516
  deviceId: number(),
16223
16517
  className: string(),
16224
16518
  label: string().optional(),
16519
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16520
+ source: TrackSourceSchema.optional(),
16225
16521
  firstSeen: number(),
16226
16522
  lastSeen: number(),
16227
16523
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16598,6 +16894,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16598
16894
  eventId: string(),
16599
16895
  timestamp: number()
16600
16896
  });
16897
+ /**
16898
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16899
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16900
+ * caps into per-camera event-kind descriptors.
16901
+ *
16902
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16903
+ * is NOT duplicated here — every entry is derived from the single
16904
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16905
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16906
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16907
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16908
+ * eventful cap is missing.
16909
+ */
16910
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16911
+ var LEGACY_ICON = {
16912
+ motion: "motion",
16913
+ audio: "audio",
16914
+ person: "person",
16915
+ vehicle: "vehicle",
16916
+ animal: "animal",
16917
+ package: "package",
16918
+ door: "door",
16919
+ pir: "pir",
16920
+ smoke: "smoke",
16921
+ water: "water",
16922
+ button: "button",
16923
+ generic: "generic",
16924
+ gas: "smoke",
16925
+ vibration: "generic",
16926
+ tamper: "generic",
16927
+ presence: "person",
16928
+ lock: "generic",
16929
+ siren: "generic",
16930
+ switch: "generic",
16931
+ doorbell: "button"
16932
+ };
16933
+ function legacyIcon(iconId) {
16934
+ return LEGACY_ICON[iconId] ?? "generic";
16935
+ }
16936
+ /**
16937
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16938
+ * The anti-drift guard cross-checks this against the eventful caps declared
16939
+ * in `packages/types/src/capabilities/*.cap.ts`.
16940
+ */
16941
+ var CAP_TO_KIND = {
16942
+ contact: "contact",
16943
+ motion: "motion-sensor",
16944
+ smoke: "smoke",
16945
+ flood: "flood",
16946
+ gas: "gas",
16947
+ "carbon-monoxide": "carbon-monoxide",
16948
+ vibration: "vibration",
16949
+ tamper: "tamper",
16950
+ presence: "presence",
16951
+ "enum-sensor": "enum-sensor",
16952
+ "event-emitter": "device-event",
16953
+ "lock-control": "lock",
16954
+ switch: "switch",
16955
+ button: "button",
16956
+ doorbell: "doorbell"
16957
+ };
16958
+ function buildDescriptor(capName, kind) {
16959
+ const t = EVENT_TAXONOMY[kind];
16960
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16961
+ return {
16962
+ ...t,
16963
+ icon: legacyIcon(t.iconId)
16964
+ };
16965
+ }
16966
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16601
16967
  var CameraPipelineConfigSchema = object({
16602
16968
  engine: PipelineEngineChoiceSchema.optional(),
16603
16969
  steps: array(PipelineStepInputSchema).readonly(),
@@ -7822,6 +7822,160 @@ function pickClosestResolution(entries, target) {
7822
7822
  }
7823
7823
  return bestAbove?.entry ?? bestBelow?.entry;
7824
7824
  }
7825
+ var COCO_TO_MACRO = {
7826
+ mapping: {
7827
+ person: "person",
7828
+ bicycle: "vehicle",
7829
+ car: "vehicle",
7830
+ motorcycle: "vehicle",
7831
+ airplane: "vehicle",
7832
+ bus: "vehicle",
7833
+ train: "vehicle",
7834
+ truck: "vehicle",
7835
+ boat: "vehicle",
7836
+ bird: "animal",
7837
+ cat: "animal",
7838
+ dog: "animal",
7839
+ horse: "animal",
7840
+ sheep: "animal",
7841
+ cow: "animal",
7842
+ elephant: "animal",
7843
+ bear: "animal",
7844
+ zebra: "animal",
7845
+ giraffe: "animal",
7846
+ suitcase: "package",
7847
+ backpack: "package",
7848
+ handbag: "package"
7849
+ },
7850
+ preserveOriginal: false
7851
+ };
7852
+ var AUDIO_MACRO_LABELS = [
7853
+ {
7854
+ id: "speech",
7855
+ name: "Speech",
7856
+ icon: "🗣️"
7857
+ },
7858
+ {
7859
+ id: "scream",
7860
+ name: "Scream / Shout",
7861
+ icon: "😱"
7862
+ },
7863
+ {
7864
+ id: "crying",
7865
+ name: "Crying / Baby",
7866
+ icon: "😢"
7867
+ },
7868
+ {
7869
+ id: "laughter",
7870
+ name: "Laughter",
7871
+ icon: "😂"
7872
+ },
7873
+ {
7874
+ id: "music",
7875
+ name: "Music",
7876
+ icon: "🎵"
7877
+ },
7878
+ {
7879
+ id: "dog",
7880
+ name: "Dog",
7881
+ icon: "🐕"
7882
+ },
7883
+ {
7884
+ id: "cat",
7885
+ name: "Cat",
7886
+ icon: "🐈"
7887
+ },
7888
+ {
7889
+ id: "bird",
7890
+ name: "Bird",
7891
+ icon: "🐦"
7892
+ },
7893
+ {
7894
+ id: "animal",
7895
+ name: "Animal (other)",
7896
+ icon: "🐾"
7897
+ },
7898
+ {
7899
+ id: "alarm",
7900
+ name: "Alarm / Siren",
7901
+ icon: "🚨"
7902
+ },
7903
+ {
7904
+ id: "doorbell",
7905
+ name: "Doorbell / Knock",
7906
+ icon: "🔔"
7907
+ },
7908
+ {
7909
+ id: "glass_breaking",
7910
+ name: "Glass Breaking",
7911
+ icon: "💥"
7912
+ },
7913
+ {
7914
+ id: "gunshot",
7915
+ name: "Gunshot / Explosion",
7916
+ icon: "💣"
7917
+ },
7918
+ {
7919
+ id: "vehicle",
7920
+ name: "Vehicle",
7921
+ icon: "🚗"
7922
+ },
7923
+ {
7924
+ id: "siren",
7925
+ name: "Emergency Siren",
7926
+ icon: "🚑"
7927
+ },
7928
+ {
7929
+ id: "fire",
7930
+ name: "Fire / Smoke",
7931
+ icon: "🔥"
7932
+ },
7933
+ {
7934
+ id: "water",
7935
+ name: "Water",
7936
+ icon: "💧"
7937
+ },
7938
+ {
7939
+ id: "wind",
7940
+ name: "Wind / Weather",
7941
+ icon: "🌬️"
7942
+ },
7943
+ {
7944
+ id: "door",
7945
+ name: "Door",
7946
+ icon: "🚪"
7947
+ },
7948
+ {
7949
+ id: "footsteps",
7950
+ name: "Footsteps",
7951
+ icon: "👣"
7952
+ },
7953
+ {
7954
+ id: "crowd",
7955
+ name: "Crowd / Chatter",
7956
+ icon: "👥"
7957
+ },
7958
+ {
7959
+ id: "telephone",
7960
+ name: "Telephone",
7961
+ icon: "📞"
7962
+ },
7963
+ {
7964
+ id: "engine",
7965
+ name: "Engine / Motor",
7966
+ icon: "⚙️"
7967
+ },
7968
+ {
7969
+ id: "tools",
7970
+ name: "Tools / Construction",
7971
+ icon: "🔨"
7972
+ },
7973
+ {
7974
+ id: "silence",
7975
+ name: "Silence",
7976
+ icon: "🤫"
7977
+ }
7978
+ ];
7825
7979
  var YAMNET_TO_MACRO = {
7826
7980
  mapping: {
7827
7981
  Speech: "speech",
@@ -8088,6 +8242,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8088
8242
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8089
8243
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8090
8244
  /**
8245
+ * Unified event-kind taxonomy — THE single source of truth for
8246
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8247
+ * icon }`.
8248
+ *
8249
+ * This dictionary folds together what used to be scattered across four
8250
+ * copies:
8251
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8252
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8253
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8254
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8255
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8256
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8257
+ *
8258
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8259
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8260
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8261
+ * color or an icon: they read this dictionary (server descriptors carry the
8262
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8263
+ *
8264
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8265
+ */
8266
+ var TAXONOMY_COLORS = {
8267
+ motion: "#f59e0b",
8268
+ audio: "#06b6d4",
8269
+ person: "#22c55e",
8270
+ vehicle: "#3b82f6",
8271
+ animal: "#f97316",
8272
+ package: "#a855f7",
8273
+ sensor: "#8b5cf6",
8274
+ control: "#10b981",
8275
+ genericDetection: "#64748b"
8276
+ };
8277
+ var DETECTION_SUB_COLORS = {
8278
+ car: "#f59e0b",
8279
+ truck: "#d97706",
8280
+ bus: "#b45309",
8281
+ motorcycle: "#eab308",
8282
+ bicycle: "#ca8a04",
8283
+ airplane: "#60a5fa",
8284
+ boat: "#2563eb",
8285
+ train: "#1d4ed8",
8286
+ bird: "#14b8a6",
8287
+ dog: "#84cc16",
8288
+ cat: "#f97316",
8289
+ horse: "#a16207",
8290
+ sheep: "#a3a3a3",
8291
+ cow: "#78716c",
8292
+ elephant: "#6b7280",
8293
+ bear: "#7c2d12",
8294
+ zebra: "#404040",
8295
+ giraffe: "#d4a373"
8296
+ };
8297
+ function titleCase(id) {
8298
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8299
+ }
8300
+ var entries = /* @__PURE__ */ new Map();
8301
+ function macro(kind, category, color, iconId, label) {
8302
+ entries.set(kind, {
8303
+ kind,
8304
+ parentKind: null,
8305
+ level: "macro",
8306
+ category,
8307
+ color,
8308
+ iconId,
8309
+ labelKey: `eventKind.${kind}`,
8310
+ label
8311
+ });
8312
+ }
8313
+ function sub(kind, parentKind, category, color, iconId, label) {
8314
+ entries.set(kind, {
8315
+ kind,
8316
+ parentKind,
8317
+ level: "sub",
8318
+ category,
8319
+ color,
8320
+ iconId,
8321
+ labelKey: `eventKind.${kind}`,
8322
+ label
8323
+ });
8324
+ }
8325
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8326
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8327
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8328
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8329
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8330
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8331
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8332
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8333
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8334
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8335
+ if (entries.has(cocoClass)) continue;
8336
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8337
+ }
8338
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8339
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8340
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8341
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8342
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8343
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8344
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8345
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8346
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8347
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8348
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8349
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8350
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8351
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8352
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8353
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8354
+ sub("button", "control", "control", "#10b981", "button", "Button");
8355
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8356
+ for (const l of AUDIO_MACRO_LABELS) {
8357
+ const kind = `audio-${l.id}`;
8358
+ if (entries.has(kind)) continue;
8359
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8360
+ }
8361
+ /** The complete taxonomy dictionary, keyed by kind. */
8362
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8363
+ /**
8091
8364
  * Error types for the safe expression engine. Two distinct classes so callers
8092
8365
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8093
8366
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -16108,17 +16381,30 @@ var EventKindCategorySchema = _enum([
16108
16381
  "audio",
16109
16382
  "detection",
16110
16383
  "sensor",
16384
+ "control",
16111
16385
  "custom",
16112
16386
  "package"
16113
16387
  ]);
16388
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16389
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
16114
16390
  var EventKindDescriptorSchema = object({
16115
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16391
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
16116
16392
  kind: string(),
16393
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16394
+ labelKey: string(),
16395
+ /** English fallback label (kept for clients that don't translate). */
16117
16396
  label: string(),
16118
16397
  /** Hex color for timeline/legend rendering. */
16119
16398
  color: string(),
16399
+ /** Dictionary id → lucide component on the UI side. */
16400
+ iconId: string(),
16401
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
16120
16402
  icon: EventKindIconSchema,
16121
16403
  category: EventKindCategorySchema,
16404
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16405
+ parentKind: string().nullable(),
16406
+ /** Derived from `parentKind`, explicit for the client tree. */
16407
+ level: EventKindLevelSchema,
16122
16408
  /** Which cap + device contributes this kind. For built-ins the camera
16123
16409
  * itself; for sensor kinds the LINKED source device. */
16124
16410
  source: object({
@@ -16191,11 +16477,21 @@ var TrackAudioLabelSchema = object({
16191
16477
  firstAt: number(),
16192
16478
  lastAt: number()
16193
16479
  });
16480
+ /**
16481
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16482
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16483
+ * linked sensor/control state change (no positions; carries a snapshot). The
16484
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16485
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16486
+ */
16487
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16194
16488
  var TrackSchema = object({
16195
16489
  trackId: string(),
16196
16490
  deviceId: number(),
16197
16491
  className: string(),
16198
16492
  label: string().optional(),
16493
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16494
+ source: TrackSourceSchema.optional(),
16199
16495
  firstSeen: number(),
16200
16496
  lastSeen: number(),
16201
16497
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16572,6 +16868,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16572
16868
  eventId: string(),
16573
16869
  timestamp: number()
16574
16870
  });
16871
+ /**
16872
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16873
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16874
+ * caps into per-camera event-kind descriptors.
16875
+ *
16876
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16877
+ * is NOT duplicated here — every entry is derived from the single
16878
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16879
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16880
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16881
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16882
+ * eventful cap is missing.
16883
+ */
16884
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16885
+ var LEGACY_ICON = {
16886
+ motion: "motion",
16887
+ audio: "audio",
16888
+ person: "person",
16889
+ vehicle: "vehicle",
16890
+ animal: "animal",
16891
+ package: "package",
16892
+ door: "door",
16893
+ pir: "pir",
16894
+ smoke: "smoke",
16895
+ water: "water",
16896
+ button: "button",
16897
+ generic: "generic",
16898
+ gas: "smoke",
16899
+ vibration: "generic",
16900
+ tamper: "generic",
16901
+ presence: "person",
16902
+ lock: "generic",
16903
+ siren: "generic",
16904
+ switch: "generic",
16905
+ doorbell: "button"
16906
+ };
16907
+ function legacyIcon(iconId) {
16908
+ return LEGACY_ICON[iconId] ?? "generic";
16909
+ }
16910
+ /**
16911
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16912
+ * The anti-drift guard cross-checks this against the eventful caps declared
16913
+ * in `packages/types/src/capabilities/*.cap.ts`.
16914
+ */
16915
+ var CAP_TO_KIND = {
16916
+ contact: "contact",
16917
+ motion: "motion-sensor",
16918
+ smoke: "smoke",
16919
+ flood: "flood",
16920
+ gas: "gas",
16921
+ "carbon-monoxide": "carbon-monoxide",
16922
+ vibration: "vibration",
16923
+ tamper: "tamper",
16924
+ presence: "presence",
16925
+ "enum-sensor": "enum-sensor",
16926
+ "event-emitter": "device-event",
16927
+ "lock-control": "lock",
16928
+ switch: "switch",
16929
+ button: "button",
16930
+ doorbell: "doorbell"
16931
+ };
16932
+ function buildDescriptor(capName, kind) {
16933
+ const t = EVENT_TAXONOMY[kind];
16934
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16935
+ return {
16936
+ ...t,
16937
+ icon: legacyIcon(t.iconId)
16938
+ };
16939
+ }
16940
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16575
16941
  var CameraPipelineConfigSchema = object({
16576
16942
  engine: PipelineEngineChoiceSchema.optional(),
16577
16943
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-alexa",
3
- "version": "1.1.26",
3
+ "version": "1.1.27",
4
4
  "description": "Alexa Smart Home Skill export — hub-side OAuth provider + directive handler. The companion AWS Lambda forwards Alexa directives to this addon's /addon/export-alexa/directive endpoint.",
5
5
  "keywords": [
6
6
  "camstack",