@camstack/addon-provider-homematic 1.1.26 → 1.1.27

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
@@ -7666,6 +7666,160 @@ var EncodeProfileSchema = object({
7666
7666
  */
7667
7667
  outputArgs: array(string()).optional()
7668
7668
  });
7669
+ var COCO_TO_MACRO = {
7670
+ mapping: {
7671
+ person: "person",
7672
+ bicycle: "vehicle",
7673
+ car: "vehicle",
7674
+ motorcycle: "vehicle",
7675
+ airplane: "vehicle",
7676
+ bus: "vehicle",
7677
+ train: "vehicle",
7678
+ truck: "vehicle",
7679
+ boat: "vehicle",
7680
+ bird: "animal",
7681
+ cat: "animal",
7682
+ dog: "animal",
7683
+ horse: "animal",
7684
+ sheep: "animal",
7685
+ cow: "animal",
7686
+ elephant: "animal",
7687
+ bear: "animal",
7688
+ zebra: "animal",
7689
+ giraffe: "animal",
7690
+ suitcase: "package",
7691
+ backpack: "package",
7692
+ handbag: "package"
7693
+ },
7694
+ preserveOriginal: false
7695
+ };
7696
+ var AUDIO_MACRO_LABELS = [
7697
+ {
7698
+ id: "speech",
7699
+ name: "Speech",
7700
+ icon: "🗣️"
7701
+ },
7702
+ {
7703
+ id: "scream",
7704
+ name: "Scream / Shout",
7705
+ icon: "😱"
7706
+ },
7707
+ {
7708
+ id: "crying",
7709
+ name: "Crying / Baby",
7710
+ icon: "😢"
7711
+ },
7712
+ {
7713
+ id: "laughter",
7714
+ name: "Laughter",
7715
+ icon: "😂"
7716
+ },
7717
+ {
7718
+ id: "music",
7719
+ name: "Music",
7720
+ icon: "🎵"
7721
+ },
7722
+ {
7723
+ id: "dog",
7724
+ name: "Dog",
7725
+ icon: "🐕"
7726
+ },
7727
+ {
7728
+ id: "cat",
7729
+ name: "Cat",
7730
+ icon: "🐈"
7731
+ },
7732
+ {
7733
+ id: "bird",
7734
+ name: "Bird",
7735
+ icon: "🐦"
7736
+ },
7737
+ {
7738
+ id: "animal",
7739
+ name: "Animal (other)",
7740
+ icon: "🐾"
7741
+ },
7742
+ {
7743
+ id: "alarm",
7744
+ name: "Alarm / Siren",
7745
+ icon: "🚨"
7746
+ },
7747
+ {
7748
+ id: "doorbell",
7749
+ name: "Doorbell / Knock",
7750
+ icon: "🔔"
7751
+ },
7752
+ {
7753
+ id: "glass_breaking",
7754
+ name: "Glass Breaking",
7755
+ icon: "💥"
7756
+ },
7757
+ {
7758
+ id: "gunshot",
7759
+ name: "Gunshot / Explosion",
7760
+ icon: "💣"
7761
+ },
7762
+ {
7763
+ id: "vehicle",
7764
+ name: "Vehicle",
7765
+ icon: "🚗"
7766
+ },
7767
+ {
7768
+ id: "siren",
7769
+ name: "Emergency Siren",
7770
+ icon: "🚑"
7771
+ },
7772
+ {
7773
+ id: "fire",
7774
+ name: "Fire / Smoke",
7775
+ icon: "🔥"
7776
+ },
7777
+ {
7778
+ id: "water",
7779
+ name: "Water",
7780
+ icon: "💧"
7781
+ },
7782
+ {
7783
+ id: "wind",
7784
+ name: "Wind / Weather",
7785
+ icon: "🌬️"
7786
+ },
7787
+ {
7788
+ id: "door",
7789
+ name: "Door",
7790
+ icon: "🚪"
7791
+ },
7792
+ {
7793
+ id: "footsteps",
7794
+ name: "Footsteps",
7795
+ icon: "👣"
7796
+ },
7797
+ {
7798
+ id: "crowd",
7799
+ name: "Crowd / Chatter",
7800
+ icon: "👥"
7801
+ },
7802
+ {
7803
+ id: "telephone",
7804
+ name: "Telephone",
7805
+ icon: "📞"
7806
+ },
7807
+ {
7808
+ id: "engine",
7809
+ name: "Engine / Motor",
7810
+ icon: "⚙️"
7811
+ },
7812
+ {
7813
+ id: "tools",
7814
+ name: "Tools / Construction",
7815
+ icon: "🔨"
7816
+ },
7817
+ {
7818
+ id: "silence",
7819
+ name: "Silence",
7820
+ icon: "🤫"
7821
+ }
7822
+ ];
7669
7823
  var YAMNET_TO_MACRO = {
7670
7824
  mapping: {
7671
7825
  Speech: "speech",
@@ -7932,6 +8086,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7932
8086
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7933
8087
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7934
8088
  /**
8089
+ * Unified event-kind taxonomy — THE single source of truth for
8090
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8091
+ * icon }`.
8092
+ *
8093
+ * This dictionary folds together what used to be scattered across four
8094
+ * copies:
8095
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8096
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8097
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8098
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8099
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8100
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8101
+ *
8102
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8103
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8104
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8105
+ * color or an icon: they read this dictionary (server descriptors carry the
8106
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8107
+ *
8108
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8109
+ */
8110
+ var TAXONOMY_COLORS = {
8111
+ motion: "#f59e0b",
8112
+ audio: "#06b6d4",
8113
+ person: "#22c55e",
8114
+ vehicle: "#3b82f6",
8115
+ animal: "#f97316",
8116
+ package: "#a855f7",
8117
+ sensor: "#8b5cf6",
8118
+ control: "#10b981",
8119
+ genericDetection: "#64748b"
8120
+ };
8121
+ var DETECTION_SUB_COLORS = {
8122
+ car: "#f59e0b",
8123
+ truck: "#d97706",
8124
+ bus: "#b45309",
8125
+ motorcycle: "#eab308",
8126
+ bicycle: "#ca8a04",
8127
+ airplane: "#60a5fa",
8128
+ boat: "#2563eb",
8129
+ train: "#1d4ed8",
8130
+ bird: "#14b8a6",
8131
+ dog: "#84cc16",
8132
+ cat: "#f97316",
8133
+ horse: "#a16207",
8134
+ sheep: "#a3a3a3",
8135
+ cow: "#78716c",
8136
+ elephant: "#6b7280",
8137
+ bear: "#7c2d12",
8138
+ zebra: "#404040",
8139
+ giraffe: "#d4a373"
8140
+ };
8141
+ function titleCase(id) {
8142
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8143
+ }
8144
+ var entries = /* @__PURE__ */ new Map();
8145
+ function macro(kind, category, color, iconId, label) {
8146
+ entries.set(kind, {
8147
+ kind,
8148
+ parentKind: null,
8149
+ level: "macro",
8150
+ category,
8151
+ color,
8152
+ iconId,
8153
+ labelKey: `eventKind.${kind}`,
8154
+ label
8155
+ });
8156
+ }
8157
+ function sub(kind, parentKind, category, color, iconId, label) {
8158
+ entries.set(kind, {
8159
+ kind,
8160
+ parentKind,
8161
+ level: "sub",
8162
+ category,
8163
+ color,
8164
+ iconId,
8165
+ labelKey: `eventKind.${kind}`,
8166
+ label
8167
+ });
8168
+ }
8169
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8170
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8171
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8172
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8173
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8174
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8175
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8176
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8177
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8178
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8179
+ if (entries.has(cocoClass)) continue;
8180
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8181
+ }
8182
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8183
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8184
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8185
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8186
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8187
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8188
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8189
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8190
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8191
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8192
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8193
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8194
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8195
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8196
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8197
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8198
+ sub("button", "control", "control", "#10b981", "button", "Button");
8199
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8200
+ for (const l of AUDIO_MACRO_LABELS) {
8201
+ const kind = `audio-${l.id}`;
8202
+ if (entries.has(kind)) continue;
8203
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8204
+ }
8205
+ /** The complete taxonomy dictionary, keyed by kind. */
8206
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8207
+ /**
7935
8208
  * Error types for the safe expression engine. Two distinct classes so callers
7936
8209
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7937
8210
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -18930,17 +19203,30 @@ var EventKindCategorySchema = _enum([
18930
19203
  "audio",
18931
19204
  "detection",
18932
19205
  "sensor",
19206
+ "control",
18933
19207
  "custom",
18934
19208
  "package"
18935
19209
  ]);
19210
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19211
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
18936
19212
  var EventKindDescriptorSchema = object({
18937
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19213
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
18938
19214
  kind: string(),
19215
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19216
+ labelKey: string(),
19217
+ /** English fallback label (kept for clients that don't translate). */
18939
19218
  label: string(),
18940
19219
  /** Hex color for timeline/legend rendering. */
18941
19220
  color: string(),
19221
+ /** Dictionary id → lucide component on the UI side. */
19222
+ iconId: string(),
19223
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
18942
19224
  icon: EventKindIconSchema,
18943
19225
  category: EventKindCategorySchema,
19226
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19227
+ parentKind: string().nullable(),
19228
+ /** Derived from `parentKind`, explicit for the client tree. */
19229
+ level: EventKindLevelSchema,
18944
19230
  /** Which cap + device contributes this kind. For built-ins the camera
18945
19231
  * itself; for sensor kinds the LINKED source device. */
18946
19232
  source: object({
@@ -19013,11 +19299,21 @@ var TrackAudioLabelSchema = object({
19013
19299
  firstAt: number(),
19014
19300
  lastAt: number()
19015
19301
  });
19302
+ /**
19303
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19304
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19305
+ * linked sensor/control state change (no positions; carries a snapshot). The
19306
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19307
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19308
+ */
19309
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19016
19310
  var TrackSchema = object({
19017
19311
  trackId: string(),
19018
19312
  deviceId: number(),
19019
19313
  className: string(),
19020
19314
  label: string().optional(),
19315
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19316
+ source: TrackSourceSchema.optional(),
19021
19317
  firstSeen: number(),
19022
19318
  lastSeen: number(),
19023
19319
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19394,6 +19690,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19394
19690
  eventId: string(),
19395
19691
  timestamp: number()
19396
19692
  });
19693
+ /**
19694
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19695
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19696
+ * caps into per-camera event-kind descriptors.
19697
+ *
19698
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19699
+ * is NOT duplicated here — every entry is derived from the single
19700
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19701
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19702
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19703
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19704
+ * eventful cap is missing.
19705
+ */
19706
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19707
+ var LEGACY_ICON = {
19708
+ motion: "motion",
19709
+ audio: "audio",
19710
+ person: "person",
19711
+ vehicle: "vehicle",
19712
+ animal: "animal",
19713
+ package: "package",
19714
+ door: "door",
19715
+ pir: "pir",
19716
+ smoke: "smoke",
19717
+ water: "water",
19718
+ button: "button",
19719
+ generic: "generic",
19720
+ gas: "smoke",
19721
+ vibration: "generic",
19722
+ tamper: "generic",
19723
+ presence: "person",
19724
+ lock: "generic",
19725
+ siren: "generic",
19726
+ switch: "generic",
19727
+ doorbell: "button"
19728
+ };
19729
+ function legacyIcon(iconId) {
19730
+ return LEGACY_ICON[iconId] ?? "generic";
19731
+ }
19732
+ /**
19733
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19734
+ * The anti-drift guard cross-checks this against the eventful caps declared
19735
+ * in `packages/types/src/capabilities/*.cap.ts`.
19736
+ */
19737
+ var CAP_TO_KIND = {
19738
+ contact: "contact",
19739
+ motion: "motion-sensor",
19740
+ smoke: "smoke",
19741
+ flood: "flood",
19742
+ gas: "gas",
19743
+ "carbon-monoxide": "carbon-monoxide",
19744
+ vibration: "vibration",
19745
+ tamper: "tamper",
19746
+ presence: "presence",
19747
+ "enum-sensor": "enum-sensor",
19748
+ "event-emitter": "device-event",
19749
+ "lock-control": "lock",
19750
+ switch: "switch",
19751
+ button: "button",
19752
+ doorbell: "doorbell"
19753
+ };
19754
+ function buildDescriptor(capName, kind) {
19755
+ const t = EVENT_TAXONOMY[kind];
19756
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19757
+ return {
19758
+ ...t,
19759
+ icon: legacyIcon(t.iconId)
19760
+ };
19761
+ }
19762
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19397
19763
  var CameraPipelineConfigSchema = object({
19398
19764
  engine: PipelineEngineChoiceSchema.optional(),
19399
19765
  steps: array(PipelineStepInputSchema).readonly(),
package/dist/addon.mjs CHANGED
@@ -7667,6 +7667,160 @@ var EncodeProfileSchema = object({
7667
7667
  */
7668
7668
  outputArgs: array(string()).optional()
7669
7669
  });
7670
+ var COCO_TO_MACRO = {
7671
+ mapping: {
7672
+ person: "person",
7673
+ bicycle: "vehicle",
7674
+ car: "vehicle",
7675
+ motorcycle: "vehicle",
7676
+ airplane: "vehicle",
7677
+ bus: "vehicle",
7678
+ train: "vehicle",
7679
+ truck: "vehicle",
7680
+ boat: "vehicle",
7681
+ bird: "animal",
7682
+ cat: "animal",
7683
+ dog: "animal",
7684
+ horse: "animal",
7685
+ sheep: "animal",
7686
+ cow: "animal",
7687
+ elephant: "animal",
7688
+ bear: "animal",
7689
+ zebra: "animal",
7690
+ giraffe: "animal",
7691
+ suitcase: "package",
7692
+ backpack: "package",
7693
+ handbag: "package"
7694
+ },
7695
+ preserveOriginal: false
7696
+ };
7697
+ var AUDIO_MACRO_LABELS = [
7698
+ {
7699
+ id: "speech",
7700
+ name: "Speech",
7701
+ icon: "🗣️"
7702
+ },
7703
+ {
7704
+ id: "scream",
7705
+ name: "Scream / Shout",
7706
+ icon: "😱"
7707
+ },
7708
+ {
7709
+ id: "crying",
7710
+ name: "Crying / Baby",
7711
+ icon: "😢"
7712
+ },
7713
+ {
7714
+ id: "laughter",
7715
+ name: "Laughter",
7716
+ icon: "😂"
7717
+ },
7718
+ {
7719
+ id: "music",
7720
+ name: "Music",
7721
+ icon: "🎵"
7722
+ },
7723
+ {
7724
+ id: "dog",
7725
+ name: "Dog",
7726
+ icon: "🐕"
7727
+ },
7728
+ {
7729
+ id: "cat",
7730
+ name: "Cat",
7731
+ icon: "🐈"
7732
+ },
7733
+ {
7734
+ id: "bird",
7735
+ name: "Bird",
7736
+ icon: "🐦"
7737
+ },
7738
+ {
7739
+ id: "animal",
7740
+ name: "Animal (other)",
7741
+ icon: "🐾"
7742
+ },
7743
+ {
7744
+ id: "alarm",
7745
+ name: "Alarm / Siren",
7746
+ icon: "🚨"
7747
+ },
7748
+ {
7749
+ id: "doorbell",
7750
+ name: "Doorbell / Knock",
7751
+ icon: "🔔"
7752
+ },
7753
+ {
7754
+ id: "glass_breaking",
7755
+ name: "Glass Breaking",
7756
+ icon: "💥"
7757
+ },
7758
+ {
7759
+ id: "gunshot",
7760
+ name: "Gunshot / Explosion",
7761
+ icon: "💣"
7762
+ },
7763
+ {
7764
+ id: "vehicle",
7765
+ name: "Vehicle",
7766
+ icon: "🚗"
7767
+ },
7768
+ {
7769
+ id: "siren",
7770
+ name: "Emergency Siren",
7771
+ icon: "🚑"
7772
+ },
7773
+ {
7774
+ id: "fire",
7775
+ name: "Fire / Smoke",
7776
+ icon: "🔥"
7777
+ },
7778
+ {
7779
+ id: "water",
7780
+ name: "Water",
7781
+ icon: "💧"
7782
+ },
7783
+ {
7784
+ id: "wind",
7785
+ name: "Wind / Weather",
7786
+ icon: "🌬️"
7787
+ },
7788
+ {
7789
+ id: "door",
7790
+ name: "Door",
7791
+ icon: "🚪"
7792
+ },
7793
+ {
7794
+ id: "footsteps",
7795
+ name: "Footsteps",
7796
+ icon: "👣"
7797
+ },
7798
+ {
7799
+ id: "crowd",
7800
+ name: "Crowd / Chatter",
7801
+ icon: "👥"
7802
+ },
7803
+ {
7804
+ id: "telephone",
7805
+ name: "Telephone",
7806
+ icon: "📞"
7807
+ },
7808
+ {
7809
+ id: "engine",
7810
+ name: "Engine / Motor",
7811
+ icon: "⚙️"
7812
+ },
7813
+ {
7814
+ id: "tools",
7815
+ name: "Tools / Construction",
7816
+ icon: "🔨"
7817
+ },
7818
+ {
7819
+ id: "silence",
7820
+ name: "Silence",
7821
+ icon: "🤫"
7822
+ }
7823
+ ];
7670
7824
  var YAMNET_TO_MACRO = {
7671
7825
  mapping: {
7672
7826
  Speech: "speech",
@@ -7933,6 +8087,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7933
8087
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7934
8088
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7935
8089
  /**
8090
+ * Unified event-kind taxonomy — THE single source of truth for
8091
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8092
+ * icon }`.
8093
+ *
8094
+ * This dictionary folds together what used to be scattered across four
8095
+ * copies:
8096
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8097
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8098
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8099
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8100
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8101
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8102
+ *
8103
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8104
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8105
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8106
+ * color or an icon: they read this dictionary (server descriptors carry the
8107
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8108
+ *
8109
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8110
+ */
8111
+ var TAXONOMY_COLORS = {
8112
+ motion: "#f59e0b",
8113
+ audio: "#06b6d4",
8114
+ person: "#22c55e",
8115
+ vehicle: "#3b82f6",
8116
+ animal: "#f97316",
8117
+ package: "#a855f7",
8118
+ sensor: "#8b5cf6",
8119
+ control: "#10b981",
8120
+ genericDetection: "#64748b"
8121
+ };
8122
+ var DETECTION_SUB_COLORS = {
8123
+ car: "#f59e0b",
8124
+ truck: "#d97706",
8125
+ bus: "#b45309",
8126
+ motorcycle: "#eab308",
8127
+ bicycle: "#ca8a04",
8128
+ airplane: "#60a5fa",
8129
+ boat: "#2563eb",
8130
+ train: "#1d4ed8",
8131
+ bird: "#14b8a6",
8132
+ dog: "#84cc16",
8133
+ cat: "#f97316",
8134
+ horse: "#a16207",
8135
+ sheep: "#a3a3a3",
8136
+ cow: "#78716c",
8137
+ elephant: "#6b7280",
8138
+ bear: "#7c2d12",
8139
+ zebra: "#404040",
8140
+ giraffe: "#d4a373"
8141
+ };
8142
+ function titleCase(id) {
8143
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8144
+ }
8145
+ var entries = /* @__PURE__ */ new Map();
8146
+ function macro(kind, category, color, iconId, label) {
8147
+ entries.set(kind, {
8148
+ kind,
8149
+ parentKind: null,
8150
+ level: "macro",
8151
+ category,
8152
+ color,
8153
+ iconId,
8154
+ labelKey: `eventKind.${kind}`,
8155
+ label
8156
+ });
8157
+ }
8158
+ function sub(kind, parentKind, category, color, iconId, label) {
8159
+ entries.set(kind, {
8160
+ kind,
8161
+ parentKind,
8162
+ level: "sub",
8163
+ category,
8164
+ color,
8165
+ iconId,
8166
+ labelKey: `eventKind.${kind}`,
8167
+ label
8168
+ });
8169
+ }
8170
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8171
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8172
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8173
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8174
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8175
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8176
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8177
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8178
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8179
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8180
+ if (entries.has(cocoClass)) continue;
8181
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8182
+ }
8183
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8184
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8185
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8186
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8187
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8188
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8189
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8190
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8191
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8192
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8193
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8194
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8195
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8196
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8197
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8198
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8199
+ sub("button", "control", "control", "#10b981", "button", "Button");
8200
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8201
+ for (const l of AUDIO_MACRO_LABELS) {
8202
+ const kind = `audio-${l.id}`;
8203
+ if (entries.has(kind)) continue;
8204
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8205
+ }
8206
+ /** The complete taxonomy dictionary, keyed by kind. */
8207
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8208
+ /**
7936
8209
  * Error types for the safe expression engine. Two distinct classes so callers
7937
8210
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7938
8211
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -18931,17 +19204,30 @@ var EventKindCategorySchema = _enum([
18931
19204
  "audio",
18932
19205
  "detection",
18933
19206
  "sensor",
19207
+ "control",
18934
19208
  "custom",
18935
19209
  "package"
18936
19210
  ]);
19211
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19212
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
18937
19213
  var EventKindDescriptorSchema = object({
18938
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19214
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
18939
19215
  kind: string(),
19216
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19217
+ labelKey: string(),
19218
+ /** English fallback label (kept for clients that don't translate). */
18940
19219
  label: string(),
18941
19220
  /** Hex color for timeline/legend rendering. */
18942
19221
  color: string(),
19222
+ /** Dictionary id → lucide component on the UI side. */
19223
+ iconId: string(),
19224
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
18943
19225
  icon: EventKindIconSchema,
18944
19226
  category: EventKindCategorySchema,
19227
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19228
+ parentKind: string().nullable(),
19229
+ /** Derived from `parentKind`, explicit for the client tree. */
19230
+ level: EventKindLevelSchema,
18945
19231
  /** Which cap + device contributes this kind. For built-ins the camera
18946
19232
  * itself; for sensor kinds the LINKED source device. */
18947
19233
  source: object({
@@ -19014,11 +19300,21 @@ var TrackAudioLabelSchema = object({
19014
19300
  firstAt: number(),
19015
19301
  lastAt: number()
19016
19302
  });
19303
+ /**
19304
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19305
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19306
+ * linked sensor/control state change (no positions; carries a snapshot). The
19307
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19308
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19309
+ */
19310
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19017
19311
  var TrackSchema = object({
19018
19312
  trackId: string(),
19019
19313
  deviceId: number(),
19020
19314
  className: string(),
19021
19315
  label: string().optional(),
19316
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19317
+ source: TrackSourceSchema.optional(),
19022
19318
  firstSeen: number(),
19023
19319
  lastSeen: number(),
19024
19320
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19395,6 +19691,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19395
19691
  eventId: string(),
19396
19692
  timestamp: number()
19397
19693
  });
19694
+ /**
19695
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19696
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19697
+ * caps into per-camera event-kind descriptors.
19698
+ *
19699
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19700
+ * is NOT duplicated here — every entry is derived from the single
19701
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19702
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19703
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19704
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19705
+ * eventful cap is missing.
19706
+ */
19707
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19708
+ var LEGACY_ICON = {
19709
+ motion: "motion",
19710
+ audio: "audio",
19711
+ person: "person",
19712
+ vehicle: "vehicle",
19713
+ animal: "animal",
19714
+ package: "package",
19715
+ door: "door",
19716
+ pir: "pir",
19717
+ smoke: "smoke",
19718
+ water: "water",
19719
+ button: "button",
19720
+ generic: "generic",
19721
+ gas: "smoke",
19722
+ vibration: "generic",
19723
+ tamper: "generic",
19724
+ presence: "person",
19725
+ lock: "generic",
19726
+ siren: "generic",
19727
+ switch: "generic",
19728
+ doorbell: "button"
19729
+ };
19730
+ function legacyIcon(iconId) {
19731
+ return LEGACY_ICON[iconId] ?? "generic";
19732
+ }
19733
+ /**
19734
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19735
+ * The anti-drift guard cross-checks this against the eventful caps declared
19736
+ * in `packages/types/src/capabilities/*.cap.ts`.
19737
+ */
19738
+ var CAP_TO_KIND = {
19739
+ contact: "contact",
19740
+ motion: "motion-sensor",
19741
+ smoke: "smoke",
19742
+ flood: "flood",
19743
+ gas: "gas",
19744
+ "carbon-monoxide": "carbon-monoxide",
19745
+ vibration: "vibration",
19746
+ tamper: "tamper",
19747
+ presence: "presence",
19748
+ "enum-sensor": "enum-sensor",
19749
+ "event-emitter": "device-event",
19750
+ "lock-control": "lock",
19751
+ switch: "switch",
19752
+ button: "button",
19753
+ doorbell: "doorbell"
19754
+ };
19755
+ function buildDescriptor(capName, kind) {
19756
+ const t = EVENT_TAXONOMY[kind];
19757
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19758
+ return {
19759
+ ...t,
19760
+ icon: legacyIcon(t.iconId)
19761
+ };
19762
+ }
19763
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19398
19764
  var CameraPipelineConfigSchema = object({
19399
19765
  engine: PipelineEngineChoiceSchema.optional(),
19400
19766
  steps: array(PipelineStepInputSchema).readonly(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.1.26",
3
+ "version": "1.1.27",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",