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