@camstack/addon-provider-hikvision 1.1.28 → 1.1.29

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
@@ -7813,6 +7813,160 @@ var EncodeProfileSchema = object({
7813
7813
  */
7814
7814
  outputArgs: array(string()).optional()
7815
7815
  });
7816
+ var COCO_TO_MACRO = {
7817
+ mapping: {
7818
+ person: "person",
7819
+ bicycle: "vehicle",
7820
+ car: "vehicle",
7821
+ motorcycle: "vehicle",
7822
+ airplane: "vehicle",
7823
+ bus: "vehicle",
7824
+ train: "vehicle",
7825
+ truck: "vehicle",
7826
+ boat: "vehicle",
7827
+ bird: "animal",
7828
+ cat: "animal",
7829
+ dog: "animal",
7830
+ horse: "animal",
7831
+ sheep: "animal",
7832
+ cow: "animal",
7833
+ elephant: "animal",
7834
+ bear: "animal",
7835
+ zebra: "animal",
7836
+ giraffe: "animal",
7837
+ suitcase: "package",
7838
+ backpack: "package",
7839
+ handbag: "package"
7840
+ },
7841
+ preserveOriginal: false
7842
+ };
7843
+ var AUDIO_MACRO_LABELS = [
7844
+ {
7845
+ id: "speech",
7846
+ name: "Speech",
7847
+ icon: "🗣️"
7848
+ },
7849
+ {
7850
+ id: "scream",
7851
+ name: "Scream / Shout",
7852
+ icon: "😱"
7853
+ },
7854
+ {
7855
+ id: "crying",
7856
+ name: "Crying / Baby",
7857
+ icon: "😢"
7858
+ },
7859
+ {
7860
+ id: "laughter",
7861
+ name: "Laughter",
7862
+ icon: "😂"
7863
+ },
7864
+ {
7865
+ id: "music",
7866
+ name: "Music",
7867
+ icon: "🎵"
7868
+ },
7869
+ {
7870
+ id: "dog",
7871
+ name: "Dog",
7872
+ icon: "🐕"
7873
+ },
7874
+ {
7875
+ id: "cat",
7876
+ name: "Cat",
7877
+ icon: "🐈"
7878
+ },
7879
+ {
7880
+ id: "bird",
7881
+ name: "Bird",
7882
+ icon: "🐦"
7883
+ },
7884
+ {
7885
+ id: "animal",
7886
+ name: "Animal (other)",
7887
+ icon: "🐾"
7888
+ },
7889
+ {
7890
+ id: "alarm",
7891
+ name: "Alarm / Siren",
7892
+ icon: "🚨"
7893
+ },
7894
+ {
7895
+ id: "doorbell",
7896
+ name: "Doorbell / Knock",
7897
+ icon: "🔔"
7898
+ },
7899
+ {
7900
+ id: "glass_breaking",
7901
+ name: "Glass Breaking",
7902
+ icon: "💥"
7903
+ },
7904
+ {
7905
+ id: "gunshot",
7906
+ name: "Gunshot / Explosion",
7907
+ icon: "💣"
7908
+ },
7909
+ {
7910
+ id: "vehicle",
7911
+ name: "Vehicle",
7912
+ icon: "🚗"
7913
+ },
7914
+ {
7915
+ id: "siren",
7916
+ name: "Emergency Siren",
7917
+ icon: "🚑"
7918
+ },
7919
+ {
7920
+ id: "fire",
7921
+ name: "Fire / Smoke",
7922
+ icon: "🔥"
7923
+ },
7924
+ {
7925
+ id: "water",
7926
+ name: "Water",
7927
+ icon: "💧"
7928
+ },
7929
+ {
7930
+ id: "wind",
7931
+ name: "Wind / Weather",
7932
+ icon: "🌬️"
7933
+ },
7934
+ {
7935
+ id: "door",
7936
+ name: "Door",
7937
+ icon: "🚪"
7938
+ },
7939
+ {
7940
+ id: "footsteps",
7941
+ name: "Footsteps",
7942
+ icon: "👣"
7943
+ },
7944
+ {
7945
+ id: "crowd",
7946
+ name: "Crowd / Chatter",
7947
+ icon: "👥"
7948
+ },
7949
+ {
7950
+ id: "telephone",
7951
+ name: "Telephone",
7952
+ icon: "📞"
7953
+ },
7954
+ {
7955
+ id: "engine",
7956
+ name: "Engine / Motor",
7957
+ icon: "⚙️"
7958
+ },
7959
+ {
7960
+ id: "tools",
7961
+ name: "Tools / Construction",
7962
+ icon: "🔨"
7963
+ },
7964
+ {
7965
+ id: "silence",
7966
+ name: "Silence",
7967
+ icon: "🤫"
7968
+ }
7969
+ ];
7816
7970
  var YAMNET_TO_MACRO = {
7817
7971
  mapping: {
7818
7972
  Speech: "speech",
@@ -8079,6 +8233,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8079
8233
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8080
8234
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8081
8235
  /**
8236
+ * Unified event-kind taxonomy — THE single source of truth for
8237
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8238
+ * icon }`.
8239
+ *
8240
+ * This dictionary folds together what used to be scattered across four
8241
+ * copies:
8242
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8243
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8244
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8245
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8246
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8247
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8248
+ *
8249
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8250
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8251
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8252
+ * color or an icon: they read this dictionary (server descriptors carry the
8253
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8254
+ *
8255
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8256
+ */
8257
+ var TAXONOMY_COLORS = {
8258
+ motion: "#f59e0b",
8259
+ audio: "#06b6d4",
8260
+ person: "#22c55e",
8261
+ vehicle: "#3b82f6",
8262
+ animal: "#f97316",
8263
+ package: "#a855f7",
8264
+ sensor: "#8b5cf6",
8265
+ control: "#10b981",
8266
+ genericDetection: "#64748b"
8267
+ };
8268
+ var DETECTION_SUB_COLORS = {
8269
+ car: "#f59e0b",
8270
+ truck: "#d97706",
8271
+ bus: "#b45309",
8272
+ motorcycle: "#eab308",
8273
+ bicycle: "#ca8a04",
8274
+ airplane: "#60a5fa",
8275
+ boat: "#2563eb",
8276
+ train: "#1d4ed8",
8277
+ bird: "#14b8a6",
8278
+ dog: "#84cc16",
8279
+ cat: "#f97316",
8280
+ horse: "#a16207",
8281
+ sheep: "#a3a3a3",
8282
+ cow: "#78716c",
8283
+ elephant: "#6b7280",
8284
+ bear: "#7c2d12",
8285
+ zebra: "#404040",
8286
+ giraffe: "#d4a373"
8287
+ };
8288
+ function titleCase(id) {
8289
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8290
+ }
8291
+ var entries = /* @__PURE__ */ new Map();
8292
+ function macro(kind, category, color, iconId, label) {
8293
+ entries.set(kind, {
8294
+ kind,
8295
+ parentKind: null,
8296
+ level: "macro",
8297
+ category,
8298
+ color,
8299
+ iconId,
8300
+ labelKey: `eventKind.${kind}`,
8301
+ label
8302
+ });
8303
+ }
8304
+ function sub(kind, parentKind, category, color, iconId, label) {
8305
+ entries.set(kind, {
8306
+ kind,
8307
+ parentKind,
8308
+ level: "sub",
8309
+ category,
8310
+ color,
8311
+ iconId,
8312
+ labelKey: `eventKind.${kind}`,
8313
+ label
8314
+ });
8315
+ }
8316
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8317
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8318
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8319
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8320
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8321
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8322
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8323
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8324
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8325
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8326
+ if (entries.has(cocoClass)) continue;
8327
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8328
+ }
8329
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8330
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8331
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8332
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8333
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8334
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8335
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8336
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8337
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8338
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8339
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8340
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8341
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8342
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8343
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8344
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8345
+ sub("button", "control", "control", "#10b981", "button", "Button");
8346
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8347
+ for (const l of AUDIO_MACRO_LABELS) {
8348
+ const kind = `audio-${l.id}`;
8349
+ if (entries.has(kind)) continue;
8350
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8351
+ }
8352
+ /** The complete taxonomy dictionary, keyed by kind. */
8353
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8354
+ /**
8082
8355
  * Error types for the safe expression engine. Two distinct classes so callers
8083
8356
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8084
8357
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -19128,17 +19401,30 @@ var EventKindCategorySchema = _enum([
19128
19401
  "audio",
19129
19402
  "detection",
19130
19403
  "sensor",
19404
+ "control",
19131
19405
  "custom",
19132
19406
  "package"
19133
19407
  ]);
19408
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19409
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
19134
19410
  var EventKindDescriptorSchema = object({
19135
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19411
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
19136
19412
  kind: string(),
19413
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19414
+ labelKey: string(),
19415
+ /** English fallback label (kept for clients that don't translate). */
19137
19416
  label: string(),
19138
19417
  /** Hex color for timeline/legend rendering. */
19139
19418
  color: string(),
19419
+ /** Dictionary id → lucide component on the UI side. */
19420
+ iconId: string(),
19421
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
19140
19422
  icon: EventKindIconSchema,
19141
19423
  category: EventKindCategorySchema,
19424
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19425
+ parentKind: string().nullable(),
19426
+ /** Derived from `parentKind`, explicit for the client tree. */
19427
+ level: EventKindLevelSchema,
19142
19428
  /** Which cap + device contributes this kind. For built-ins the camera
19143
19429
  * itself; for sensor kinds the LINKED source device. */
19144
19430
  source: object({
@@ -19211,11 +19497,21 @@ var TrackAudioLabelSchema = object({
19211
19497
  firstAt: number(),
19212
19498
  lastAt: number()
19213
19499
  });
19500
+ /**
19501
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19502
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19503
+ * linked sensor/control state change (no positions; carries a snapshot). The
19504
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19505
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19506
+ */
19507
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19214
19508
  var TrackSchema = object({
19215
19509
  trackId: string(),
19216
19510
  deviceId: number(),
19217
19511
  className: string(),
19218
19512
  label: string().optional(),
19513
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19514
+ source: TrackSourceSchema.optional(),
19219
19515
  firstSeen: number(),
19220
19516
  lastSeen: number(),
19221
19517
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19592,6 +19888,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19592
19888
  eventId: string(),
19593
19889
  timestamp: number()
19594
19890
  });
19891
+ /**
19892
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19893
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19894
+ * caps into per-camera event-kind descriptors.
19895
+ *
19896
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19897
+ * is NOT duplicated here — every entry is derived from the single
19898
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19899
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19900
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19901
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19902
+ * eventful cap is missing.
19903
+ */
19904
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19905
+ var LEGACY_ICON = {
19906
+ motion: "motion",
19907
+ audio: "audio",
19908
+ person: "person",
19909
+ vehicle: "vehicle",
19910
+ animal: "animal",
19911
+ package: "package",
19912
+ door: "door",
19913
+ pir: "pir",
19914
+ smoke: "smoke",
19915
+ water: "water",
19916
+ button: "button",
19917
+ generic: "generic",
19918
+ gas: "smoke",
19919
+ vibration: "generic",
19920
+ tamper: "generic",
19921
+ presence: "person",
19922
+ lock: "generic",
19923
+ siren: "generic",
19924
+ switch: "generic",
19925
+ doorbell: "button"
19926
+ };
19927
+ function legacyIcon(iconId) {
19928
+ return LEGACY_ICON[iconId] ?? "generic";
19929
+ }
19930
+ /**
19931
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19932
+ * The anti-drift guard cross-checks this against the eventful caps declared
19933
+ * in `packages/types/src/capabilities/*.cap.ts`.
19934
+ */
19935
+ var CAP_TO_KIND = {
19936
+ contact: "contact",
19937
+ motion: "motion-sensor",
19938
+ smoke: "smoke",
19939
+ flood: "flood",
19940
+ gas: "gas",
19941
+ "carbon-monoxide": "carbon-monoxide",
19942
+ vibration: "vibration",
19943
+ tamper: "tamper",
19944
+ presence: "presence",
19945
+ "enum-sensor": "enum-sensor",
19946
+ "event-emitter": "device-event",
19947
+ "lock-control": "lock",
19948
+ switch: "switch",
19949
+ button: "button",
19950
+ doorbell: "doorbell"
19951
+ };
19952
+ function buildDescriptor(capName, kind) {
19953
+ const t = EVENT_TAXONOMY[kind];
19954
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19955
+ return {
19956
+ ...t,
19957
+ icon: legacyIcon(t.iconId)
19958
+ };
19959
+ }
19960
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19595
19961
  var CameraPipelineConfigSchema = object({
19596
19962
  engine: PipelineEngineChoiceSchema.optional(),
19597
19963
  steps: array(PipelineStepInputSchema).readonly(),
package/dist/addon.mjs CHANGED
@@ -7814,6 +7814,160 @@ var EncodeProfileSchema = object({
7814
7814
  */
7815
7815
  outputArgs: array(string()).optional()
7816
7816
  });
7817
+ var COCO_TO_MACRO = {
7818
+ mapping: {
7819
+ person: "person",
7820
+ bicycle: "vehicle",
7821
+ car: "vehicle",
7822
+ motorcycle: "vehicle",
7823
+ airplane: "vehicle",
7824
+ bus: "vehicle",
7825
+ train: "vehicle",
7826
+ truck: "vehicle",
7827
+ boat: "vehicle",
7828
+ bird: "animal",
7829
+ cat: "animal",
7830
+ dog: "animal",
7831
+ horse: "animal",
7832
+ sheep: "animal",
7833
+ cow: "animal",
7834
+ elephant: "animal",
7835
+ bear: "animal",
7836
+ zebra: "animal",
7837
+ giraffe: "animal",
7838
+ suitcase: "package",
7839
+ backpack: "package",
7840
+ handbag: "package"
7841
+ },
7842
+ preserveOriginal: false
7843
+ };
7844
+ var AUDIO_MACRO_LABELS = [
7845
+ {
7846
+ id: "speech",
7847
+ name: "Speech",
7848
+ icon: "🗣️"
7849
+ },
7850
+ {
7851
+ id: "scream",
7852
+ name: "Scream / Shout",
7853
+ icon: "😱"
7854
+ },
7855
+ {
7856
+ id: "crying",
7857
+ name: "Crying / Baby",
7858
+ icon: "😢"
7859
+ },
7860
+ {
7861
+ id: "laughter",
7862
+ name: "Laughter",
7863
+ icon: "😂"
7864
+ },
7865
+ {
7866
+ id: "music",
7867
+ name: "Music",
7868
+ icon: "🎵"
7869
+ },
7870
+ {
7871
+ id: "dog",
7872
+ name: "Dog",
7873
+ icon: "🐕"
7874
+ },
7875
+ {
7876
+ id: "cat",
7877
+ name: "Cat",
7878
+ icon: "🐈"
7879
+ },
7880
+ {
7881
+ id: "bird",
7882
+ name: "Bird",
7883
+ icon: "🐦"
7884
+ },
7885
+ {
7886
+ id: "animal",
7887
+ name: "Animal (other)",
7888
+ icon: "🐾"
7889
+ },
7890
+ {
7891
+ id: "alarm",
7892
+ name: "Alarm / Siren",
7893
+ icon: "🚨"
7894
+ },
7895
+ {
7896
+ id: "doorbell",
7897
+ name: "Doorbell / Knock",
7898
+ icon: "🔔"
7899
+ },
7900
+ {
7901
+ id: "glass_breaking",
7902
+ name: "Glass Breaking",
7903
+ icon: "💥"
7904
+ },
7905
+ {
7906
+ id: "gunshot",
7907
+ name: "Gunshot / Explosion",
7908
+ icon: "💣"
7909
+ },
7910
+ {
7911
+ id: "vehicle",
7912
+ name: "Vehicle",
7913
+ icon: "🚗"
7914
+ },
7915
+ {
7916
+ id: "siren",
7917
+ name: "Emergency Siren",
7918
+ icon: "🚑"
7919
+ },
7920
+ {
7921
+ id: "fire",
7922
+ name: "Fire / Smoke",
7923
+ icon: "🔥"
7924
+ },
7925
+ {
7926
+ id: "water",
7927
+ name: "Water",
7928
+ icon: "💧"
7929
+ },
7930
+ {
7931
+ id: "wind",
7932
+ name: "Wind / Weather",
7933
+ icon: "🌬️"
7934
+ },
7935
+ {
7936
+ id: "door",
7937
+ name: "Door",
7938
+ icon: "🚪"
7939
+ },
7940
+ {
7941
+ id: "footsteps",
7942
+ name: "Footsteps",
7943
+ icon: "👣"
7944
+ },
7945
+ {
7946
+ id: "crowd",
7947
+ name: "Crowd / Chatter",
7948
+ icon: "👥"
7949
+ },
7950
+ {
7951
+ id: "telephone",
7952
+ name: "Telephone",
7953
+ icon: "📞"
7954
+ },
7955
+ {
7956
+ id: "engine",
7957
+ name: "Engine / Motor",
7958
+ icon: "⚙️"
7959
+ },
7960
+ {
7961
+ id: "tools",
7962
+ name: "Tools / Construction",
7963
+ icon: "🔨"
7964
+ },
7965
+ {
7966
+ id: "silence",
7967
+ name: "Silence",
7968
+ icon: "🤫"
7969
+ }
7970
+ ];
7817
7971
  var YAMNET_TO_MACRO = {
7818
7972
  mapping: {
7819
7973
  Speech: "speech",
@@ -8080,6 +8234,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8080
8234
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8081
8235
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8082
8236
  /**
8237
+ * Unified event-kind taxonomy — THE single source of truth for
8238
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8239
+ * icon }`.
8240
+ *
8241
+ * This dictionary folds together what used to be scattered across four
8242
+ * copies:
8243
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8244
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8245
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8246
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8247
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8248
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8249
+ *
8250
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8251
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8252
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8253
+ * color or an icon: they read this dictionary (server descriptors carry the
8254
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8255
+ *
8256
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8257
+ */
8258
+ var TAXONOMY_COLORS = {
8259
+ motion: "#f59e0b",
8260
+ audio: "#06b6d4",
8261
+ person: "#22c55e",
8262
+ vehicle: "#3b82f6",
8263
+ animal: "#f97316",
8264
+ package: "#a855f7",
8265
+ sensor: "#8b5cf6",
8266
+ control: "#10b981",
8267
+ genericDetection: "#64748b"
8268
+ };
8269
+ var DETECTION_SUB_COLORS = {
8270
+ car: "#f59e0b",
8271
+ truck: "#d97706",
8272
+ bus: "#b45309",
8273
+ motorcycle: "#eab308",
8274
+ bicycle: "#ca8a04",
8275
+ airplane: "#60a5fa",
8276
+ boat: "#2563eb",
8277
+ train: "#1d4ed8",
8278
+ bird: "#14b8a6",
8279
+ dog: "#84cc16",
8280
+ cat: "#f97316",
8281
+ horse: "#a16207",
8282
+ sheep: "#a3a3a3",
8283
+ cow: "#78716c",
8284
+ elephant: "#6b7280",
8285
+ bear: "#7c2d12",
8286
+ zebra: "#404040",
8287
+ giraffe: "#d4a373"
8288
+ };
8289
+ function titleCase(id) {
8290
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8291
+ }
8292
+ var entries = /* @__PURE__ */ new Map();
8293
+ function macro(kind, category, color, iconId, label) {
8294
+ entries.set(kind, {
8295
+ kind,
8296
+ parentKind: null,
8297
+ level: "macro",
8298
+ category,
8299
+ color,
8300
+ iconId,
8301
+ labelKey: `eventKind.${kind}`,
8302
+ label
8303
+ });
8304
+ }
8305
+ function sub(kind, parentKind, category, color, iconId, label) {
8306
+ entries.set(kind, {
8307
+ kind,
8308
+ parentKind,
8309
+ level: "sub",
8310
+ category,
8311
+ color,
8312
+ iconId,
8313
+ labelKey: `eventKind.${kind}`,
8314
+ label
8315
+ });
8316
+ }
8317
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8318
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8319
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8320
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8321
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8322
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8323
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8324
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8325
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8326
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8327
+ if (entries.has(cocoClass)) continue;
8328
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8329
+ }
8330
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8331
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8332
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8333
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8334
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8335
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8336
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8337
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8338
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8339
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8340
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8341
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8342
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8343
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8344
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8345
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8346
+ sub("button", "control", "control", "#10b981", "button", "Button");
8347
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8348
+ for (const l of AUDIO_MACRO_LABELS) {
8349
+ const kind = `audio-${l.id}`;
8350
+ if (entries.has(kind)) continue;
8351
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8352
+ }
8353
+ /** The complete taxonomy dictionary, keyed by kind. */
8354
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8355
+ /**
8083
8356
  * Error types for the safe expression engine. Two distinct classes so callers
8084
8357
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8085
8358
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -19129,17 +19402,30 @@ var EventKindCategorySchema = _enum([
19129
19402
  "audio",
19130
19403
  "detection",
19131
19404
  "sensor",
19405
+ "control",
19132
19406
  "custom",
19133
19407
  "package"
19134
19408
  ]);
19409
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19410
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
19135
19411
  var EventKindDescriptorSchema = object({
19136
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19412
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
19137
19413
  kind: string(),
19414
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19415
+ labelKey: string(),
19416
+ /** English fallback label (kept for clients that don't translate). */
19138
19417
  label: string(),
19139
19418
  /** Hex color for timeline/legend rendering. */
19140
19419
  color: string(),
19420
+ /** Dictionary id → lucide component on the UI side. */
19421
+ iconId: string(),
19422
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
19141
19423
  icon: EventKindIconSchema,
19142
19424
  category: EventKindCategorySchema,
19425
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19426
+ parentKind: string().nullable(),
19427
+ /** Derived from `parentKind`, explicit for the client tree. */
19428
+ level: EventKindLevelSchema,
19143
19429
  /** Which cap + device contributes this kind. For built-ins the camera
19144
19430
  * itself; for sensor kinds the LINKED source device. */
19145
19431
  source: object({
@@ -19212,11 +19498,21 @@ var TrackAudioLabelSchema = object({
19212
19498
  firstAt: number(),
19213
19499
  lastAt: number()
19214
19500
  });
19501
+ /**
19502
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19503
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19504
+ * linked sensor/control state change (no positions; carries a snapshot). The
19505
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19506
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19507
+ */
19508
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19215
19509
  var TrackSchema = object({
19216
19510
  trackId: string(),
19217
19511
  deviceId: number(),
19218
19512
  className: string(),
19219
19513
  label: string().optional(),
19514
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19515
+ source: TrackSourceSchema.optional(),
19220
19516
  firstSeen: number(),
19221
19517
  lastSeen: number(),
19222
19518
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19593,6 +19889,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19593
19889
  eventId: string(),
19594
19890
  timestamp: number()
19595
19891
  });
19892
+ /**
19893
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19894
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19895
+ * caps into per-camera event-kind descriptors.
19896
+ *
19897
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19898
+ * is NOT duplicated here — every entry is derived from the single
19899
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19900
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19901
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19902
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19903
+ * eventful cap is missing.
19904
+ */
19905
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19906
+ var LEGACY_ICON = {
19907
+ motion: "motion",
19908
+ audio: "audio",
19909
+ person: "person",
19910
+ vehicle: "vehicle",
19911
+ animal: "animal",
19912
+ package: "package",
19913
+ door: "door",
19914
+ pir: "pir",
19915
+ smoke: "smoke",
19916
+ water: "water",
19917
+ button: "button",
19918
+ generic: "generic",
19919
+ gas: "smoke",
19920
+ vibration: "generic",
19921
+ tamper: "generic",
19922
+ presence: "person",
19923
+ lock: "generic",
19924
+ siren: "generic",
19925
+ switch: "generic",
19926
+ doorbell: "button"
19927
+ };
19928
+ function legacyIcon(iconId) {
19929
+ return LEGACY_ICON[iconId] ?? "generic";
19930
+ }
19931
+ /**
19932
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19933
+ * The anti-drift guard cross-checks this against the eventful caps declared
19934
+ * in `packages/types/src/capabilities/*.cap.ts`.
19935
+ */
19936
+ var CAP_TO_KIND = {
19937
+ contact: "contact",
19938
+ motion: "motion-sensor",
19939
+ smoke: "smoke",
19940
+ flood: "flood",
19941
+ gas: "gas",
19942
+ "carbon-monoxide": "carbon-monoxide",
19943
+ vibration: "vibration",
19944
+ tamper: "tamper",
19945
+ presence: "presence",
19946
+ "enum-sensor": "enum-sensor",
19947
+ "event-emitter": "device-event",
19948
+ "lock-control": "lock",
19949
+ switch: "switch",
19950
+ button: "button",
19951
+ doorbell: "doorbell"
19952
+ };
19953
+ function buildDescriptor(capName, kind) {
19954
+ const t = EVENT_TAXONOMY[kind];
19955
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19956
+ return {
19957
+ ...t,
19958
+ icon: legacyIcon(t.iconId)
19959
+ };
19960
+ }
19961
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19596
19962
  var CameraPipelineConfigSchema = object({
19597
19963
  engine: PipelineEngineChoiceSchema.optional(),
19598
19964
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-hikvision",
3
- "version": "1.1.28",
3
+ "version": "1.1.29",
4
4
  "description": "Hikvision camera device provider addon for CamStack — ISAPI over HTTP(S) with digest auth (snapshot, alarm stream, RTSP discovery)",
5
5
  "keywords": [
6
6
  "camstack",