@camstack/addon-provider-unraid 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
@@ -7657,6 +7657,160 @@ var EncodeProfileSchema = object({
7657
7657
  */
7658
7658
  outputArgs: array(string()).optional()
7659
7659
  });
7660
+ var COCO_TO_MACRO = {
7661
+ mapping: {
7662
+ person: "person",
7663
+ bicycle: "vehicle",
7664
+ car: "vehicle",
7665
+ motorcycle: "vehicle",
7666
+ airplane: "vehicle",
7667
+ bus: "vehicle",
7668
+ train: "vehicle",
7669
+ truck: "vehicle",
7670
+ boat: "vehicle",
7671
+ bird: "animal",
7672
+ cat: "animal",
7673
+ dog: "animal",
7674
+ horse: "animal",
7675
+ sheep: "animal",
7676
+ cow: "animal",
7677
+ elephant: "animal",
7678
+ bear: "animal",
7679
+ zebra: "animal",
7680
+ giraffe: "animal",
7681
+ suitcase: "package",
7682
+ backpack: "package",
7683
+ handbag: "package"
7684
+ },
7685
+ preserveOriginal: false
7686
+ };
7687
+ var AUDIO_MACRO_LABELS = [
7688
+ {
7689
+ id: "speech",
7690
+ name: "Speech",
7691
+ icon: "🗣️"
7692
+ },
7693
+ {
7694
+ id: "scream",
7695
+ name: "Scream / Shout",
7696
+ icon: "😱"
7697
+ },
7698
+ {
7699
+ id: "crying",
7700
+ name: "Crying / Baby",
7701
+ icon: "😢"
7702
+ },
7703
+ {
7704
+ id: "laughter",
7705
+ name: "Laughter",
7706
+ icon: "😂"
7707
+ },
7708
+ {
7709
+ id: "music",
7710
+ name: "Music",
7711
+ icon: "🎵"
7712
+ },
7713
+ {
7714
+ id: "dog",
7715
+ name: "Dog",
7716
+ icon: "🐕"
7717
+ },
7718
+ {
7719
+ id: "cat",
7720
+ name: "Cat",
7721
+ icon: "🐈"
7722
+ },
7723
+ {
7724
+ id: "bird",
7725
+ name: "Bird",
7726
+ icon: "🐦"
7727
+ },
7728
+ {
7729
+ id: "animal",
7730
+ name: "Animal (other)",
7731
+ icon: "🐾"
7732
+ },
7733
+ {
7734
+ id: "alarm",
7735
+ name: "Alarm / Siren",
7736
+ icon: "🚨"
7737
+ },
7738
+ {
7739
+ id: "doorbell",
7740
+ name: "Doorbell / Knock",
7741
+ icon: "🔔"
7742
+ },
7743
+ {
7744
+ id: "glass_breaking",
7745
+ name: "Glass Breaking",
7746
+ icon: "💥"
7747
+ },
7748
+ {
7749
+ id: "gunshot",
7750
+ name: "Gunshot / Explosion",
7751
+ icon: "💣"
7752
+ },
7753
+ {
7754
+ id: "vehicle",
7755
+ name: "Vehicle",
7756
+ icon: "🚗"
7757
+ },
7758
+ {
7759
+ id: "siren",
7760
+ name: "Emergency Siren",
7761
+ icon: "🚑"
7762
+ },
7763
+ {
7764
+ id: "fire",
7765
+ name: "Fire / Smoke",
7766
+ icon: "🔥"
7767
+ },
7768
+ {
7769
+ id: "water",
7770
+ name: "Water",
7771
+ icon: "💧"
7772
+ },
7773
+ {
7774
+ id: "wind",
7775
+ name: "Wind / Weather",
7776
+ icon: "🌬️"
7777
+ },
7778
+ {
7779
+ id: "door",
7780
+ name: "Door",
7781
+ icon: "🚪"
7782
+ },
7783
+ {
7784
+ id: "footsteps",
7785
+ name: "Footsteps",
7786
+ icon: "👣"
7787
+ },
7788
+ {
7789
+ id: "crowd",
7790
+ name: "Crowd / Chatter",
7791
+ icon: "👥"
7792
+ },
7793
+ {
7794
+ id: "telephone",
7795
+ name: "Telephone",
7796
+ icon: "📞"
7797
+ },
7798
+ {
7799
+ id: "engine",
7800
+ name: "Engine / Motor",
7801
+ icon: "⚙️"
7802
+ },
7803
+ {
7804
+ id: "tools",
7805
+ name: "Tools / Construction",
7806
+ icon: "🔨"
7807
+ },
7808
+ {
7809
+ id: "silence",
7810
+ name: "Silence",
7811
+ icon: "🤫"
7812
+ }
7813
+ ];
7660
7814
  var YAMNET_TO_MACRO = {
7661
7815
  mapping: {
7662
7816
  Speech: "speech",
@@ -7923,6 +8077,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7923
8077
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7924
8078
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7925
8079
  /**
8080
+ * Unified event-kind taxonomy — THE single source of truth for
8081
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8082
+ * icon }`.
8083
+ *
8084
+ * This dictionary folds together what used to be scattered across four
8085
+ * copies:
8086
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8087
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8088
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8089
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8090
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8091
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8092
+ *
8093
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8094
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8095
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8096
+ * color or an icon: they read this dictionary (server descriptors carry the
8097
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8098
+ *
8099
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8100
+ */
8101
+ var TAXONOMY_COLORS = {
8102
+ motion: "#f59e0b",
8103
+ audio: "#06b6d4",
8104
+ person: "#22c55e",
8105
+ vehicle: "#3b82f6",
8106
+ animal: "#f97316",
8107
+ package: "#a855f7",
8108
+ sensor: "#8b5cf6",
8109
+ control: "#10b981",
8110
+ genericDetection: "#64748b"
8111
+ };
8112
+ var DETECTION_SUB_COLORS = {
8113
+ car: "#f59e0b",
8114
+ truck: "#d97706",
8115
+ bus: "#b45309",
8116
+ motorcycle: "#eab308",
8117
+ bicycle: "#ca8a04",
8118
+ airplane: "#60a5fa",
8119
+ boat: "#2563eb",
8120
+ train: "#1d4ed8",
8121
+ bird: "#14b8a6",
8122
+ dog: "#84cc16",
8123
+ cat: "#f97316",
8124
+ horse: "#a16207",
8125
+ sheep: "#a3a3a3",
8126
+ cow: "#78716c",
8127
+ elephant: "#6b7280",
8128
+ bear: "#7c2d12",
8129
+ zebra: "#404040",
8130
+ giraffe: "#d4a373"
8131
+ };
8132
+ function titleCase(id) {
8133
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8134
+ }
8135
+ var entries = /* @__PURE__ */ new Map();
8136
+ function macro(kind, category, color, iconId, label) {
8137
+ entries.set(kind, {
8138
+ kind,
8139
+ parentKind: null,
8140
+ level: "macro",
8141
+ category,
8142
+ color,
8143
+ iconId,
8144
+ labelKey: `eventKind.${kind}`,
8145
+ label
8146
+ });
8147
+ }
8148
+ function sub(kind, parentKind, category, color, iconId, label) {
8149
+ entries.set(kind, {
8150
+ kind,
8151
+ parentKind,
8152
+ level: "sub",
8153
+ category,
8154
+ color,
8155
+ iconId,
8156
+ labelKey: `eventKind.${kind}`,
8157
+ label
8158
+ });
8159
+ }
8160
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8161
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8162
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8163
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8164
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8165
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8166
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8167
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8168
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8169
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8170
+ if (entries.has(cocoClass)) continue;
8171
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8172
+ }
8173
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8174
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8175
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8176
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8177
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8178
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8179
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8180
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8181
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8182
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8183
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8184
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8185
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8186
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8187
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8188
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8189
+ sub("button", "control", "control", "#10b981", "button", "Button");
8190
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8191
+ for (const l of AUDIO_MACRO_LABELS) {
8192
+ const kind = `audio-${l.id}`;
8193
+ if (entries.has(kind)) continue;
8194
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8195
+ }
8196
+ /** The complete taxonomy dictionary, keyed by kind. */
8197
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8198
+ /**
7926
8199
  * Error types for the safe expression engine. Two distinct classes so callers
7927
8200
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7928
8201
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -18855,17 +19128,30 @@ var EventKindCategorySchema = _enum([
18855
19128
  "audio",
18856
19129
  "detection",
18857
19130
  "sensor",
19131
+ "control",
18858
19132
  "custom",
18859
19133
  "package"
18860
19134
  ]);
19135
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19136
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
18861
19137
  var EventKindDescriptorSchema = object({
18862
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19138
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
18863
19139
  kind: string(),
19140
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19141
+ labelKey: string(),
19142
+ /** English fallback label (kept for clients that don't translate). */
18864
19143
  label: string(),
18865
19144
  /** Hex color for timeline/legend rendering. */
18866
19145
  color: string(),
19146
+ /** Dictionary id → lucide component on the UI side. */
19147
+ iconId: string(),
19148
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
18867
19149
  icon: EventKindIconSchema,
18868
19150
  category: EventKindCategorySchema,
19151
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19152
+ parentKind: string().nullable(),
19153
+ /** Derived from `parentKind`, explicit for the client tree. */
19154
+ level: EventKindLevelSchema,
18869
19155
  /** Which cap + device contributes this kind. For built-ins the camera
18870
19156
  * itself; for sensor kinds the LINKED source device. */
18871
19157
  source: object({
@@ -18938,11 +19224,21 @@ var TrackAudioLabelSchema = object({
18938
19224
  firstAt: number(),
18939
19225
  lastAt: number()
18940
19226
  });
19227
+ /**
19228
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19229
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19230
+ * linked sensor/control state change (no positions; carries a snapshot). The
19231
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19232
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19233
+ */
19234
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
18941
19235
  var TrackSchema = object({
18942
19236
  trackId: string(),
18943
19237
  deviceId: number(),
18944
19238
  className: string(),
18945
19239
  label: string().optional(),
19240
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19241
+ source: TrackSourceSchema.optional(),
18946
19242
  firstSeen: number(),
18947
19243
  lastSeen: number(),
18948
19244
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19319,6 +19615,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19319
19615
  eventId: string(),
19320
19616
  timestamp: number()
19321
19617
  });
19618
+ /**
19619
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19620
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19621
+ * caps into per-camera event-kind descriptors.
19622
+ *
19623
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19624
+ * is NOT duplicated here — every entry is derived from the single
19625
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19626
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19627
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19628
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19629
+ * eventful cap is missing.
19630
+ */
19631
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19632
+ var LEGACY_ICON = {
19633
+ motion: "motion",
19634
+ audio: "audio",
19635
+ person: "person",
19636
+ vehicle: "vehicle",
19637
+ animal: "animal",
19638
+ package: "package",
19639
+ door: "door",
19640
+ pir: "pir",
19641
+ smoke: "smoke",
19642
+ water: "water",
19643
+ button: "button",
19644
+ generic: "generic",
19645
+ gas: "smoke",
19646
+ vibration: "generic",
19647
+ tamper: "generic",
19648
+ presence: "person",
19649
+ lock: "generic",
19650
+ siren: "generic",
19651
+ switch: "generic",
19652
+ doorbell: "button"
19653
+ };
19654
+ function legacyIcon(iconId) {
19655
+ return LEGACY_ICON[iconId] ?? "generic";
19656
+ }
19657
+ /**
19658
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19659
+ * The anti-drift guard cross-checks this against the eventful caps declared
19660
+ * in `packages/types/src/capabilities/*.cap.ts`.
19661
+ */
19662
+ var CAP_TO_KIND = {
19663
+ contact: "contact",
19664
+ motion: "motion-sensor",
19665
+ smoke: "smoke",
19666
+ flood: "flood",
19667
+ gas: "gas",
19668
+ "carbon-monoxide": "carbon-monoxide",
19669
+ vibration: "vibration",
19670
+ tamper: "tamper",
19671
+ presence: "presence",
19672
+ "enum-sensor": "enum-sensor",
19673
+ "event-emitter": "device-event",
19674
+ "lock-control": "lock",
19675
+ switch: "switch",
19676
+ button: "button",
19677
+ doorbell: "doorbell"
19678
+ };
19679
+ function buildDescriptor(capName, kind) {
19680
+ const t = EVENT_TAXONOMY[kind];
19681
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19682
+ return {
19683
+ ...t,
19684
+ icon: legacyIcon(t.iconId)
19685
+ };
19686
+ }
19687
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19322
19688
  var CameraPipelineConfigSchema = object({
19323
19689
  engine: PipelineEngineChoiceSchema.optional(),
19324
19690
  steps: array(PipelineStepInputSchema).readonly(),
package/dist/addon.mjs CHANGED
@@ -7656,6 +7656,160 @@ var EncodeProfileSchema = object({
7656
7656
  */
7657
7657
  outputArgs: array(string()).optional()
7658
7658
  });
7659
+ var COCO_TO_MACRO = {
7660
+ mapping: {
7661
+ person: "person",
7662
+ bicycle: "vehicle",
7663
+ car: "vehicle",
7664
+ motorcycle: "vehicle",
7665
+ airplane: "vehicle",
7666
+ bus: "vehicle",
7667
+ train: "vehicle",
7668
+ truck: "vehicle",
7669
+ boat: "vehicle",
7670
+ bird: "animal",
7671
+ cat: "animal",
7672
+ dog: "animal",
7673
+ horse: "animal",
7674
+ sheep: "animal",
7675
+ cow: "animal",
7676
+ elephant: "animal",
7677
+ bear: "animal",
7678
+ zebra: "animal",
7679
+ giraffe: "animal",
7680
+ suitcase: "package",
7681
+ backpack: "package",
7682
+ handbag: "package"
7683
+ },
7684
+ preserveOriginal: false
7685
+ };
7686
+ var AUDIO_MACRO_LABELS = [
7687
+ {
7688
+ id: "speech",
7689
+ name: "Speech",
7690
+ icon: "🗣️"
7691
+ },
7692
+ {
7693
+ id: "scream",
7694
+ name: "Scream / Shout",
7695
+ icon: "😱"
7696
+ },
7697
+ {
7698
+ id: "crying",
7699
+ name: "Crying / Baby",
7700
+ icon: "😢"
7701
+ },
7702
+ {
7703
+ id: "laughter",
7704
+ name: "Laughter",
7705
+ icon: "😂"
7706
+ },
7707
+ {
7708
+ id: "music",
7709
+ name: "Music",
7710
+ icon: "🎵"
7711
+ },
7712
+ {
7713
+ id: "dog",
7714
+ name: "Dog",
7715
+ icon: "🐕"
7716
+ },
7717
+ {
7718
+ id: "cat",
7719
+ name: "Cat",
7720
+ icon: "🐈"
7721
+ },
7722
+ {
7723
+ id: "bird",
7724
+ name: "Bird",
7725
+ icon: "🐦"
7726
+ },
7727
+ {
7728
+ id: "animal",
7729
+ name: "Animal (other)",
7730
+ icon: "🐾"
7731
+ },
7732
+ {
7733
+ id: "alarm",
7734
+ name: "Alarm / Siren",
7735
+ icon: "🚨"
7736
+ },
7737
+ {
7738
+ id: "doorbell",
7739
+ name: "Doorbell / Knock",
7740
+ icon: "🔔"
7741
+ },
7742
+ {
7743
+ id: "glass_breaking",
7744
+ name: "Glass Breaking",
7745
+ icon: "💥"
7746
+ },
7747
+ {
7748
+ id: "gunshot",
7749
+ name: "Gunshot / Explosion",
7750
+ icon: "💣"
7751
+ },
7752
+ {
7753
+ id: "vehicle",
7754
+ name: "Vehicle",
7755
+ icon: "🚗"
7756
+ },
7757
+ {
7758
+ id: "siren",
7759
+ name: "Emergency Siren",
7760
+ icon: "🚑"
7761
+ },
7762
+ {
7763
+ id: "fire",
7764
+ name: "Fire / Smoke",
7765
+ icon: "🔥"
7766
+ },
7767
+ {
7768
+ id: "water",
7769
+ name: "Water",
7770
+ icon: "💧"
7771
+ },
7772
+ {
7773
+ id: "wind",
7774
+ name: "Wind / Weather",
7775
+ icon: "🌬️"
7776
+ },
7777
+ {
7778
+ id: "door",
7779
+ name: "Door",
7780
+ icon: "🚪"
7781
+ },
7782
+ {
7783
+ id: "footsteps",
7784
+ name: "Footsteps",
7785
+ icon: "👣"
7786
+ },
7787
+ {
7788
+ id: "crowd",
7789
+ name: "Crowd / Chatter",
7790
+ icon: "👥"
7791
+ },
7792
+ {
7793
+ id: "telephone",
7794
+ name: "Telephone",
7795
+ icon: "📞"
7796
+ },
7797
+ {
7798
+ id: "engine",
7799
+ name: "Engine / Motor",
7800
+ icon: "⚙️"
7801
+ },
7802
+ {
7803
+ id: "tools",
7804
+ name: "Tools / Construction",
7805
+ icon: "🔨"
7806
+ },
7807
+ {
7808
+ id: "silence",
7809
+ name: "Silence",
7810
+ icon: "🤫"
7811
+ }
7812
+ ];
7659
7813
  var YAMNET_TO_MACRO = {
7660
7814
  mapping: {
7661
7815
  Speech: "speech",
@@ -7922,6 +8076,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7922
8076
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7923
8077
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7924
8078
  /**
8079
+ * Unified event-kind taxonomy — THE single source of truth for
8080
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8081
+ * icon }`.
8082
+ *
8083
+ * This dictionary folds together what used to be scattered across four
8084
+ * copies:
8085
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8086
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8087
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8088
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8089
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8090
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8091
+ *
8092
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8093
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8094
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8095
+ * color or an icon: they read this dictionary (server descriptors carry the
8096
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8097
+ *
8098
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8099
+ */
8100
+ var TAXONOMY_COLORS = {
8101
+ motion: "#f59e0b",
8102
+ audio: "#06b6d4",
8103
+ person: "#22c55e",
8104
+ vehicle: "#3b82f6",
8105
+ animal: "#f97316",
8106
+ package: "#a855f7",
8107
+ sensor: "#8b5cf6",
8108
+ control: "#10b981",
8109
+ genericDetection: "#64748b"
8110
+ };
8111
+ var DETECTION_SUB_COLORS = {
8112
+ car: "#f59e0b",
8113
+ truck: "#d97706",
8114
+ bus: "#b45309",
8115
+ motorcycle: "#eab308",
8116
+ bicycle: "#ca8a04",
8117
+ airplane: "#60a5fa",
8118
+ boat: "#2563eb",
8119
+ train: "#1d4ed8",
8120
+ bird: "#14b8a6",
8121
+ dog: "#84cc16",
8122
+ cat: "#f97316",
8123
+ horse: "#a16207",
8124
+ sheep: "#a3a3a3",
8125
+ cow: "#78716c",
8126
+ elephant: "#6b7280",
8127
+ bear: "#7c2d12",
8128
+ zebra: "#404040",
8129
+ giraffe: "#d4a373"
8130
+ };
8131
+ function titleCase(id) {
8132
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8133
+ }
8134
+ var entries = /* @__PURE__ */ new Map();
8135
+ function macro(kind, category, color, iconId, label) {
8136
+ entries.set(kind, {
8137
+ kind,
8138
+ parentKind: null,
8139
+ level: "macro",
8140
+ category,
8141
+ color,
8142
+ iconId,
8143
+ labelKey: `eventKind.${kind}`,
8144
+ label
8145
+ });
8146
+ }
8147
+ function sub(kind, parentKind, category, color, iconId, label) {
8148
+ entries.set(kind, {
8149
+ kind,
8150
+ parentKind,
8151
+ level: "sub",
8152
+ category,
8153
+ color,
8154
+ iconId,
8155
+ labelKey: `eventKind.${kind}`,
8156
+ label
8157
+ });
8158
+ }
8159
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8160
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8161
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8162
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8163
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8164
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8165
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8166
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8167
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8168
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8169
+ if (entries.has(cocoClass)) continue;
8170
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8171
+ }
8172
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8173
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8174
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8175
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8176
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8177
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8178
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8179
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8180
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8181
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8182
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8183
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8184
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8185
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8186
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8187
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8188
+ sub("button", "control", "control", "#10b981", "button", "Button");
8189
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8190
+ for (const l of AUDIO_MACRO_LABELS) {
8191
+ const kind = `audio-${l.id}`;
8192
+ if (entries.has(kind)) continue;
8193
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8194
+ }
8195
+ /** The complete taxonomy dictionary, keyed by kind. */
8196
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8197
+ /**
7925
8198
  * Error types for the safe expression engine. Two distinct classes so callers
7926
8199
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7927
8200
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -18854,17 +19127,30 @@ var EventKindCategorySchema = _enum([
18854
19127
  "audio",
18855
19128
  "detection",
18856
19129
  "sensor",
19130
+ "control",
18857
19131
  "custom",
18858
19132
  "package"
18859
19133
  ]);
19134
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19135
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
18860
19136
  var EventKindDescriptorSchema = object({
18861
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19137
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
18862
19138
  kind: string(),
19139
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19140
+ labelKey: string(),
19141
+ /** English fallback label (kept for clients that don't translate). */
18863
19142
  label: string(),
18864
19143
  /** Hex color for timeline/legend rendering. */
18865
19144
  color: string(),
19145
+ /** Dictionary id → lucide component on the UI side. */
19146
+ iconId: string(),
19147
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
18866
19148
  icon: EventKindIconSchema,
18867
19149
  category: EventKindCategorySchema,
19150
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19151
+ parentKind: string().nullable(),
19152
+ /** Derived from `parentKind`, explicit for the client tree. */
19153
+ level: EventKindLevelSchema,
18868
19154
  /** Which cap + device contributes this kind. For built-ins the camera
18869
19155
  * itself; for sensor kinds the LINKED source device. */
18870
19156
  source: object({
@@ -18937,11 +19223,21 @@ var TrackAudioLabelSchema = object({
18937
19223
  firstAt: number(),
18938
19224
  lastAt: number()
18939
19225
  });
19226
+ /**
19227
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19228
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19229
+ * linked sensor/control state change (no positions; carries a snapshot). The
19230
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19231
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19232
+ */
19233
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
18940
19234
  var TrackSchema = object({
18941
19235
  trackId: string(),
18942
19236
  deviceId: number(),
18943
19237
  className: string(),
18944
19238
  label: string().optional(),
19239
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19240
+ source: TrackSourceSchema.optional(),
18945
19241
  firstSeen: number(),
18946
19242
  lastSeen: number(),
18947
19243
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19318,6 +19614,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19318
19614
  eventId: string(),
19319
19615
  timestamp: number()
19320
19616
  });
19617
+ /**
19618
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19619
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19620
+ * caps into per-camera event-kind descriptors.
19621
+ *
19622
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19623
+ * is NOT duplicated here — every entry is derived from the single
19624
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19625
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19626
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19627
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19628
+ * eventful cap is missing.
19629
+ */
19630
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19631
+ var LEGACY_ICON = {
19632
+ motion: "motion",
19633
+ audio: "audio",
19634
+ person: "person",
19635
+ vehicle: "vehicle",
19636
+ animal: "animal",
19637
+ package: "package",
19638
+ door: "door",
19639
+ pir: "pir",
19640
+ smoke: "smoke",
19641
+ water: "water",
19642
+ button: "button",
19643
+ generic: "generic",
19644
+ gas: "smoke",
19645
+ vibration: "generic",
19646
+ tamper: "generic",
19647
+ presence: "person",
19648
+ lock: "generic",
19649
+ siren: "generic",
19650
+ switch: "generic",
19651
+ doorbell: "button"
19652
+ };
19653
+ function legacyIcon(iconId) {
19654
+ return LEGACY_ICON[iconId] ?? "generic";
19655
+ }
19656
+ /**
19657
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19658
+ * The anti-drift guard cross-checks this against the eventful caps declared
19659
+ * in `packages/types/src/capabilities/*.cap.ts`.
19660
+ */
19661
+ var CAP_TO_KIND = {
19662
+ contact: "contact",
19663
+ motion: "motion-sensor",
19664
+ smoke: "smoke",
19665
+ flood: "flood",
19666
+ gas: "gas",
19667
+ "carbon-monoxide": "carbon-monoxide",
19668
+ vibration: "vibration",
19669
+ tamper: "tamper",
19670
+ presence: "presence",
19671
+ "enum-sensor": "enum-sensor",
19672
+ "event-emitter": "device-event",
19673
+ "lock-control": "lock",
19674
+ switch: "switch",
19675
+ button: "button",
19676
+ doorbell: "doorbell"
19677
+ };
19678
+ function buildDescriptor(capName, kind) {
19679
+ const t = EVENT_TAXONOMY[kind];
19680
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19681
+ return {
19682
+ ...t,
19683
+ icon: legacyIcon(t.iconId)
19684
+ };
19685
+ }
19686
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19321
19687
  var CameraPipelineConfigSchema = object({
19322
19688
  engine: PipelineEngineChoiceSchema.optional(),
19323
19689
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-unraid",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Unraid device-provider addon for CamStack — one Container per Unraid instance fanning out array, disk, docker and notification entities",
5
5
  "keywords": [
6
6
  "camstack",