@camstack/addon-static-turn 1.1.27 → 1.1.28

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.
@@ -7627,6 +7627,160 @@ var EncodeProfileSchema = object({
7627
7627
  */
7628
7628
  outputArgs: array(string()).optional()
7629
7629
  });
7630
+ var COCO_TO_MACRO = {
7631
+ mapping: {
7632
+ person: "person",
7633
+ bicycle: "vehicle",
7634
+ car: "vehicle",
7635
+ motorcycle: "vehicle",
7636
+ airplane: "vehicle",
7637
+ bus: "vehicle",
7638
+ train: "vehicle",
7639
+ truck: "vehicle",
7640
+ boat: "vehicle",
7641
+ bird: "animal",
7642
+ cat: "animal",
7643
+ dog: "animal",
7644
+ horse: "animal",
7645
+ sheep: "animal",
7646
+ cow: "animal",
7647
+ elephant: "animal",
7648
+ bear: "animal",
7649
+ zebra: "animal",
7650
+ giraffe: "animal",
7651
+ suitcase: "package",
7652
+ backpack: "package",
7653
+ handbag: "package"
7654
+ },
7655
+ preserveOriginal: false
7656
+ };
7657
+ var AUDIO_MACRO_LABELS = [
7658
+ {
7659
+ id: "speech",
7660
+ name: "Speech",
7661
+ icon: "🗣️"
7662
+ },
7663
+ {
7664
+ id: "scream",
7665
+ name: "Scream / Shout",
7666
+ icon: "😱"
7667
+ },
7668
+ {
7669
+ id: "crying",
7670
+ name: "Crying / Baby",
7671
+ icon: "😢"
7672
+ },
7673
+ {
7674
+ id: "laughter",
7675
+ name: "Laughter",
7676
+ icon: "😂"
7677
+ },
7678
+ {
7679
+ id: "music",
7680
+ name: "Music",
7681
+ icon: "🎵"
7682
+ },
7683
+ {
7684
+ id: "dog",
7685
+ name: "Dog",
7686
+ icon: "🐕"
7687
+ },
7688
+ {
7689
+ id: "cat",
7690
+ name: "Cat",
7691
+ icon: "🐈"
7692
+ },
7693
+ {
7694
+ id: "bird",
7695
+ name: "Bird",
7696
+ icon: "🐦"
7697
+ },
7698
+ {
7699
+ id: "animal",
7700
+ name: "Animal (other)",
7701
+ icon: "🐾"
7702
+ },
7703
+ {
7704
+ id: "alarm",
7705
+ name: "Alarm / Siren",
7706
+ icon: "🚨"
7707
+ },
7708
+ {
7709
+ id: "doorbell",
7710
+ name: "Doorbell / Knock",
7711
+ icon: "🔔"
7712
+ },
7713
+ {
7714
+ id: "glass_breaking",
7715
+ name: "Glass Breaking",
7716
+ icon: "💥"
7717
+ },
7718
+ {
7719
+ id: "gunshot",
7720
+ name: "Gunshot / Explosion",
7721
+ icon: "💣"
7722
+ },
7723
+ {
7724
+ id: "vehicle",
7725
+ name: "Vehicle",
7726
+ icon: "🚗"
7727
+ },
7728
+ {
7729
+ id: "siren",
7730
+ name: "Emergency Siren",
7731
+ icon: "🚑"
7732
+ },
7733
+ {
7734
+ id: "fire",
7735
+ name: "Fire / Smoke",
7736
+ icon: "🔥"
7737
+ },
7738
+ {
7739
+ id: "water",
7740
+ name: "Water",
7741
+ icon: "💧"
7742
+ },
7743
+ {
7744
+ id: "wind",
7745
+ name: "Wind / Weather",
7746
+ icon: "🌬️"
7747
+ },
7748
+ {
7749
+ id: "door",
7750
+ name: "Door",
7751
+ icon: "🚪"
7752
+ },
7753
+ {
7754
+ id: "footsteps",
7755
+ name: "Footsteps",
7756
+ icon: "👣"
7757
+ },
7758
+ {
7759
+ id: "crowd",
7760
+ name: "Crowd / Chatter",
7761
+ icon: "👥"
7762
+ },
7763
+ {
7764
+ id: "telephone",
7765
+ name: "Telephone",
7766
+ icon: "📞"
7767
+ },
7768
+ {
7769
+ id: "engine",
7770
+ name: "Engine / Motor",
7771
+ icon: "⚙️"
7772
+ },
7773
+ {
7774
+ id: "tools",
7775
+ name: "Tools / Construction",
7776
+ icon: "🔨"
7777
+ },
7778
+ {
7779
+ id: "silence",
7780
+ name: "Silence",
7781
+ icon: "🤫"
7782
+ }
7783
+ ];
7630
7784
  var YAMNET_TO_MACRO = {
7631
7785
  mapping: {
7632
7786
  Speech: "speech",
@@ -7893,6 +8047,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7893
8047
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7894
8048
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7895
8049
  /**
8050
+ * Unified event-kind taxonomy — THE single source of truth for
8051
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8052
+ * icon }`.
8053
+ *
8054
+ * This dictionary folds together what used to be scattered across four
8055
+ * copies:
8056
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8057
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8058
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8059
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8060
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8061
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8062
+ *
8063
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8064
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8065
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8066
+ * color or an icon: they read this dictionary (server descriptors carry the
8067
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8068
+ *
8069
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8070
+ */
8071
+ var TAXONOMY_COLORS = {
8072
+ motion: "#f59e0b",
8073
+ audio: "#06b6d4",
8074
+ person: "#22c55e",
8075
+ vehicle: "#3b82f6",
8076
+ animal: "#f97316",
8077
+ package: "#a855f7",
8078
+ sensor: "#8b5cf6",
8079
+ control: "#10b981",
8080
+ genericDetection: "#64748b"
8081
+ };
8082
+ var DETECTION_SUB_COLORS = {
8083
+ car: "#f59e0b",
8084
+ truck: "#d97706",
8085
+ bus: "#b45309",
8086
+ motorcycle: "#eab308",
8087
+ bicycle: "#ca8a04",
8088
+ airplane: "#60a5fa",
8089
+ boat: "#2563eb",
8090
+ train: "#1d4ed8",
8091
+ bird: "#14b8a6",
8092
+ dog: "#84cc16",
8093
+ cat: "#f97316",
8094
+ horse: "#a16207",
8095
+ sheep: "#a3a3a3",
8096
+ cow: "#78716c",
8097
+ elephant: "#6b7280",
8098
+ bear: "#7c2d12",
8099
+ zebra: "#404040",
8100
+ giraffe: "#d4a373"
8101
+ };
8102
+ function titleCase(id) {
8103
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8104
+ }
8105
+ var entries = /* @__PURE__ */ new Map();
8106
+ function macro(kind, category, color, iconId, label) {
8107
+ entries.set(kind, {
8108
+ kind,
8109
+ parentKind: null,
8110
+ level: "macro",
8111
+ category,
8112
+ color,
8113
+ iconId,
8114
+ labelKey: `eventKind.${kind}`,
8115
+ label
8116
+ });
8117
+ }
8118
+ function sub(kind, parentKind, category, color, iconId, label) {
8119
+ entries.set(kind, {
8120
+ kind,
8121
+ parentKind,
8122
+ level: "sub",
8123
+ category,
8124
+ color,
8125
+ iconId,
8126
+ labelKey: `eventKind.${kind}`,
8127
+ label
8128
+ });
8129
+ }
8130
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8131
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8132
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8133
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8134
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8135
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8136
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8137
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8138
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8139
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8140
+ if (entries.has(cocoClass)) continue;
8141
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8142
+ }
8143
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8144
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8145
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8146
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8147
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8148
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8149
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8150
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8151
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8152
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8153
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8154
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8155
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8156
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8157
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8158
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8159
+ sub("button", "control", "control", "#10b981", "button", "Button");
8160
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8161
+ for (const l of AUDIO_MACRO_LABELS) {
8162
+ const kind = `audio-${l.id}`;
8163
+ if (entries.has(kind)) continue;
8164
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8165
+ }
8166
+ /** The complete taxonomy dictionary, keyed by kind. */
8167
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8168
+ /**
7896
8169
  * Error types for the safe expression engine. Two distinct classes so callers
7897
8170
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7898
8171
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -15842,17 +16115,30 @@ var EventKindCategorySchema = _enum([
15842
16115
  "audio",
15843
16116
  "detection",
15844
16117
  "sensor",
16118
+ "control",
15845
16119
  "custom",
15846
16120
  "package"
15847
16121
  ]);
16122
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16123
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
15848
16124
  var EventKindDescriptorSchema = object({
15849
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16125
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
15850
16126
  kind: string(),
16127
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16128
+ labelKey: string(),
16129
+ /** English fallback label (kept for clients that don't translate). */
15851
16130
  label: string(),
15852
16131
  /** Hex color for timeline/legend rendering. */
15853
16132
  color: string(),
16133
+ /** Dictionary id → lucide component on the UI side. */
16134
+ iconId: string(),
16135
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
15854
16136
  icon: EventKindIconSchema,
15855
16137
  category: EventKindCategorySchema,
16138
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16139
+ parentKind: string().nullable(),
16140
+ /** Derived from `parentKind`, explicit for the client tree. */
16141
+ level: EventKindLevelSchema,
15856
16142
  /** Which cap + device contributes this kind. For built-ins the camera
15857
16143
  * itself; for sensor kinds the LINKED source device. */
15858
16144
  source: object({
@@ -15925,11 +16211,21 @@ var TrackAudioLabelSchema = object({
15925
16211
  firstAt: number(),
15926
16212
  lastAt: number()
15927
16213
  });
16214
+ /**
16215
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16216
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16217
+ * linked sensor/control state change (no positions; carries a snapshot). The
16218
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16219
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16220
+ */
16221
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
15928
16222
  var TrackSchema = object({
15929
16223
  trackId: string(),
15930
16224
  deviceId: number(),
15931
16225
  className: string(),
15932
16226
  label: string().optional(),
16227
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16228
+ source: TrackSourceSchema.optional(),
15933
16229
  firstSeen: number(),
15934
16230
  lastSeen: number(),
15935
16231
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16306,6 +16602,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16306
16602
  eventId: string(),
16307
16603
  timestamp: number()
16308
16604
  });
16605
+ /**
16606
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16607
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16608
+ * caps into per-camera event-kind descriptors.
16609
+ *
16610
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16611
+ * is NOT duplicated here — every entry is derived from the single
16612
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16613
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16614
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16615
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16616
+ * eventful cap is missing.
16617
+ */
16618
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16619
+ var LEGACY_ICON = {
16620
+ motion: "motion",
16621
+ audio: "audio",
16622
+ person: "person",
16623
+ vehicle: "vehicle",
16624
+ animal: "animal",
16625
+ package: "package",
16626
+ door: "door",
16627
+ pir: "pir",
16628
+ smoke: "smoke",
16629
+ water: "water",
16630
+ button: "button",
16631
+ generic: "generic",
16632
+ gas: "smoke",
16633
+ vibration: "generic",
16634
+ tamper: "generic",
16635
+ presence: "person",
16636
+ lock: "generic",
16637
+ siren: "generic",
16638
+ switch: "generic",
16639
+ doorbell: "button"
16640
+ };
16641
+ function legacyIcon(iconId) {
16642
+ return LEGACY_ICON[iconId] ?? "generic";
16643
+ }
16644
+ /**
16645
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16646
+ * The anti-drift guard cross-checks this against the eventful caps declared
16647
+ * in `packages/types/src/capabilities/*.cap.ts`.
16648
+ */
16649
+ var CAP_TO_KIND = {
16650
+ contact: "contact",
16651
+ motion: "motion-sensor",
16652
+ smoke: "smoke",
16653
+ flood: "flood",
16654
+ gas: "gas",
16655
+ "carbon-monoxide": "carbon-monoxide",
16656
+ vibration: "vibration",
16657
+ tamper: "tamper",
16658
+ presence: "presence",
16659
+ "enum-sensor": "enum-sensor",
16660
+ "event-emitter": "device-event",
16661
+ "lock-control": "lock",
16662
+ switch: "switch",
16663
+ button: "button",
16664
+ doorbell: "doorbell"
16665
+ };
16666
+ function buildDescriptor(capName, kind) {
16667
+ const t = EVENT_TAXONOMY[kind];
16668
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16669
+ return {
16670
+ ...t,
16671
+ icon: legacyIcon(t.iconId)
16672
+ };
16673
+ }
16674
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16309
16675
  var CameraPipelineConfigSchema = object({
16310
16676
  engine: PipelineEngineChoiceSchema.optional(),
16311
16677
  steps: array(PipelineStepInputSchema).readonly(),
@@ -7626,6 +7626,160 @@ var EncodeProfileSchema = object({
7626
7626
  */
7627
7627
  outputArgs: array(string()).optional()
7628
7628
  });
7629
+ var COCO_TO_MACRO = {
7630
+ mapping: {
7631
+ person: "person",
7632
+ bicycle: "vehicle",
7633
+ car: "vehicle",
7634
+ motorcycle: "vehicle",
7635
+ airplane: "vehicle",
7636
+ bus: "vehicle",
7637
+ train: "vehicle",
7638
+ truck: "vehicle",
7639
+ boat: "vehicle",
7640
+ bird: "animal",
7641
+ cat: "animal",
7642
+ dog: "animal",
7643
+ horse: "animal",
7644
+ sheep: "animal",
7645
+ cow: "animal",
7646
+ elephant: "animal",
7647
+ bear: "animal",
7648
+ zebra: "animal",
7649
+ giraffe: "animal",
7650
+ suitcase: "package",
7651
+ backpack: "package",
7652
+ handbag: "package"
7653
+ },
7654
+ preserveOriginal: false
7655
+ };
7656
+ var AUDIO_MACRO_LABELS = [
7657
+ {
7658
+ id: "speech",
7659
+ name: "Speech",
7660
+ icon: "🗣️"
7661
+ },
7662
+ {
7663
+ id: "scream",
7664
+ name: "Scream / Shout",
7665
+ icon: "😱"
7666
+ },
7667
+ {
7668
+ id: "crying",
7669
+ name: "Crying / Baby",
7670
+ icon: "😢"
7671
+ },
7672
+ {
7673
+ id: "laughter",
7674
+ name: "Laughter",
7675
+ icon: "😂"
7676
+ },
7677
+ {
7678
+ id: "music",
7679
+ name: "Music",
7680
+ icon: "🎵"
7681
+ },
7682
+ {
7683
+ id: "dog",
7684
+ name: "Dog",
7685
+ icon: "🐕"
7686
+ },
7687
+ {
7688
+ id: "cat",
7689
+ name: "Cat",
7690
+ icon: "🐈"
7691
+ },
7692
+ {
7693
+ id: "bird",
7694
+ name: "Bird",
7695
+ icon: "🐦"
7696
+ },
7697
+ {
7698
+ id: "animal",
7699
+ name: "Animal (other)",
7700
+ icon: "🐾"
7701
+ },
7702
+ {
7703
+ id: "alarm",
7704
+ name: "Alarm / Siren",
7705
+ icon: "🚨"
7706
+ },
7707
+ {
7708
+ id: "doorbell",
7709
+ name: "Doorbell / Knock",
7710
+ icon: "🔔"
7711
+ },
7712
+ {
7713
+ id: "glass_breaking",
7714
+ name: "Glass Breaking",
7715
+ icon: "💥"
7716
+ },
7717
+ {
7718
+ id: "gunshot",
7719
+ name: "Gunshot / Explosion",
7720
+ icon: "💣"
7721
+ },
7722
+ {
7723
+ id: "vehicle",
7724
+ name: "Vehicle",
7725
+ icon: "🚗"
7726
+ },
7727
+ {
7728
+ id: "siren",
7729
+ name: "Emergency Siren",
7730
+ icon: "🚑"
7731
+ },
7732
+ {
7733
+ id: "fire",
7734
+ name: "Fire / Smoke",
7735
+ icon: "🔥"
7736
+ },
7737
+ {
7738
+ id: "water",
7739
+ name: "Water",
7740
+ icon: "💧"
7741
+ },
7742
+ {
7743
+ id: "wind",
7744
+ name: "Wind / Weather",
7745
+ icon: "🌬️"
7746
+ },
7747
+ {
7748
+ id: "door",
7749
+ name: "Door",
7750
+ icon: "🚪"
7751
+ },
7752
+ {
7753
+ id: "footsteps",
7754
+ name: "Footsteps",
7755
+ icon: "👣"
7756
+ },
7757
+ {
7758
+ id: "crowd",
7759
+ name: "Crowd / Chatter",
7760
+ icon: "👥"
7761
+ },
7762
+ {
7763
+ id: "telephone",
7764
+ name: "Telephone",
7765
+ icon: "📞"
7766
+ },
7767
+ {
7768
+ id: "engine",
7769
+ name: "Engine / Motor",
7770
+ icon: "⚙️"
7771
+ },
7772
+ {
7773
+ id: "tools",
7774
+ name: "Tools / Construction",
7775
+ icon: "🔨"
7776
+ },
7777
+ {
7778
+ id: "silence",
7779
+ name: "Silence",
7780
+ icon: "🤫"
7781
+ }
7782
+ ];
7629
7783
  var YAMNET_TO_MACRO = {
7630
7784
  mapping: {
7631
7785
  Speech: "speech",
@@ -7892,6 +8046,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7892
8046
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7893
8047
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7894
8048
  /**
8049
+ * Unified event-kind taxonomy — THE single source of truth for
8050
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8051
+ * icon }`.
8052
+ *
8053
+ * This dictionary folds together what used to be scattered across four
8054
+ * copies:
8055
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8056
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8057
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8058
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8059
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8060
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8061
+ *
8062
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8063
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8064
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8065
+ * color or an icon: they read this dictionary (server descriptors carry the
8066
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8067
+ *
8068
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8069
+ */
8070
+ var TAXONOMY_COLORS = {
8071
+ motion: "#f59e0b",
8072
+ audio: "#06b6d4",
8073
+ person: "#22c55e",
8074
+ vehicle: "#3b82f6",
8075
+ animal: "#f97316",
8076
+ package: "#a855f7",
8077
+ sensor: "#8b5cf6",
8078
+ control: "#10b981",
8079
+ genericDetection: "#64748b"
8080
+ };
8081
+ var DETECTION_SUB_COLORS = {
8082
+ car: "#f59e0b",
8083
+ truck: "#d97706",
8084
+ bus: "#b45309",
8085
+ motorcycle: "#eab308",
8086
+ bicycle: "#ca8a04",
8087
+ airplane: "#60a5fa",
8088
+ boat: "#2563eb",
8089
+ train: "#1d4ed8",
8090
+ bird: "#14b8a6",
8091
+ dog: "#84cc16",
8092
+ cat: "#f97316",
8093
+ horse: "#a16207",
8094
+ sheep: "#a3a3a3",
8095
+ cow: "#78716c",
8096
+ elephant: "#6b7280",
8097
+ bear: "#7c2d12",
8098
+ zebra: "#404040",
8099
+ giraffe: "#d4a373"
8100
+ };
8101
+ function titleCase(id) {
8102
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8103
+ }
8104
+ var entries = /* @__PURE__ */ new Map();
8105
+ function macro(kind, category, color, iconId, label) {
8106
+ entries.set(kind, {
8107
+ kind,
8108
+ parentKind: null,
8109
+ level: "macro",
8110
+ category,
8111
+ color,
8112
+ iconId,
8113
+ labelKey: `eventKind.${kind}`,
8114
+ label
8115
+ });
8116
+ }
8117
+ function sub(kind, parentKind, category, color, iconId, label) {
8118
+ entries.set(kind, {
8119
+ kind,
8120
+ parentKind,
8121
+ level: "sub",
8122
+ category,
8123
+ color,
8124
+ iconId,
8125
+ labelKey: `eventKind.${kind}`,
8126
+ label
8127
+ });
8128
+ }
8129
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8130
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8131
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8132
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8133
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8134
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8135
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8136
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8137
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8138
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8139
+ if (entries.has(cocoClass)) continue;
8140
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8141
+ }
8142
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8143
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8144
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8145
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8146
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8147
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8148
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8149
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8150
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8151
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8152
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8153
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8154
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8155
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8156
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8157
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8158
+ sub("button", "control", "control", "#10b981", "button", "Button");
8159
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8160
+ for (const l of AUDIO_MACRO_LABELS) {
8161
+ const kind = `audio-${l.id}`;
8162
+ if (entries.has(kind)) continue;
8163
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8164
+ }
8165
+ /** The complete taxonomy dictionary, keyed by kind. */
8166
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8167
+ /**
7895
8168
  * Error types for the safe expression engine. Two distinct classes so callers
7896
8169
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7897
8170
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -15841,17 +16114,30 @@ var EventKindCategorySchema = _enum([
15841
16114
  "audio",
15842
16115
  "detection",
15843
16116
  "sensor",
16117
+ "control",
15844
16118
  "custom",
15845
16119
  "package"
15846
16120
  ]);
16121
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16122
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
15847
16123
  var EventKindDescriptorSchema = object({
15848
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16124
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
15849
16125
  kind: string(),
16126
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16127
+ labelKey: string(),
16128
+ /** English fallback label (kept for clients that don't translate). */
15850
16129
  label: string(),
15851
16130
  /** Hex color for timeline/legend rendering. */
15852
16131
  color: string(),
16132
+ /** Dictionary id → lucide component on the UI side. */
16133
+ iconId: string(),
16134
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
15853
16135
  icon: EventKindIconSchema,
15854
16136
  category: EventKindCategorySchema,
16137
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16138
+ parentKind: string().nullable(),
16139
+ /** Derived from `parentKind`, explicit for the client tree. */
16140
+ level: EventKindLevelSchema,
15855
16141
  /** Which cap + device contributes this kind. For built-ins the camera
15856
16142
  * itself; for sensor kinds the LINKED source device. */
15857
16143
  source: object({
@@ -15924,11 +16210,21 @@ var TrackAudioLabelSchema = object({
15924
16210
  firstAt: number(),
15925
16211
  lastAt: number()
15926
16212
  });
16213
+ /**
16214
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16215
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16216
+ * linked sensor/control state change (no positions; carries a snapshot). The
16217
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16218
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16219
+ */
16220
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
15927
16221
  var TrackSchema = object({
15928
16222
  trackId: string(),
15929
16223
  deviceId: number(),
15930
16224
  className: string(),
15931
16225
  label: string().optional(),
16226
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16227
+ source: TrackSourceSchema.optional(),
15932
16228
  firstSeen: number(),
15933
16229
  lastSeen: number(),
15934
16230
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16305,6 +16601,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16305
16601
  eventId: string(),
16306
16602
  timestamp: number()
16307
16603
  });
16604
+ /**
16605
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16606
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16607
+ * caps into per-camera event-kind descriptors.
16608
+ *
16609
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16610
+ * is NOT duplicated here — every entry is derived from the single
16611
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16612
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16613
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16614
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16615
+ * eventful cap is missing.
16616
+ */
16617
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16618
+ var LEGACY_ICON = {
16619
+ motion: "motion",
16620
+ audio: "audio",
16621
+ person: "person",
16622
+ vehicle: "vehicle",
16623
+ animal: "animal",
16624
+ package: "package",
16625
+ door: "door",
16626
+ pir: "pir",
16627
+ smoke: "smoke",
16628
+ water: "water",
16629
+ button: "button",
16630
+ generic: "generic",
16631
+ gas: "smoke",
16632
+ vibration: "generic",
16633
+ tamper: "generic",
16634
+ presence: "person",
16635
+ lock: "generic",
16636
+ siren: "generic",
16637
+ switch: "generic",
16638
+ doorbell: "button"
16639
+ };
16640
+ function legacyIcon(iconId) {
16641
+ return LEGACY_ICON[iconId] ?? "generic";
16642
+ }
16643
+ /**
16644
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16645
+ * The anti-drift guard cross-checks this against the eventful caps declared
16646
+ * in `packages/types/src/capabilities/*.cap.ts`.
16647
+ */
16648
+ var CAP_TO_KIND = {
16649
+ contact: "contact",
16650
+ motion: "motion-sensor",
16651
+ smoke: "smoke",
16652
+ flood: "flood",
16653
+ gas: "gas",
16654
+ "carbon-monoxide": "carbon-monoxide",
16655
+ vibration: "vibration",
16656
+ tamper: "tamper",
16657
+ presence: "presence",
16658
+ "enum-sensor": "enum-sensor",
16659
+ "event-emitter": "device-event",
16660
+ "lock-control": "lock",
16661
+ switch: "switch",
16662
+ button: "button",
16663
+ doorbell: "doorbell"
16664
+ };
16665
+ function buildDescriptor(capName, kind) {
16666
+ const t = EVENT_TAXONOMY[kind];
16667
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16668
+ return {
16669
+ ...t,
16670
+ icon: legacyIcon(t.iconId)
16671
+ };
16672
+ }
16673
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16308
16674
  var CameraPipelineConfigSchema = object({
16309
16675
  engine: PipelineEngineChoiceSchema.optional(),
16310
16676
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-static-turn",
3
- "version": "1.1.27",
3
+ "version": "1.1.28",
4
4
  "description": "Static / self-hosted (coturn) TURN provider for CamStack",
5
5
  "keywords": [
6
6
  "camstack",