@camstack/addon-decoder-ffmpeg 1.1.18 → 1.1.19

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.
package/dist/index.js CHANGED
@@ -7644,6 +7644,160 @@ var EncodeProfileSchema = object({
7644
7644
  */
7645
7645
  outputArgs: array(string()).optional()
7646
7646
  });
7647
+ var COCO_TO_MACRO = {
7648
+ mapping: {
7649
+ person: "person",
7650
+ bicycle: "vehicle",
7651
+ car: "vehicle",
7652
+ motorcycle: "vehicle",
7653
+ airplane: "vehicle",
7654
+ bus: "vehicle",
7655
+ train: "vehicle",
7656
+ truck: "vehicle",
7657
+ boat: "vehicle",
7658
+ bird: "animal",
7659
+ cat: "animal",
7660
+ dog: "animal",
7661
+ horse: "animal",
7662
+ sheep: "animal",
7663
+ cow: "animal",
7664
+ elephant: "animal",
7665
+ bear: "animal",
7666
+ zebra: "animal",
7667
+ giraffe: "animal",
7668
+ suitcase: "package",
7669
+ backpack: "package",
7670
+ handbag: "package"
7671
+ },
7672
+ preserveOriginal: false
7673
+ };
7674
+ var AUDIO_MACRO_LABELS = [
7675
+ {
7676
+ id: "speech",
7677
+ name: "Speech",
7678
+ icon: "🗣️"
7679
+ },
7680
+ {
7681
+ id: "scream",
7682
+ name: "Scream / Shout",
7683
+ icon: "😱"
7684
+ },
7685
+ {
7686
+ id: "crying",
7687
+ name: "Crying / Baby",
7688
+ icon: "😢"
7689
+ },
7690
+ {
7691
+ id: "laughter",
7692
+ name: "Laughter",
7693
+ icon: "😂"
7694
+ },
7695
+ {
7696
+ id: "music",
7697
+ name: "Music",
7698
+ icon: "🎵"
7699
+ },
7700
+ {
7701
+ id: "dog",
7702
+ name: "Dog",
7703
+ icon: "🐕"
7704
+ },
7705
+ {
7706
+ id: "cat",
7707
+ name: "Cat",
7708
+ icon: "🐈"
7709
+ },
7710
+ {
7711
+ id: "bird",
7712
+ name: "Bird",
7713
+ icon: "🐦"
7714
+ },
7715
+ {
7716
+ id: "animal",
7717
+ name: "Animal (other)",
7718
+ icon: "🐾"
7719
+ },
7720
+ {
7721
+ id: "alarm",
7722
+ name: "Alarm / Siren",
7723
+ icon: "🚨"
7724
+ },
7725
+ {
7726
+ id: "doorbell",
7727
+ name: "Doorbell / Knock",
7728
+ icon: "🔔"
7729
+ },
7730
+ {
7731
+ id: "glass_breaking",
7732
+ name: "Glass Breaking",
7733
+ icon: "💥"
7734
+ },
7735
+ {
7736
+ id: "gunshot",
7737
+ name: "Gunshot / Explosion",
7738
+ icon: "💣"
7739
+ },
7740
+ {
7741
+ id: "vehicle",
7742
+ name: "Vehicle",
7743
+ icon: "🚗"
7744
+ },
7745
+ {
7746
+ id: "siren",
7747
+ name: "Emergency Siren",
7748
+ icon: "🚑"
7749
+ },
7750
+ {
7751
+ id: "fire",
7752
+ name: "Fire / Smoke",
7753
+ icon: "🔥"
7754
+ },
7755
+ {
7756
+ id: "water",
7757
+ name: "Water",
7758
+ icon: "💧"
7759
+ },
7760
+ {
7761
+ id: "wind",
7762
+ name: "Wind / Weather",
7763
+ icon: "🌬️"
7764
+ },
7765
+ {
7766
+ id: "door",
7767
+ name: "Door",
7768
+ icon: "🚪"
7769
+ },
7770
+ {
7771
+ id: "footsteps",
7772
+ name: "Footsteps",
7773
+ icon: "👣"
7774
+ },
7775
+ {
7776
+ id: "crowd",
7777
+ name: "Crowd / Chatter",
7778
+ icon: "👥"
7779
+ },
7780
+ {
7781
+ id: "telephone",
7782
+ name: "Telephone",
7783
+ icon: "📞"
7784
+ },
7785
+ {
7786
+ id: "engine",
7787
+ name: "Engine / Motor",
7788
+ icon: "⚙️"
7789
+ },
7790
+ {
7791
+ id: "tools",
7792
+ name: "Tools / Construction",
7793
+ icon: "🔨"
7794
+ },
7795
+ {
7796
+ id: "silence",
7797
+ name: "Silence",
7798
+ icon: "🤫"
7799
+ }
7800
+ ];
7647
7801
  var YAMNET_TO_MACRO = {
7648
7802
  mapping: {
7649
7803
  Speech: "speech",
@@ -7910,6 +8064,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7910
8064
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7911
8065
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7912
8066
  /**
8067
+ * Unified event-kind taxonomy — THE single source of truth for
8068
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8069
+ * icon }`.
8070
+ *
8071
+ * This dictionary folds together what used to be scattered across four
8072
+ * copies:
8073
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8074
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8075
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8076
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8077
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8078
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8079
+ *
8080
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8081
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8082
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8083
+ * color or an icon: they read this dictionary (server descriptors carry the
8084
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8085
+ *
8086
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8087
+ */
8088
+ var TAXONOMY_COLORS = {
8089
+ motion: "#f59e0b",
8090
+ audio: "#06b6d4",
8091
+ person: "#22c55e",
8092
+ vehicle: "#3b82f6",
8093
+ animal: "#f97316",
8094
+ package: "#a855f7",
8095
+ sensor: "#8b5cf6",
8096
+ control: "#10b981",
8097
+ genericDetection: "#64748b"
8098
+ };
8099
+ var DETECTION_SUB_COLORS = {
8100
+ car: "#f59e0b",
8101
+ truck: "#d97706",
8102
+ bus: "#b45309",
8103
+ motorcycle: "#eab308",
8104
+ bicycle: "#ca8a04",
8105
+ airplane: "#60a5fa",
8106
+ boat: "#2563eb",
8107
+ train: "#1d4ed8",
8108
+ bird: "#14b8a6",
8109
+ dog: "#84cc16",
8110
+ cat: "#f97316",
8111
+ horse: "#a16207",
8112
+ sheep: "#a3a3a3",
8113
+ cow: "#78716c",
8114
+ elephant: "#6b7280",
8115
+ bear: "#7c2d12",
8116
+ zebra: "#404040",
8117
+ giraffe: "#d4a373"
8118
+ };
8119
+ function titleCase(id) {
8120
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8121
+ }
8122
+ var entries = /* @__PURE__ */ new Map();
8123
+ function macro(kind, category, color, iconId, label) {
8124
+ entries.set(kind, {
8125
+ kind,
8126
+ parentKind: null,
8127
+ level: "macro",
8128
+ category,
8129
+ color,
8130
+ iconId,
8131
+ labelKey: `eventKind.${kind}`,
8132
+ label
8133
+ });
8134
+ }
8135
+ function sub(kind, parentKind, category, color, iconId, label) {
8136
+ entries.set(kind, {
8137
+ kind,
8138
+ parentKind,
8139
+ level: "sub",
8140
+ category,
8141
+ color,
8142
+ iconId,
8143
+ labelKey: `eventKind.${kind}`,
8144
+ label
8145
+ });
8146
+ }
8147
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8148
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8149
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8150
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8151
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8152
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8153
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8154
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8155
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8156
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8157
+ if (entries.has(cocoClass)) continue;
8158
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8159
+ }
8160
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8161
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8162
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8163
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8164
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8165
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8166
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8167
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8168
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8169
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8170
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8171
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8172
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8173
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8174
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8175
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8176
+ sub("button", "control", "control", "#10b981", "button", "Button");
8177
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8178
+ for (const l of AUDIO_MACRO_LABELS) {
8179
+ const kind = `audio-${l.id}`;
8180
+ if (entries.has(kind)) continue;
8181
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8182
+ }
8183
+ /** The complete taxonomy dictionary, keyed by kind. */
8184
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8185
+ /**
7913
8186
  * Error types for the safe expression engine. Two distinct classes so callers
7914
8187
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7915
8188
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -15989,17 +16262,30 @@ var EventKindCategorySchema = _enum([
15989
16262
  "audio",
15990
16263
  "detection",
15991
16264
  "sensor",
16265
+ "control",
15992
16266
  "custom",
15993
16267
  "package"
15994
16268
  ]);
16269
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16270
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
15995
16271
  var EventKindDescriptorSchema = object({
15996
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16272
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
15997
16273
  kind: string(),
16274
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16275
+ labelKey: string(),
16276
+ /** English fallback label (kept for clients that don't translate). */
15998
16277
  label: string(),
15999
16278
  /** Hex color for timeline/legend rendering. */
16000
16279
  color: string(),
16280
+ /** Dictionary id → lucide component on the UI side. */
16281
+ iconId: string(),
16282
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
16001
16283
  icon: EventKindIconSchema,
16002
16284
  category: EventKindCategorySchema,
16285
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16286
+ parentKind: string().nullable(),
16287
+ /** Derived from `parentKind`, explicit for the client tree. */
16288
+ level: EventKindLevelSchema,
16003
16289
  /** Which cap + device contributes this kind. For built-ins the camera
16004
16290
  * itself; for sensor kinds the LINKED source device. */
16005
16291
  source: object({
@@ -16072,11 +16358,21 @@ var TrackAudioLabelSchema = object({
16072
16358
  firstAt: number(),
16073
16359
  lastAt: number()
16074
16360
  });
16361
+ /**
16362
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16363
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16364
+ * linked sensor/control state change (no positions; carries a snapshot). The
16365
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16366
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16367
+ */
16368
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16075
16369
  var TrackSchema = object({
16076
16370
  trackId: string(),
16077
16371
  deviceId: number(),
16078
16372
  className: string(),
16079
16373
  label: string().optional(),
16374
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16375
+ source: TrackSourceSchema.optional(),
16080
16376
  firstSeen: number(),
16081
16377
  lastSeen: number(),
16082
16378
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16453,6 +16749,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16453
16749
  eventId: string(),
16454
16750
  timestamp: number()
16455
16751
  });
16752
+ /**
16753
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16754
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16755
+ * caps into per-camera event-kind descriptors.
16756
+ *
16757
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16758
+ * is NOT duplicated here — every entry is derived from the single
16759
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16760
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16761
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16762
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16763
+ * eventful cap is missing.
16764
+ */
16765
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16766
+ var LEGACY_ICON = {
16767
+ motion: "motion",
16768
+ audio: "audio",
16769
+ person: "person",
16770
+ vehicle: "vehicle",
16771
+ animal: "animal",
16772
+ package: "package",
16773
+ door: "door",
16774
+ pir: "pir",
16775
+ smoke: "smoke",
16776
+ water: "water",
16777
+ button: "button",
16778
+ generic: "generic",
16779
+ gas: "smoke",
16780
+ vibration: "generic",
16781
+ tamper: "generic",
16782
+ presence: "person",
16783
+ lock: "generic",
16784
+ siren: "generic",
16785
+ switch: "generic",
16786
+ doorbell: "button"
16787
+ };
16788
+ function legacyIcon(iconId) {
16789
+ return LEGACY_ICON[iconId] ?? "generic";
16790
+ }
16791
+ /**
16792
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16793
+ * The anti-drift guard cross-checks this against the eventful caps declared
16794
+ * in `packages/types/src/capabilities/*.cap.ts`.
16795
+ */
16796
+ var CAP_TO_KIND = {
16797
+ contact: "contact",
16798
+ motion: "motion-sensor",
16799
+ smoke: "smoke",
16800
+ flood: "flood",
16801
+ gas: "gas",
16802
+ "carbon-monoxide": "carbon-monoxide",
16803
+ vibration: "vibration",
16804
+ tamper: "tamper",
16805
+ presence: "presence",
16806
+ "enum-sensor": "enum-sensor",
16807
+ "event-emitter": "device-event",
16808
+ "lock-control": "lock",
16809
+ switch: "switch",
16810
+ button: "button",
16811
+ doorbell: "doorbell"
16812
+ };
16813
+ function buildDescriptor(capName, kind) {
16814
+ const t = EVENT_TAXONOMY[kind];
16815
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16816
+ return {
16817
+ ...t,
16818
+ icon: legacyIcon(t.iconId)
16819
+ };
16820
+ }
16821
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16456
16822
  var CameraPipelineConfigSchema = object({
16457
16823
  engine: PipelineEngineChoiceSchema.optional(),
16458
16824
  steps: array(PipelineStepInputSchema).readonly(),
package/dist/index.mjs CHANGED
@@ -7640,6 +7640,160 @@ var EncodeProfileSchema = object({
7640
7640
  */
7641
7641
  outputArgs: array(string()).optional()
7642
7642
  });
7643
+ var COCO_TO_MACRO = {
7644
+ mapping: {
7645
+ person: "person",
7646
+ bicycle: "vehicle",
7647
+ car: "vehicle",
7648
+ motorcycle: "vehicle",
7649
+ airplane: "vehicle",
7650
+ bus: "vehicle",
7651
+ train: "vehicle",
7652
+ truck: "vehicle",
7653
+ boat: "vehicle",
7654
+ bird: "animal",
7655
+ cat: "animal",
7656
+ dog: "animal",
7657
+ horse: "animal",
7658
+ sheep: "animal",
7659
+ cow: "animal",
7660
+ elephant: "animal",
7661
+ bear: "animal",
7662
+ zebra: "animal",
7663
+ giraffe: "animal",
7664
+ suitcase: "package",
7665
+ backpack: "package",
7666
+ handbag: "package"
7667
+ },
7668
+ preserveOriginal: false
7669
+ };
7670
+ var AUDIO_MACRO_LABELS = [
7671
+ {
7672
+ id: "speech",
7673
+ name: "Speech",
7674
+ icon: "🗣️"
7675
+ },
7676
+ {
7677
+ id: "scream",
7678
+ name: "Scream / Shout",
7679
+ icon: "😱"
7680
+ },
7681
+ {
7682
+ id: "crying",
7683
+ name: "Crying / Baby",
7684
+ icon: "😢"
7685
+ },
7686
+ {
7687
+ id: "laughter",
7688
+ name: "Laughter",
7689
+ icon: "😂"
7690
+ },
7691
+ {
7692
+ id: "music",
7693
+ name: "Music",
7694
+ icon: "🎵"
7695
+ },
7696
+ {
7697
+ id: "dog",
7698
+ name: "Dog",
7699
+ icon: "🐕"
7700
+ },
7701
+ {
7702
+ id: "cat",
7703
+ name: "Cat",
7704
+ icon: "🐈"
7705
+ },
7706
+ {
7707
+ id: "bird",
7708
+ name: "Bird",
7709
+ icon: "🐦"
7710
+ },
7711
+ {
7712
+ id: "animal",
7713
+ name: "Animal (other)",
7714
+ icon: "🐾"
7715
+ },
7716
+ {
7717
+ id: "alarm",
7718
+ name: "Alarm / Siren",
7719
+ icon: "🚨"
7720
+ },
7721
+ {
7722
+ id: "doorbell",
7723
+ name: "Doorbell / Knock",
7724
+ icon: "🔔"
7725
+ },
7726
+ {
7727
+ id: "glass_breaking",
7728
+ name: "Glass Breaking",
7729
+ icon: "💥"
7730
+ },
7731
+ {
7732
+ id: "gunshot",
7733
+ name: "Gunshot / Explosion",
7734
+ icon: "💣"
7735
+ },
7736
+ {
7737
+ id: "vehicle",
7738
+ name: "Vehicle",
7739
+ icon: "🚗"
7740
+ },
7741
+ {
7742
+ id: "siren",
7743
+ name: "Emergency Siren",
7744
+ icon: "🚑"
7745
+ },
7746
+ {
7747
+ id: "fire",
7748
+ name: "Fire / Smoke",
7749
+ icon: "🔥"
7750
+ },
7751
+ {
7752
+ id: "water",
7753
+ name: "Water",
7754
+ icon: "💧"
7755
+ },
7756
+ {
7757
+ id: "wind",
7758
+ name: "Wind / Weather",
7759
+ icon: "🌬️"
7760
+ },
7761
+ {
7762
+ id: "door",
7763
+ name: "Door",
7764
+ icon: "🚪"
7765
+ },
7766
+ {
7767
+ id: "footsteps",
7768
+ name: "Footsteps",
7769
+ icon: "👣"
7770
+ },
7771
+ {
7772
+ id: "crowd",
7773
+ name: "Crowd / Chatter",
7774
+ icon: "👥"
7775
+ },
7776
+ {
7777
+ id: "telephone",
7778
+ name: "Telephone",
7779
+ icon: "📞"
7780
+ },
7781
+ {
7782
+ id: "engine",
7783
+ name: "Engine / Motor",
7784
+ icon: "⚙️"
7785
+ },
7786
+ {
7787
+ id: "tools",
7788
+ name: "Tools / Construction",
7789
+ icon: "🔨"
7790
+ },
7791
+ {
7792
+ id: "silence",
7793
+ name: "Silence",
7794
+ icon: "🤫"
7795
+ }
7796
+ ];
7643
7797
  var YAMNET_TO_MACRO = {
7644
7798
  mapping: {
7645
7799
  Speech: "speech",
@@ -7906,6 +8060,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7906
8060
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7907
8061
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7908
8062
  /**
8063
+ * Unified event-kind taxonomy — THE single source of truth for
8064
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8065
+ * icon }`.
8066
+ *
8067
+ * This dictionary folds together what used to be scattered across four
8068
+ * copies:
8069
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8070
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8071
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8072
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8073
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8074
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8075
+ *
8076
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8077
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8078
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8079
+ * color or an icon: they read this dictionary (server descriptors carry the
8080
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8081
+ *
8082
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8083
+ */
8084
+ var TAXONOMY_COLORS = {
8085
+ motion: "#f59e0b",
8086
+ audio: "#06b6d4",
8087
+ person: "#22c55e",
8088
+ vehicle: "#3b82f6",
8089
+ animal: "#f97316",
8090
+ package: "#a855f7",
8091
+ sensor: "#8b5cf6",
8092
+ control: "#10b981",
8093
+ genericDetection: "#64748b"
8094
+ };
8095
+ var DETECTION_SUB_COLORS = {
8096
+ car: "#f59e0b",
8097
+ truck: "#d97706",
8098
+ bus: "#b45309",
8099
+ motorcycle: "#eab308",
8100
+ bicycle: "#ca8a04",
8101
+ airplane: "#60a5fa",
8102
+ boat: "#2563eb",
8103
+ train: "#1d4ed8",
8104
+ bird: "#14b8a6",
8105
+ dog: "#84cc16",
8106
+ cat: "#f97316",
8107
+ horse: "#a16207",
8108
+ sheep: "#a3a3a3",
8109
+ cow: "#78716c",
8110
+ elephant: "#6b7280",
8111
+ bear: "#7c2d12",
8112
+ zebra: "#404040",
8113
+ giraffe: "#d4a373"
8114
+ };
8115
+ function titleCase(id) {
8116
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8117
+ }
8118
+ var entries = /* @__PURE__ */ new Map();
8119
+ function macro(kind, category, color, iconId, label) {
8120
+ entries.set(kind, {
8121
+ kind,
8122
+ parentKind: null,
8123
+ level: "macro",
8124
+ category,
8125
+ color,
8126
+ iconId,
8127
+ labelKey: `eventKind.${kind}`,
8128
+ label
8129
+ });
8130
+ }
8131
+ function sub(kind, parentKind, category, color, iconId, label) {
8132
+ entries.set(kind, {
8133
+ kind,
8134
+ parentKind,
8135
+ level: "sub",
8136
+ category,
8137
+ color,
8138
+ iconId,
8139
+ labelKey: `eventKind.${kind}`,
8140
+ label
8141
+ });
8142
+ }
8143
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8144
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8145
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8146
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8147
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8148
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8149
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8150
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8151
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8152
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8153
+ if (entries.has(cocoClass)) continue;
8154
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8155
+ }
8156
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8157
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8158
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8159
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8160
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8161
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8162
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8163
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8164
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8165
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8166
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8167
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8168
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8169
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8170
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8171
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8172
+ sub("button", "control", "control", "#10b981", "button", "Button");
8173
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8174
+ for (const l of AUDIO_MACRO_LABELS) {
8175
+ const kind = `audio-${l.id}`;
8176
+ if (entries.has(kind)) continue;
8177
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8178
+ }
8179
+ /** The complete taxonomy dictionary, keyed by kind. */
8180
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8181
+ /**
7909
8182
  * Error types for the safe expression engine. Two distinct classes so callers
7910
8183
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7911
8184
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -15985,17 +16258,30 @@ var EventKindCategorySchema = _enum([
15985
16258
  "audio",
15986
16259
  "detection",
15987
16260
  "sensor",
16261
+ "control",
15988
16262
  "custom",
15989
16263
  "package"
15990
16264
  ]);
16265
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16266
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
15991
16267
  var EventKindDescriptorSchema = object({
15992
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16268
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
15993
16269
  kind: string(),
16270
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16271
+ labelKey: string(),
16272
+ /** English fallback label (kept for clients that don't translate). */
15994
16273
  label: string(),
15995
16274
  /** Hex color for timeline/legend rendering. */
15996
16275
  color: string(),
16276
+ /** Dictionary id → lucide component on the UI side. */
16277
+ iconId: string(),
16278
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
15997
16279
  icon: EventKindIconSchema,
15998
16280
  category: EventKindCategorySchema,
16281
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16282
+ parentKind: string().nullable(),
16283
+ /** Derived from `parentKind`, explicit for the client tree. */
16284
+ level: EventKindLevelSchema,
15999
16285
  /** Which cap + device contributes this kind. For built-ins the camera
16000
16286
  * itself; for sensor kinds the LINKED source device. */
16001
16287
  source: object({
@@ -16068,11 +16354,21 @@ var TrackAudioLabelSchema = object({
16068
16354
  firstAt: number(),
16069
16355
  lastAt: number()
16070
16356
  });
16357
+ /**
16358
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16359
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16360
+ * linked sensor/control state change (no positions; carries a snapshot). The
16361
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16362
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16363
+ */
16364
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16071
16365
  var TrackSchema = object({
16072
16366
  trackId: string(),
16073
16367
  deviceId: number(),
16074
16368
  className: string(),
16075
16369
  label: string().optional(),
16370
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16371
+ source: TrackSourceSchema.optional(),
16076
16372
  firstSeen: number(),
16077
16373
  lastSeen: number(),
16078
16374
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16449,6 +16745,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16449
16745
  eventId: string(),
16450
16746
  timestamp: number()
16451
16747
  });
16748
+ /**
16749
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16750
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16751
+ * caps into per-camera event-kind descriptors.
16752
+ *
16753
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16754
+ * is NOT duplicated here — every entry is derived from the single
16755
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16756
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16757
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16758
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16759
+ * eventful cap is missing.
16760
+ */
16761
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16762
+ var LEGACY_ICON = {
16763
+ motion: "motion",
16764
+ audio: "audio",
16765
+ person: "person",
16766
+ vehicle: "vehicle",
16767
+ animal: "animal",
16768
+ package: "package",
16769
+ door: "door",
16770
+ pir: "pir",
16771
+ smoke: "smoke",
16772
+ water: "water",
16773
+ button: "button",
16774
+ generic: "generic",
16775
+ gas: "smoke",
16776
+ vibration: "generic",
16777
+ tamper: "generic",
16778
+ presence: "person",
16779
+ lock: "generic",
16780
+ siren: "generic",
16781
+ switch: "generic",
16782
+ doorbell: "button"
16783
+ };
16784
+ function legacyIcon(iconId) {
16785
+ return LEGACY_ICON[iconId] ?? "generic";
16786
+ }
16787
+ /**
16788
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16789
+ * The anti-drift guard cross-checks this against the eventful caps declared
16790
+ * in `packages/types/src/capabilities/*.cap.ts`.
16791
+ */
16792
+ var CAP_TO_KIND = {
16793
+ contact: "contact",
16794
+ motion: "motion-sensor",
16795
+ smoke: "smoke",
16796
+ flood: "flood",
16797
+ gas: "gas",
16798
+ "carbon-monoxide": "carbon-monoxide",
16799
+ vibration: "vibration",
16800
+ tamper: "tamper",
16801
+ presence: "presence",
16802
+ "enum-sensor": "enum-sensor",
16803
+ "event-emitter": "device-event",
16804
+ "lock-control": "lock",
16805
+ switch: "switch",
16806
+ button: "button",
16807
+ doorbell: "doorbell"
16808
+ };
16809
+ function buildDescriptor(capName, kind) {
16810
+ const t = EVENT_TAXONOMY[kind];
16811
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16812
+ return {
16813
+ ...t,
16814
+ icon: legacyIcon(t.iconId)
16815
+ };
16816
+ }
16817
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16452
16818
  var CameraPipelineConfigSchema = object({
16453
16819
  engine: PipelineEngineChoiceSchema.optional(),
16454
16820
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-decoder-ffmpeg",
3
- "version": "1.1.18",
3
+ "version": "1.1.19",
4
4
  "description": "Standalone ffmpeg-subprocess decoder fallback addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",