@camstack/addon-provider-rademacher 0.1.13 → 0.1.14

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/addon.js CHANGED
@@ -8642,6 +8642,160 @@ var EncodeProfileSchema = object({
8642
8642
  */
8643
8643
  outputArgs: array(string()).optional()
8644
8644
  });
8645
+ var COCO_TO_MACRO = {
8646
+ mapping: {
8647
+ person: "person",
8648
+ bicycle: "vehicle",
8649
+ car: "vehicle",
8650
+ motorcycle: "vehicle",
8651
+ airplane: "vehicle",
8652
+ bus: "vehicle",
8653
+ train: "vehicle",
8654
+ truck: "vehicle",
8655
+ boat: "vehicle",
8656
+ bird: "animal",
8657
+ cat: "animal",
8658
+ dog: "animal",
8659
+ horse: "animal",
8660
+ sheep: "animal",
8661
+ cow: "animal",
8662
+ elephant: "animal",
8663
+ bear: "animal",
8664
+ zebra: "animal",
8665
+ giraffe: "animal",
8666
+ suitcase: "package",
8667
+ backpack: "package",
8668
+ handbag: "package"
8669
+ },
8670
+ preserveOriginal: false
8671
+ };
8672
+ var AUDIO_MACRO_LABELS = [
8673
+ {
8674
+ id: "speech",
8675
+ name: "Speech",
8676
+ icon: "🗣️"
8677
+ },
8678
+ {
8679
+ id: "scream",
8680
+ name: "Scream / Shout",
8681
+ icon: "😱"
8682
+ },
8683
+ {
8684
+ id: "crying",
8685
+ name: "Crying / Baby",
8686
+ icon: "😢"
8687
+ },
8688
+ {
8689
+ id: "laughter",
8690
+ name: "Laughter",
8691
+ icon: "😂"
8692
+ },
8693
+ {
8694
+ id: "music",
8695
+ name: "Music",
8696
+ icon: "🎵"
8697
+ },
8698
+ {
8699
+ id: "dog",
8700
+ name: "Dog",
8701
+ icon: "🐕"
8702
+ },
8703
+ {
8704
+ id: "cat",
8705
+ name: "Cat",
8706
+ icon: "🐈"
8707
+ },
8708
+ {
8709
+ id: "bird",
8710
+ name: "Bird",
8711
+ icon: "🐦"
8712
+ },
8713
+ {
8714
+ id: "animal",
8715
+ name: "Animal (other)",
8716
+ icon: "🐾"
8717
+ },
8718
+ {
8719
+ id: "alarm",
8720
+ name: "Alarm / Siren",
8721
+ icon: "🚨"
8722
+ },
8723
+ {
8724
+ id: "doorbell",
8725
+ name: "Doorbell / Knock",
8726
+ icon: "🔔"
8727
+ },
8728
+ {
8729
+ id: "glass_breaking",
8730
+ name: "Glass Breaking",
8731
+ icon: "💥"
8732
+ },
8733
+ {
8734
+ id: "gunshot",
8735
+ name: "Gunshot / Explosion",
8736
+ icon: "💣"
8737
+ },
8738
+ {
8739
+ id: "vehicle",
8740
+ name: "Vehicle",
8741
+ icon: "🚗"
8742
+ },
8743
+ {
8744
+ id: "siren",
8745
+ name: "Emergency Siren",
8746
+ icon: "🚑"
8747
+ },
8748
+ {
8749
+ id: "fire",
8750
+ name: "Fire / Smoke",
8751
+ icon: "🔥"
8752
+ },
8753
+ {
8754
+ id: "water",
8755
+ name: "Water",
8756
+ icon: "💧"
8757
+ },
8758
+ {
8759
+ id: "wind",
8760
+ name: "Wind / Weather",
8761
+ icon: "🌬️"
8762
+ },
8763
+ {
8764
+ id: "door",
8765
+ name: "Door",
8766
+ icon: "🚪"
8767
+ },
8768
+ {
8769
+ id: "footsteps",
8770
+ name: "Footsteps",
8771
+ icon: "👣"
8772
+ },
8773
+ {
8774
+ id: "crowd",
8775
+ name: "Crowd / Chatter",
8776
+ icon: "👥"
8777
+ },
8778
+ {
8779
+ id: "telephone",
8780
+ name: "Telephone",
8781
+ icon: "📞"
8782
+ },
8783
+ {
8784
+ id: "engine",
8785
+ name: "Engine / Motor",
8786
+ icon: "⚙️"
8787
+ },
8788
+ {
8789
+ id: "tools",
8790
+ name: "Tools / Construction",
8791
+ icon: "🔨"
8792
+ },
8793
+ {
8794
+ id: "silence",
8795
+ name: "Silence",
8796
+ icon: "🤫"
8797
+ }
8798
+ ];
8645
8799
  var YAMNET_TO_MACRO = {
8646
8800
  mapping: {
8647
8801
  Speech: "speech",
@@ -8908,6 +9062,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8908
9062
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8909
9063
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8910
9064
  /**
9065
+ * Unified event-kind taxonomy — THE single source of truth for
9066
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
9067
+ * icon }`.
9068
+ *
9069
+ * This dictionary folds together what used to be scattered across four
9070
+ * copies:
9071
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
9072
+ * - `addon-post-analysis/.../services/event-kinds.ts`
9073
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
9074
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
9075
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
9076
+ * - the COCO / audio class maps (macro ↔ sub relationships)
9077
+ *
9078
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
9079
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
9080
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
9081
+ * color or an icon: they read this dictionary (server descriptors carry the
9082
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
9083
+ *
9084
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
9085
+ */
9086
+ var TAXONOMY_COLORS = {
9087
+ motion: "#f59e0b",
9088
+ audio: "#06b6d4",
9089
+ person: "#22c55e",
9090
+ vehicle: "#3b82f6",
9091
+ animal: "#f97316",
9092
+ package: "#a855f7",
9093
+ sensor: "#8b5cf6",
9094
+ control: "#10b981",
9095
+ genericDetection: "#64748b"
9096
+ };
9097
+ var DETECTION_SUB_COLORS = {
9098
+ car: "#f59e0b",
9099
+ truck: "#d97706",
9100
+ bus: "#b45309",
9101
+ motorcycle: "#eab308",
9102
+ bicycle: "#ca8a04",
9103
+ airplane: "#60a5fa",
9104
+ boat: "#2563eb",
9105
+ train: "#1d4ed8",
9106
+ bird: "#14b8a6",
9107
+ dog: "#84cc16",
9108
+ cat: "#f97316",
9109
+ horse: "#a16207",
9110
+ sheep: "#a3a3a3",
9111
+ cow: "#78716c",
9112
+ elephant: "#6b7280",
9113
+ bear: "#7c2d12",
9114
+ zebra: "#404040",
9115
+ giraffe: "#d4a373"
9116
+ };
9117
+ function titleCase(id) {
9118
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
9119
+ }
9120
+ var entries = /* @__PURE__ */ new Map();
9121
+ function macro(kind, category, color, iconId, label) {
9122
+ entries.set(kind, {
9123
+ kind,
9124
+ parentKind: null,
9125
+ level: "macro",
9126
+ category,
9127
+ color,
9128
+ iconId,
9129
+ labelKey: `eventKind.${kind}`,
9130
+ label
9131
+ });
9132
+ }
9133
+ function sub(kind, parentKind, category, color, iconId, label) {
9134
+ entries.set(kind, {
9135
+ kind,
9136
+ parentKind,
9137
+ level: "sub",
9138
+ category,
9139
+ color,
9140
+ iconId,
9141
+ labelKey: `eventKind.${kind}`,
9142
+ label
9143
+ });
9144
+ }
9145
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
9146
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
9147
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
9148
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
9149
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
9150
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
9151
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
9152
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
9153
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
9154
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
9155
+ if (entries.has(cocoClass)) continue;
9156
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
9157
+ }
9158
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
9159
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
9160
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
9161
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
9162
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
9163
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
9164
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
9165
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
9166
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
9167
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
9168
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
9169
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
9170
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
9171
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
9172
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
9173
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
9174
+ sub("button", "control", "control", "#10b981", "button", "Button");
9175
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
9176
+ for (const l of AUDIO_MACRO_LABELS) {
9177
+ const kind = `audio-${l.id}`;
9178
+ if (entries.has(kind)) continue;
9179
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
9180
+ }
9181
+ /** The complete taxonomy dictionary, keyed by kind. */
9182
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
9183
+ /**
8911
9184
  * Error types for the safe expression engine. Two distinct classes so callers
8912
9185
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8913
9186
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -19840,17 +20113,30 @@ var EventKindCategorySchema = _enum([
19840
20113
  "audio",
19841
20114
  "detection",
19842
20115
  "sensor",
20116
+ "control",
19843
20117
  "custom",
19844
20118
  "package"
19845
20119
  ]);
20120
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
20121
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
19846
20122
  var EventKindDescriptorSchema = object({
19847
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
20123
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
19848
20124
  kind: string(),
20125
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
20126
+ labelKey: string(),
20127
+ /** English fallback label (kept for clients that don't translate). */
19849
20128
  label: string(),
19850
20129
  /** Hex color for timeline/legend rendering. */
19851
20130
  color: string(),
20131
+ /** Dictionary id → lucide component on the UI side. */
20132
+ iconId: string(),
20133
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
19852
20134
  icon: EventKindIconSchema,
19853
20135
  category: EventKindCategorySchema,
20136
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
20137
+ parentKind: string().nullable(),
20138
+ /** Derived from `parentKind`, explicit for the client tree. */
20139
+ level: EventKindLevelSchema,
19854
20140
  /** Which cap + device contributes this kind. For built-ins the camera
19855
20141
  * itself; for sensor kinds the LINKED source device. */
19856
20142
  source: object({
@@ -19923,11 +20209,21 @@ var TrackAudioLabelSchema = object({
19923
20209
  firstAt: number(),
19924
20210
  lastAt: number()
19925
20211
  });
20212
+ /**
20213
+ * How a track was produced. `pipeline` (default / absent) = the spatial
20214
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
20215
+ * linked sensor/control state change (no positions; carries a snapshot). The
20216
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
20217
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
20218
+ */
20219
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19926
20220
  var TrackSchema = object({
19927
20221
  trackId: string(),
19928
20222
  deviceId: number(),
19929
20223
  className: string(),
19930
20224
  label: string().optional(),
20225
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
20226
+ source: TrackSourceSchema.optional(),
19931
20227
  firstSeen: number(),
19932
20228
  lastSeen: number(),
19933
20229
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -20304,6 +20600,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20304
20600
  eventId: string(),
20305
20601
  timestamp: number()
20306
20602
  });
20603
+ /**
20604
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
20605
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
20606
+ * caps into per-camera event-kind descriptors.
20607
+ *
20608
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
20609
+ * is NOT duplicated here — every entry is derived from the single
20610
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
20611
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
20612
+ * control cap means adding one line here (and a taxonomy entry); the anti-
20613
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
20614
+ * eventful cap is missing.
20615
+ */
20616
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
20617
+ var LEGACY_ICON = {
20618
+ motion: "motion",
20619
+ audio: "audio",
20620
+ person: "person",
20621
+ vehicle: "vehicle",
20622
+ animal: "animal",
20623
+ package: "package",
20624
+ door: "door",
20625
+ pir: "pir",
20626
+ smoke: "smoke",
20627
+ water: "water",
20628
+ button: "button",
20629
+ generic: "generic",
20630
+ gas: "smoke",
20631
+ vibration: "generic",
20632
+ tamper: "generic",
20633
+ presence: "person",
20634
+ lock: "generic",
20635
+ siren: "generic",
20636
+ switch: "generic",
20637
+ doorbell: "button"
20638
+ };
20639
+ function legacyIcon(iconId) {
20640
+ return LEGACY_ICON[iconId] ?? "generic";
20641
+ }
20642
+ /**
20643
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
20644
+ * The anti-drift guard cross-checks this against the eventful caps declared
20645
+ * in `packages/types/src/capabilities/*.cap.ts`.
20646
+ */
20647
+ var CAP_TO_KIND = {
20648
+ contact: "contact",
20649
+ motion: "motion-sensor",
20650
+ smoke: "smoke",
20651
+ flood: "flood",
20652
+ gas: "gas",
20653
+ "carbon-monoxide": "carbon-monoxide",
20654
+ vibration: "vibration",
20655
+ tamper: "tamper",
20656
+ presence: "presence",
20657
+ "enum-sensor": "enum-sensor",
20658
+ "event-emitter": "device-event",
20659
+ "lock-control": "lock",
20660
+ switch: "switch",
20661
+ button: "button",
20662
+ doorbell: "doorbell"
20663
+ };
20664
+ function buildDescriptor(capName, kind) {
20665
+ const t = EVENT_TAXONOMY[kind];
20666
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
20667
+ return {
20668
+ ...t,
20669
+ icon: legacyIcon(t.iconId)
20670
+ };
20671
+ }
20672
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
20307
20673
  var CameraPipelineConfigSchema = object({
20308
20674
  engine: PipelineEngineChoiceSchema.optional(),
20309
20675
  steps: array(PipelineStepInputSchema).readonly(),
package/dist/addon.mjs CHANGED
@@ -8641,6 +8641,160 @@ var EncodeProfileSchema = object({
8641
8641
  */
8642
8642
  outputArgs: array(string()).optional()
8643
8643
  });
8644
+ var COCO_TO_MACRO = {
8645
+ mapping: {
8646
+ person: "person",
8647
+ bicycle: "vehicle",
8648
+ car: "vehicle",
8649
+ motorcycle: "vehicle",
8650
+ airplane: "vehicle",
8651
+ bus: "vehicle",
8652
+ train: "vehicle",
8653
+ truck: "vehicle",
8654
+ boat: "vehicle",
8655
+ bird: "animal",
8656
+ cat: "animal",
8657
+ dog: "animal",
8658
+ horse: "animal",
8659
+ sheep: "animal",
8660
+ cow: "animal",
8661
+ elephant: "animal",
8662
+ bear: "animal",
8663
+ zebra: "animal",
8664
+ giraffe: "animal",
8665
+ suitcase: "package",
8666
+ backpack: "package",
8667
+ handbag: "package"
8668
+ },
8669
+ preserveOriginal: false
8670
+ };
8671
+ var AUDIO_MACRO_LABELS = [
8672
+ {
8673
+ id: "speech",
8674
+ name: "Speech",
8675
+ icon: "🗣️"
8676
+ },
8677
+ {
8678
+ id: "scream",
8679
+ name: "Scream / Shout",
8680
+ icon: "😱"
8681
+ },
8682
+ {
8683
+ id: "crying",
8684
+ name: "Crying / Baby",
8685
+ icon: "😢"
8686
+ },
8687
+ {
8688
+ id: "laughter",
8689
+ name: "Laughter",
8690
+ icon: "😂"
8691
+ },
8692
+ {
8693
+ id: "music",
8694
+ name: "Music",
8695
+ icon: "🎵"
8696
+ },
8697
+ {
8698
+ id: "dog",
8699
+ name: "Dog",
8700
+ icon: "🐕"
8701
+ },
8702
+ {
8703
+ id: "cat",
8704
+ name: "Cat",
8705
+ icon: "🐈"
8706
+ },
8707
+ {
8708
+ id: "bird",
8709
+ name: "Bird",
8710
+ icon: "🐦"
8711
+ },
8712
+ {
8713
+ id: "animal",
8714
+ name: "Animal (other)",
8715
+ icon: "🐾"
8716
+ },
8717
+ {
8718
+ id: "alarm",
8719
+ name: "Alarm / Siren",
8720
+ icon: "🚨"
8721
+ },
8722
+ {
8723
+ id: "doorbell",
8724
+ name: "Doorbell / Knock",
8725
+ icon: "🔔"
8726
+ },
8727
+ {
8728
+ id: "glass_breaking",
8729
+ name: "Glass Breaking",
8730
+ icon: "💥"
8731
+ },
8732
+ {
8733
+ id: "gunshot",
8734
+ name: "Gunshot / Explosion",
8735
+ icon: "💣"
8736
+ },
8737
+ {
8738
+ id: "vehicle",
8739
+ name: "Vehicle",
8740
+ icon: "🚗"
8741
+ },
8742
+ {
8743
+ id: "siren",
8744
+ name: "Emergency Siren",
8745
+ icon: "🚑"
8746
+ },
8747
+ {
8748
+ id: "fire",
8749
+ name: "Fire / Smoke",
8750
+ icon: "🔥"
8751
+ },
8752
+ {
8753
+ id: "water",
8754
+ name: "Water",
8755
+ icon: "💧"
8756
+ },
8757
+ {
8758
+ id: "wind",
8759
+ name: "Wind / Weather",
8760
+ icon: "🌬️"
8761
+ },
8762
+ {
8763
+ id: "door",
8764
+ name: "Door",
8765
+ icon: "🚪"
8766
+ },
8767
+ {
8768
+ id: "footsteps",
8769
+ name: "Footsteps",
8770
+ icon: "👣"
8771
+ },
8772
+ {
8773
+ id: "crowd",
8774
+ name: "Crowd / Chatter",
8775
+ icon: "👥"
8776
+ },
8777
+ {
8778
+ id: "telephone",
8779
+ name: "Telephone",
8780
+ icon: "📞"
8781
+ },
8782
+ {
8783
+ id: "engine",
8784
+ name: "Engine / Motor",
8785
+ icon: "⚙️"
8786
+ },
8787
+ {
8788
+ id: "tools",
8789
+ name: "Tools / Construction",
8790
+ icon: "🔨"
8791
+ },
8792
+ {
8793
+ id: "silence",
8794
+ name: "Silence",
8795
+ icon: "🤫"
8796
+ }
8797
+ ];
8644
8798
  var YAMNET_TO_MACRO = {
8645
8799
  mapping: {
8646
8800
  Speech: "speech",
@@ -8907,6 +9061,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8907
9061
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8908
9062
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8909
9063
  /**
9064
+ * Unified event-kind taxonomy — THE single source of truth for
9065
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
9066
+ * icon }`.
9067
+ *
9068
+ * This dictionary folds together what used to be scattered across four
9069
+ * copies:
9070
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
9071
+ * - `addon-post-analysis/.../services/event-kinds.ts`
9072
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
9073
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
9074
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
9075
+ * - the COCO / audio class maps (macro ↔ sub relationships)
9076
+ *
9077
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
9078
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
9079
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
9080
+ * color or an icon: they read this dictionary (server descriptors carry the
9081
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
9082
+ *
9083
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
9084
+ */
9085
+ var TAXONOMY_COLORS = {
9086
+ motion: "#f59e0b",
9087
+ audio: "#06b6d4",
9088
+ person: "#22c55e",
9089
+ vehicle: "#3b82f6",
9090
+ animal: "#f97316",
9091
+ package: "#a855f7",
9092
+ sensor: "#8b5cf6",
9093
+ control: "#10b981",
9094
+ genericDetection: "#64748b"
9095
+ };
9096
+ var DETECTION_SUB_COLORS = {
9097
+ car: "#f59e0b",
9098
+ truck: "#d97706",
9099
+ bus: "#b45309",
9100
+ motorcycle: "#eab308",
9101
+ bicycle: "#ca8a04",
9102
+ airplane: "#60a5fa",
9103
+ boat: "#2563eb",
9104
+ train: "#1d4ed8",
9105
+ bird: "#14b8a6",
9106
+ dog: "#84cc16",
9107
+ cat: "#f97316",
9108
+ horse: "#a16207",
9109
+ sheep: "#a3a3a3",
9110
+ cow: "#78716c",
9111
+ elephant: "#6b7280",
9112
+ bear: "#7c2d12",
9113
+ zebra: "#404040",
9114
+ giraffe: "#d4a373"
9115
+ };
9116
+ function titleCase(id) {
9117
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
9118
+ }
9119
+ var entries = /* @__PURE__ */ new Map();
9120
+ function macro(kind, category, color, iconId, label) {
9121
+ entries.set(kind, {
9122
+ kind,
9123
+ parentKind: null,
9124
+ level: "macro",
9125
+ category,
9126
+ color,
9127
+ iconId,
9128
+ labelKey: `eventKind.${kind}`,
9129
+ label
9130
+ });
9131
+ }
9132
+ function sub(kind, parentKind, category, color, iconId, label) {
9133
+ entries.set(kind, {
9134
+ kind,
9135
+ parentKind,
9136
+ level: "sub",
9137
+ category,
9138
+ color,
9139
+ iconId,
9140
+ labelKey: `eventKind.${kind}`,
9141
+ label
9142
+ });
9143
+ }
9144
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
9145
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
9146
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
9147
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
9148
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
9149
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
9150
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
9151
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
9152
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
9153
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
9154
+ if (entries.has(cocoClass)) continue;
9155
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
9156
+ }
9157
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
9158
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
9159
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
9160
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
9161
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
9162
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
9163
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
9164
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
9165
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
9166
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
9167
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
9168
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
9169
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
9170
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
9171
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
9172
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
9173
+ sub("button", "control", "control", "#10b981", "button", "Button");
9174
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
9175
+ for (const l of AUDIO_MACRO_LABELS) {
9176
+ const kind = `audio-${l.id}`;
9177
+ if (entries.has(kind)) continue;
9178
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
9179
+ }
9180
+ /** The complete taxonomy dictionary, keyed by kind. */
9181
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
9182
+ /**
8910
9183
  * Error types for the safe expression engine. Two distinct classes so callers
8911
9184
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8912
9185
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -19839,17 +20112,30 @@ var EventKindCategorySchema = _enum([
19839
20112
  "audio",
19840
20113
  "detection",
19841
20114
  "sensor",
20115
+ "control",
19842
20116
  "custom",
19843
20117
  "package"
19844
20118
  ]);
20119
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
20120
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
19845
20121
  var EventKindDescriptorSchema = object({
19846
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
20122
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
19847
20123
  kind: string(),
20124
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
20125
+ labelKey: string(),
20126
+ /** English fallback label (kept for clients that don't translate). */
19848
20127
  label: string(),
19849
20128
  /** Hex color for timeline/legend rendering. */
19850
20129
  color: string(),
20130
+ /** Dictionary id → lucide component on the UI side. */
20131
+ iconId: string(),
20132
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
19851
20133
  icon: EventKindIconSchema,
19852
20134
  category: EventKindCategorySchema,
20135
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
20136
+ parentKind: string().nullable(),
20137
+ /** Derived from `parentKind`, explicit for the client tree. */
20138
+ level: EventKindLevelSchema,
19853
20139
  /** Which cap + device contributes this kind. For built-ins the camera
19854
20140
  * itself; for sensor kinds the LINKED source device. */
19855
20141
  source: object({
@@ -19922,11 +20208,21 @@ var TrackAudioLabelSchema = object({
19922
20208
  firstAt: number(),
19923
20209
  lastAt: number()
19924
20210
  });
20211
+ /**
20212
+ * How a track was produced. `pipeline` (default / absent) = the spatial
20213
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
20214
+ * linked sensor/control state change (no positions; carries a snapshot). The
20215
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
20216
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
20217
+ */
20218
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19925
20219
  var TrackSchema = object({
19926
20220
  trackId: string(),
19927
20221
  deviceId: number(),
19928
20222
  className: string(),
19929
20223
  label: string().optional(),
20224
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
20225
+ source: TrackSourceSchema.optional(),
19930
20226
  firstSeen: number(),
19931
20227
  lastSeen: number(),
19932
20228
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -20303,6 +20599,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20303
20599
  eventId: string(),
20304
20600
  timestamp: number()
20305
20601
  });
20602
+ /**
20603
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
20604
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
20605
+ * caps into per-camera event-kind descriptors.
20606
+ *
20607
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
20608
+ * is NOT duplicated here — every entry is derived from the single
20609
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
20610
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
20611
+ * control cap means adding one line here (and a taxonomy entry); the anti-
20612
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
20613
+ * eventful cap is missing.
20614
+ */
20615
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
20616
+ var LEGACY_ICON = {
20617
+ motion: "motion",
20618
+ audio: "audio",
20619
+ person: "person",
20620
+ vehicle: "vehicle",
20621
+ animal: "animal",
20622
+ package: "package",
20623
+ door: "door",
20624
+ pir: "pir",
20625
+ smoke: "smoke",
20626
+ water: "water",
20627
+ button: "button",
20628
+ generic: "generic",
20629
+ gas: "smoke",
20630
+ vibration: "generic",
20631
+ tamper: "generic",
20632
+ presence: "person",
20633
+ lock: "generic",
20634
+ siren: "generic",
20635
+ switch: "generic",
20636
+ doorbell: "button"
20637
+ };
20638
+ function legacyIcon(iconId) {
20639
+ return LEGACY_ICON[iconId] ?? "generic";
20640
+ }
20641
+ /**
20642
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
20643
+ * The anti-drift guard cross-checks this against the eventful caps declared
20644
+ * in `packages/types/src/capabilities/*.cap.ts`.
20645
+ */
20646
+ var CAP_TO_KIND = {
20647
+ contact: "contact",
20648
+ motion: "motion-sensor",
20649
+ smoke: "smoke",
20650
+ flood: "flood",
20651
+ gas: "gas",
20652
+ "carbon-monoxide": "carbon-monoxide",
20653
+ vibration: "vibration",
20654
+ tamper: "tamper",
20655
+ presence: "presence",
20656
+ "enum-sensor": "enum-sensor",
20657
+ "event-emitter": "device-event",
20658
+ "lock-control": "lock",
20659
+ switch: "switch",
20660
+ button: "button",
20661
+ doorbell: "doorbell"
20662
+ };
20663
+ function buildDescriptor(capName, kind) {
20664
+ const t = EVENT_TAXONOMY[kind];
20665
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
20666
+ return {
20667
+ ...t,
20668
+ icon: legacyIcon(t.iconId)
20669
+ };
20670
+ }
20671
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
20306
20672
  var CameraPipelineConfigSchema = object({
20307
20673
  engine: PipelineEngineChoiceSchema.optional(),
20308
20674
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",