@camstack/addon-provider-homematic 1.2.24 → 1.2.26

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.
Files changed (3) hide show
  1. package/dist/addon.js +204 -61
  2. package/dist/addon.mjs +204 -61
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7575,7 +7575,7 @@ var RecordingBandSchema = object({
7575
7575
  postBufferSec: number().min(0).optional()
7576
7576
  });
7577
7577
  ({
7578
- preBufferSec: 10,
7578
+ preBufferSec: 15,
7579
7579
  postBufferSec: 30
7580
7580
  }).postBufferSec * 1e3;
7581
7581
  /**
@@ -13844,128 +13844,103 @@ var COCO_TO_MACRO = {
13844
13844
  var AUDIO_MACRO_LABELS = [
13845
13845
  {
13846
13846
  id: "speech",
13847
- name: "Speech",
13848
- icon: "🗣️"
13847
+ name: "Speech"
13849
13848
  },
13850
13849
  {
13851
13850
  id: "scream",
13852
- name: "Scream / Shout",
13853
- icon: "😱"
13851
+ name: "Scream / Shout"
13854
13852
  },
13855
13853
  {
13856
13854
  id: "crying",
13857
- name: "Crying / Baby",
13858
- icon: "😢"
13855
+ name: "Crying / Baby"
13859
13856
  },
13860
13857
  {
13861
13858
  id: "laughter",
13862
- name: "Laughter",
13863
- icon: "😂"
13859
+ name: "Laughter"
13864
13860
  },
13865
13861
  {
13866
13862
  id: "music",
13867
- name: "Music",
13868
- icon: "🎵"
13863
+ name: "Music"
13869
13864
  },
13870
13865
  {
13871
13866
  id: "dog",
13872
- name: "Dog",
13873
- icon: "🐕"
13867
+ name: "Dog"
13874
13868
  },
13875
13869
  {
13876
13870
  id: "cat",
13877
- name: "Cat",
13878
- icon: "🐈"
13871
+ name: "Cat"
13879
13872
  },
13880
13873
  {
13881
13874
  id: "bird",
13882
- name: "Bird",
13883
- icon: "🐦"
13875
+ name: "Bird"
13884
13876
  },
13885
13877
  {
13886
13878
  id: "animal",
13887
- name: "Animal (other)",
13888
- icon: "🐾"
13879
+ name: "Animal (other)"
13889
13880
  },
13890
13881
  {
13891
13882
  id: "alarm",
13892
- name: "Alarm / Siren",
13893
- icon: "🚨"
13883
+ name: "Alarm / Siren"
13894
13884
  },
13895
13885
  {
13896
13886
  id: "doorbell",
13897
- name: "Doorbell / Knock",
13898
- icon: "🔔"
13887
+ name: "Doorbell / Knock"
13899
13888
  },
13900
13889
  {
13901
13890
  id: "glass_breaking",
13902
- name: "Glass Breaking",
13903
- icon: "💥"
13891
+ name: "Glass Breaking"
13904
13892
  },
13905
13893
  {
13906
13894
  id: "gunshot",
13907
- name: "Gunshot / Explosion",
13908
- icon: "💣"
13895
+ name: "Gunshot / Explosion"
13909
13896
  },
13910
13897
  {
13911
13898
  id: "vehicle",
13912
- name: "Vehicle",
13913
- icon: "🚗"
13899
+ name: "Vehicle"
13914
13900
  },
13915
13901
  {
13916
13902
  id: "siren",
13917
- name: "Emergency Siren",
13918
- icon: "🚑"
13903
+ name: "Emergency Siren"
13919
13904
  },
13920
13905
  {
13921
13906
  id: "fire",
13922
- name: "Fire / Smoke",
13923
- icon: "🔥"
13907
+ name: "Fire / Smoke"
13924
13908
  },
13925
13909
  {
13926
13910
  id: "water",
13927
- name: "Water",
13928
- icon: "💧"
13911
+ name: "Water"
13929
13912
  },
13930
13913
  {
13931
13914
  id: "wind",
13932
- name: "Wind / Weather",
13933
- icon: "🌬️"
13915
+ name: "Wind / Weather"
13934
13916
  },
13935
13917
  {
13936
13918
  id: "door",
13937
- name: "Door",
13938
- icon: "🚪"
13919
+ name: "Door"
13939
13920
  },
13940
13921
  {
13941
13922
  id: "footsteps",
13942
- name: "Footsteps",
13943
- icon: "👣"
13923
+ name: "Footsteps"
13944
13924
  },
13945
13925
  {
13946
13926
  id: "crowd",
13947
- name: "Crowd / Chatter",
13948
- icon: "👥"
13927
+ name: "Crowd / Chatter"
13949
13928
  },
13950
13929
  {
13951
13930
  id: "telephone",
13952
- name: "Telephone",
13953
- icon: "📞"
13931
+ name: "Telephone"
13954
13932
  },
13955
13933
  {
13956
13934
  id: "engine",
13957
- name: "Engine / Motor",
13958
- icon: "⚙️"
13935
+ name: "Engine / Motor"
13959
13936
  },
13960
13937
  {
13961
13938
  id: "tools",
13962
- name: "Tools / Construction",
13963
- icon: "🔨"
13939
+ name: "Tools / Construction"
13964
13940
  },
13965
13941
  {
13966
13942
  id: "silence",
13967
- name: "Silence",
13968
- icon: "🤫"
13943
+ name: "Silence"
13969
13944
  }
13970
13945
  ];
13971
13946
  var YAMNET_TO_MACRO = {
@@ -16467,7 +16442,7 @@ var TrackEnvelopeSchema = object({
16467
16442
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
16468
16443
  * keeps every scalar the list surfaces actually render (ids, class(es),
16469
16444
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
16470
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
16445
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
16471
16446
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
16472
16447
  * `getTrack`. Mirrors the event-store `projection` convention
16473
16448
  * (`getObjectEvents` et al.).
@@ -16762,6 +16737,35 @@ var TrackSchema = object({
16762
16737
  */
16763
16738
  hasFace: boolean().optional(),
16764
16739
  /**
16740
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
16741
+ * face an operator could ASSIGN to an identity.
16742
+ *
16743
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
16744
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
16745
+ * branch, before every gate, and means no more than "a face detector produced
16746
+ * a face detail". This one is stamped at the single moment the gallery row
16747
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
16748
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
16749
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
16750
+ * the crop-store drop. Everything between the detector and that insert can
16751
+ * legitimately refuse the face, so a flag written any earlier promises the
16752
+ * operator something to assign and delivers nothing.
16753
+ *
16754
+ * **Independent of recognition.** A face collected but never auto-matched is
16755
+ * still assignable — it is in fact the face an operator most wants to reach —
16756
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
16757
+ * `subLabel`; this says only that the raw material exists.
16758
+ *
16759
+ * **Set once, never cleared.** A track that produced a gallery row produced
16760
+ * one; deleting the row later is the gallery's business, not this flag's.
16761
+ *
16762
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
16763
+ * before the column omits it, and so does every server that predates the
16764
+ * field. A consumer must test `=== true` and render nothing otherwise —
16765
+ * never infer "no assignable face".
16766
+ */
16767
+ hasEmbeddedFace: boolean().optional(),
16768
+ /**
16765
16769
  * This subject CONTAINS a folded rider — a person the rider-pairing step
16766
16770
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16767
16771
  * so the passage is tracked once and as a VEHICLE.
@@ -18151,6 +18155,32 @@ var DetailResultSchema = object({
18151
18155
  /** FRAME-space bbox (already mapped back from crop space). */
18152
18156
  bbox: NativeCropBboxSchema.optional(),
18153
18157
  embedding: string().optional(),
18158
+ /**
18159
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
18160
+ * so an older runner that does not send it degrades to "unmeasurable", which
18161
+ * the consuming gate treats as accept — never as reject.
18162
+ */
18163
+ embeddingMagnitude: number().optional(),
18164
+ /**
18165
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
18166
+ * `label`, and for the same reason: the producing node is the only side that
18167
+ * knows which model actually ran, and re-deriving it downstream is exactly
18168
+ * the inference this model exists to forbid.
18169
+ *
18170
+ * Additive and optional, so a runner that predates it degrades to
18171
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
18172
+ * its previous assumption instead of refusing.
18173
+ *
18174
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
18175
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
18176
+ * with nothing on the wire it took the id from the GALLERY, so the gate
18177
+ * compared the gallery against itself and could never fire. That turned the
18178
+ * one guard protecting the face feature space into a tautology, and it is
18179
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
18180
+ * gallery as the catalog comment predicts — it would have scored the new
18181
+ * model's vectors against the old model's samples in one cosine space.
18182
+ */
18183
+ embeddingModelId: string().optional(),
18154
18184
  label: string().optional(),
18155
18185
  /**
18156
18186
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -19026,6 +19056,12 @@ var CameraStatusSchema = object({
19026
19056
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19027
19057
  fetchedAt: number()
19028
19058
  });
19059
+ var InferenceDeviceExclusionReasonSchema = _enum([
19060
+ "disabled",
19061
+ "unavailable",
19062
+ "cannot-host-camera-root",
19063
+ "accelerator-preferred"
19064
+ ]);
19029
19065
  var NodeInferenceDeviceSchema = object({
19030
19066
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
19031
19067
  key: string(),
@@ -19056,7 +19092,17 @@ var NodeInferenceDeviceSchema = object({
19056
19092
  * available per format; this is the stored selection that becomes the
19057
19093
  * default for EVERY camera landing on this accelerator.
19058
19094
  */
19059
- steps: record(string(), DeviceStepConfigSchema).optional()
19095
+ steps: record(string(), DeviceStepConfigSchema).optional(),
19096
+ /**
19097
+ * `null` when the device IS a camera-root candidate on this node; otherwise
19098
+ * the reason the dispatcher drops it. Computed by the SAME
19099
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
19100
+ * never disagree with the election — deriving it in the UI from
19101
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
19102
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
19103
+ * "an accelerator is serving" predicate).
19104
+ */
19105
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
19060
19106
  });
19061
19107
  var NodeInferenceDevicesSchema = object({
19062
19108
  nodeId: string(),
@@ -19561,6 +19607,7 @@ DeviceType.Camera, method(object({
19561
19607
  isBattery: boolean(),
19562
19608
  reason: _enum([
19563
19609
  "disabled",
19610
+ "offline",
19564
19611
  "sleeping",
19565
19612
  "unreachable",
19566
19613
  "waking"
@@ -19606,9 +19653,17 @@ targets: array(object({
19606
19653
  sleeping: boolean(),
19607
19654
  /** Current device state rendered over the cached frame. State images
19608
19655
  * remain authoritative even when their photographic background is
19609
- * old; null means the link must carry a current camera frame. */
19656
+ * old; null means the link must carry a current camera frame.
19657
+ *
19658
+ * **This is the ONLY source a surface may derive an inactive tile
19659
+ * from.** A client that reads a device's `online`/`disabled` flags and
19660
+ * draws its own verdict will disagree with the pixels it is painting
19661
+ * — the frame comes from here, and so must the sentence over it
19662
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
19663
+ * state. */
19610
19664
  stateReason: _enum([
19611
19665
  "disabled",
19666
+ "offline",
19612
19667
  "sleeping",
19613
19668
  "unreachable",
19614
19669
  "waking"
@@ -23898,7 +23953,12 @@ var ListResultSchema = object({
23898
23953
  probedAt: number()
23899
23954
  });
23900
23955
  var PreferredSchema = LocalInterfaceSchema.nullable();
23901
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
23956
+ /**
23957
+ * Candidate base URL for the SDK to race on connect. Order matters —
23958
+ * the SDK should attempt these top-to-bottom with a short per-candidate
23959
+ * timeout (e.g. 1500ms) and cache the winner for the session.
23960
+ */
23961
+ var ConnectionEndpointSchema = object({
23902
23962
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
23903
23963
  label: string(),
23904
23964
  /** Fully-formed base URL with scheme + host + port. */
@@ -23941,7 +24001,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
23941
24001
  * ordering between polls.
23942
24002
  */
23943
24003
  priority: number()
23944
- })).readonly() });
24004
+ });
24005
+ /**
24006
+ * Where the advertised local port came from. Ordered most → least
24007
+ * authoritative, and the whole point of returning it: a client must be able to
24008
+ * tell a FACT about the hub's socket from an echo of its own guess.
24009
+ */
24010
+ var LocalPortSourceEnum = _enum([
24011
+ "server-config",
24012
+ "server-env",
24013
+ "caller-hint",
24014
+ "default"
24015
+ ]);
24016
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
24017
+ var AdvertisedLocalPortSchema = object({
24018
+ port: number().int().min(1).max(65535),
24019
+ source: LocalPortSourceEnum
24020
+ });
24021
+ var GetConnectionEndpointsResultSchema = object({
24022
+ endpoints: array(ConnectionEndpointSchema).readonly(),
24023
+ /**
24024
+ * The port the hub built the LAN/loopback URLs with, and where that number
24025
+ * came from.
24026
+ *
24027
+ * Returned rather than merely applied, because "the URL is right" and "the
24028
+ * client can KNOW the URL is right" are different properties. A client that
24029
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
24030
+ * a hub that echoed the port the client sent, so it cannot decide whether to
24031
+ * race the candidate or discard it. With `source` it can: anything but
24032
+ * `caller-hint` is the hub's own socket.
24033
+ *
24034
+ * Absent on hubs predating this field — a client that finds it missing is
24035
+ * talking to an echoing hub and must degrade exactly as it does for
24036
+ * `caller-hint`.
24037
+ */
24038
+ localPort: AdvertisedLocalPortSchema
24039
+ });
23945
24040
  /**
23946
24041
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
23947
24042
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -23962,8 +24057,13 @@ var AllowedAddressesSchema = object({
23962
24057
  */
23963
24058
  addresses: array(string()).readonly() });
23964
24059
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
23965
- /** Local hub HTTP port to use in base URLs. */
23966
- port: number().int().min(1).max(65535),
24060
+ /**
24061
+ * LEGACY HINT — do not send from new code. Kept optional so clients
24062
+ * written against the echoing contract keep working; the hub uses it
24063
+ * only when it cannot read its own port, and says so via
24064
+ * `localPort.source === 'caller-hint'`.
24065
+ */
24066
+ port: number().int().min(1).max(65535).optional(),
23967
24067
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
23968
24068
  * candidate. Default `true`. */
23969
24069
  includeLoopback: boolean().optional(),
@@ -37151,7 +37251,6 @@ var NC_AUDIO_DEFAULTS = {
37151
37251
  hitPercent: 60,
37152
37252
  samplingSeconds: 10
37153
37253
  };
37154
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37155
37254
  new Set(["devices", "classes"]);
37156
37255
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
37157
37256
  /**
@@ -37385,6 +37484,39 @@ TimelapseRuleInputSchema.extend({
37385
37484
  createdAt: number(),
37386
37485
  updatedAt: number()
37387
37486
  });
37487
+ Object.freeze(Object.fromEntries([{
37488
+ stepId: "face-embedding",
37489
+ label: "Face recognition model",
37490
+ defaultModelId: "arcface-r100",
37491
+ indexName: "the enrolled face gallery",
37492
+ options: [
37493
+ {
37494
+ id: "arcface-r100",
37495
+ label: "ArcFace (ResNet34)"
37496
+ },
37497
+ {
37498
+ id: "auraface-r100",
37499
+ label: "AuraFace R100"
37500
+ },
37501
+ {
37502
+ id: "inception-resnet-v1",
37503
+ label: "Inception ResNet V1"
37504
+ }
37505
+ ]
37506
+ }, {
37507
+ stepId: "clip-embedding",
37508
+ label: "Semantic search model",
37509
+ defaultModelId: "mobileclip-s1",
37510
+ indexName: "the object semantic-search index",
37511
+ options: [{
37512
+ id: "mobileclip-s1",
37513
+ label: "MobileCLIP S1"
37514
+ }, {
37515
+ id: "mobileclip-s2",
37516
+ label: "MobileCLIP S2"
37517
+ }]
37518
+ }].map((s) => [s.stepId, s.defaultModelId])));
37519
+ string().min(1);
37388
37520
  object({
37389
37521
  /**
37390
37522
  * Fraction of the box's own size added on EACH side before cutting.
@@ -37405,10 +37537,21 @@ object({
37405
37537
  */
37406
37538
  square: boolean()
37407
37539
  });
37408
- ({
37540
+ /**
37541
+ * The convention in force when nobody has configured one — byte-for-byte the
37542
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
37543
+ */
37544
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
37409
37545
  paddingRatio: .15,
37410
37546
  square: false
37411
- }).paddingRatio;
37547
+ };
37548
+ Object.freeze({
37549
+ x: 0,
37550
+ y: 0,
37551
+ w: 1,
37552
+ h: 1
37553
+ });
37554
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
37412
37555
  /**
37413
37556
  * WHICH delivered frames the decode worker retains a native copy of.
37414
37557
  *
package/dist/addon.mjs CHANGED
@@ -7576,7 +7576,7 @@ var RecordingBandSchema = object({
7576
7576
  postBufferSec: number().min(0).optional()
7577
7577
  });
7578
7578
  ({
7579
- preBufferSec: 10,
7579
+ preBufferSec: 15,
7580
7580
  postBufferSec: 30
7581
7581
  }).postBufferSec * 1e3;
7582
7582
  /**
@@ -13845,128 +13845,103 @@ var COCO_TO_MACRO = {
13845
13845
  var AUDIO_MACRO_LABELS = [
13846
13846
  {
13847
13847
  id: "speech",
13848
- name: "Speech",
13849
- icon: "🗣️"
13848
+ name: "Speech"
13850
13849
  },
13851
13850
  {
13852
13851
  id: "scream",
13853
- name: "Scream / Shout",
13854
- icon: "😱"
13852
+ name: "Scream / Shout"
13855
13853
  },
13856
13854
  {
13857
13855
  id: "crying",
13858
- name: "Crying / Baby",
13859
- icon: "😢"
13856
+ name: "Crying / Baby"
13860
13857
  },
13861
13858
  {
13862
13859
  id: "laughter",
13863
- name: "Laughter",
13864
- icon: "😂"
13860
+ name: "Laughter"
13865
13861
  },
13866
13862
  {
13867
13863
  id: "music",
13868
- name: "Music",
13869
- icon: "🎵"
13864
+ name: "Music"
13870
13865
  },
13871
13866
  {
13872
13867
  id: "dog",
13873
- name: "Dog",
13874
- icon: "🐕"
13868
+ name: "Dog"
13875
13869
  },
13876
13870
  {
13877
13871
  id: "cat",
13878
- name: "Cat",
13879
- icon: "🐈"
13872
+ name: "Cat"
13880
13873
  },
13881
13874
  {
13882
13875
  id: "bird",
13883
- name: "Bird",
13884
- icon: "🐦"
13876
+ name: "Bird"
13885
13877
  },
13886
13878
  {
13887
13879
  id: "animal",
13888
- name: "Animal (other)",
13889
- icon: "🐾"
13880
+ name: "Animal (other)"
13890
13881
  },
13891
13882
  {
13892
13883
  id: "alarm",
13893
- name: "Alarm / Siren",
13894
- icon: "🚨"
13884
+ name: "Alarm / Siren"
13895
13885
  },
13896
13886
  {
13897
13887
  id: "doorbell",
13898
- name: "Doorbell / Knock",
13899
- icon: "🔔"
13888
+ name: "Doorbell / Knock"
13900
13889
  },
13901
13890
  {
13902
13891
  id: "glass_breaking",
13903
- name: "Glass Breaking",
13904
- icon: "💥"
13892
+ name: "Glass Breaking"
13905
13893
  },
13906
13894
  {
13907
13895
  id: "gunshot",
13908
- name: "Gunshot / Explosion",
13909
- icon: "💣"
13896
+ name: "Gunshot / Explosion"
13910
13897
  },
13911
13898
  {
13912
13899
  id: "vehicle",
13913
- name: "Vehicle",
13914
- icon: "🚗"
13900
+ name: "Vehicle"
13915
13901
  },
13916
13902
  {
13917
13903
  id: "siren",
13918
- name: "Emergency Siren",
13919
- icon: "🚑"
13904
+ name: "Emergency Siren"
13920
13905
  },
13921
13906
  {
13922
13907
  id: "fire",
13923
- name: "Fire / Smoke",
13924
- icon: "🔥"
13908
+ name: "Fire / Smoke"
13925
13909
  },
13926
13910
  {
13927
13911
  id: "water",
13928
- name: "Water",
13929
- icon: "💧"
13912
+ name: "Water"
13930
13913
  },
13931
13914
  {
13932
13915
  id: "wind",
13933
- name: "Wind / Weather",
13934
- icon: "🌬️"
13916
+ name: "Wind / Weather"
13935
13917
  },
13936
13918
  {
13937
13919
  id: "door",
13938
- name: "Door",
13939
- icon: "🚪"
13920
+ name: "Door"
13940
13921
  },
13941
13922
  {
13942
13923
  id: "footsteps",
13943
- name: "Footsteps",
13944
- icon: "👣"
13924
+ name: "Footsteps"
13945
13925
  },
13946
13926
  {
13947
13927
  id: "crowd",
13948
- name: "Crowd / Chatter",
13949
- icon: "👥"
13928
+ name: "Crowd / Chatter"
13950
13929
  },
13951
13930
  {
13952
13931
  id: "telephone",
13953
- name: "Telephone",
13954
- icon: "📞"
13932
+ name: "Telephone"
13955
13933
  },
13956
13934
  {
13957
13935
  id: "engine",
13958
- name: "Engine / Motor",
13959
- icon: "⚙️"
13936
+ name: "Engine / Motor"
13960
13937
  },
13961
13938
  {
13962
13939
  id: "tools",
13963
- name: "Tools / Construction",
13964
- icon: "🔨"
13940
+ name: "Tools / Construction"
13965
13941
  },
13966
13942
  {
13967
13943
  id: "silence",
13968
- name: "Silence",
13969
- icon: "🤫"
13944
+ name: "Silence"
13970
13945
  }
13971
13946
  ];
13972
13947
  var YAMNET_TO_MACRO = {
@@ -16468,7 +16443,7 @@ var TrackEnvelopeSchema = object({
16468
16443
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
16469
16444
  * keeps every scalar the list surfaces actually render (ids, class(es),
16470
16445
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
16471
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
16446
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
16472
16447
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
16473
16448
  * `getTrack`. Mirrors the event-store `projection` convention
16474
16449
  * (`getObjectEvents` et al.).
@@ -16763,6 +16738,35 @@ var TrackSchema = object({
16763
16738
  */
16764
16739
  hasFace: boolean().optional(),
16765
16740
  /**
16741
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
16742
+ * face an operator could ASSIGN to an identity.
16743
+ *
16744
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
16745
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
16746
+ * branch, before every gate, and means no more than "a face detector produced
16747
+ * a face detail". This one is stamped at the single moment the gallery row
16748
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
16749
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
16750
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
16751
+ * the crop-store drop. Everything between the detector and that insert can
16752
+ * legitimately refuse the face, so a flag written any earlier promises the
16753
+ * operator something to assign and delivers nothing.
16754
+ *
16755
+ * **Independent of recognition.** A face collected but never auto-matched is
16756
+ * still assignable — it is in fact the face an operator most wants to reach —
16757
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
16758
+ * `subLabel`; this says only that the raw material exists.
16759
+ *
16760
+ * **Set once, never cleared.** A track that produced a gallery row produced
16761
+ * one; deleting the row later is the gallery's business, not this flag's.
16762
+ *
16763
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
16764
+ * before the column omits it, and so does every server that predates the
16765
+ * field. A consumer must test `=== true` and render nothing otherwise —
16766
+ * never infer "no assignable face".
16767
+ */
16768
+ hasEmbeddedFace: boolean().optional(),
16769
+ /**
16766
16770
  * This subject CONTAINS a folded rider — a person the rider-pairing step
16767
16771
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16768
16772
  * so the passage is tracked once and as a VEHICLE.
@@ -18152,6 +18156,32 @@ var DetailResultSchema = object({
18152
18156
  /** FRAME-space bbox (already mapped back from crop space). */
18153
18157
  bbox: NativeCropBboxSchema.optional(),
18154
18158
  embedding: string().optional(),
18159
+ /**
18160
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
18161
+ * so an older runner that does not send it degrades to "unmeasurable", which
18162
+ * the consuming gate treats as accept — never as reject.
18163
+ */
18164
+ embeddingMagnitude: number().optional(),
18165
+ /**
18166
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
18167
+ * `label`, and for the same reason: the producing node is the only side that
18168
+ * knows which model actually ran, and re-deriving it downstream is exactly
18169
+ * the inference this model exists to forbid.
18170
+ *
18171
+ * Additive and optional, so a runner that predates it degrades to
18172
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
18173
+ * its previous assumption instead of refusing.
18174
+ *
18175
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
18176
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
18177
+ * with nothing on the wire it took the id from the GALLERY, so the gate
18178
+ * compared the gallery against itself and could never fire. That turned the
18179
+ * one guard protecting the face feature space into a tautology, and it is
18180
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
18181
+ * gallery as the catalog comment predicts — it would have scored the new
18182
+ * model's vectors against the old model's samples in one cosine space.
18183
+ */
18184
+ embeddingModelId: string().optional(),
18155
18185
  label: string().optional(),
18156
18186
  /**
18157
18187
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -19027,6 +19057,12 @@ var CameraStatusSchema = object({
19027
19057
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19028
19058
  fetchedAt: number()
19029
19059
  });
19060
+ var InferenceDeviceExclusionReasonSchema = _enum([
19061
+ "disabled",
19062
+ "unavailable",
19063
+ "cannot-host-camera-root",
19064
+ "accelerator-preferred"
19065
+ ]);
19030
19066
  var NodeInferenceDeviceSchema = object({
19031
19067
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
19032
19068
  key: string(),
@@ -19057,7 +19093,17 @@ var NodeInferenceDeviceSchema = object({
19057
19093
  * available per format; this is the stored selection that becomes the
19058
19094
  * default for EVERY camera landing on this accelerator.
19059
19095
  */
19060
- steps: record(string(), DeviceStepConfigSchema).optional()
19096
+ steps: record(string(), DeviceStepConfigSchema).optional(),
19097
+ /**
19098
+ * `null` when the device IS a camera-root candidate on this node; otherwise
19099
+ * the reason the dispatcher drops it. Computed by the SAME
19100
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
19101
+ * never disagree with the election — deriving it in the UI from
19102
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
19103
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
19104
+ * "an accelerator is serving" predicate).
19105
+ */
19106
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
19061
19107
  });
19062
19108
  var NodeInferenceDevicesSchema = object({
19063
19109
  nodeId: string(),
@@ -19562,6 +19608,7 @@ DeviceType.Camera, method(object({
19562
19608
  isBattery: boolean(),
19563
19609
  reason: _enum([
19564
19610
  "disabled",
19611
+ "offline",
19565
19612
  "sleeping",
19566
19613
  "unreachable",
19567
19614
  "waking"
@@ -19607,9 +19654,17 @@ targets: array(object({
19607
19654
  sleeping: boolean(),
19608
19655
  /** Current device state rendered over the cached frame. State images
19609
19656
  * remain authoritative even when their photographic background is
19610
- * old; null means the link must carry a current camera frame. */
19657
+ * old; null means the link must carry a current camera frame.
19658
+ *
19659
+ * **This is the ONLY source a surface may derive an inactive tile
19660
+ * from.** A client that reads a device's `online`/`disabled` flags and
19661
+ * draws its own verdict will disagree with the pixels it is painting
19662
+ * — the frame comes from here, and so must the sentence over it
19663
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
19664
+ * state. */
19611
19665
  stateReason: _enum([
19612
19666
  "disabled",
19667
+ "offline",
19613
19668
  "sleeping",
19614
19669
  "unreachable",
19615
19670
  "waking"
@@ -23899,7 +23954,12 @@ var ListResultSchema = object({
23899
23954
  probedAt: number()
23900
23955
  });
23901
23956
  var PreferredSchema = LocalInterfaceSchema.nullable();
23902
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
23957
+ /**
23958
+ * Candidate base URL for the SDK to race on connect. Order matters —
23959
+ * the SDK should attempt these top-to-bottom with a short per-candidate
23960
+ * timeout (e.g. 1500ms) and cache the winner for the session.
23961
+ */
23962
+ var ConnectionEndpointSchema = object({
23903
23963
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
23904
23964
  label: string(),
23905
23965
  /** Fully-formed base URL with scheme + host + port. */
@@ -23942,7 +24002,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
23942
24002
  * ordering between polls.
23943
24003
  */
23944
24004
  priority: number()
23945
- })).readonly() });
24005
+ });
24006
+ /**
24007
+ * Where the advertised local port came from. Ordered most → least
24008
+ * authoritative, and the whole point of returning it: a client must be able to
24009
+ * tell a FACT about the hub's socket from an echo of its own guess.
24010
+ */
24011
+ var LocalPortSourceEnum = _enum([
24012
+ "server-config",
24013
+ "server-env",
24014
+ "caller-hint",
24015
+ "default"
24016
+ ]);
24017
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
24018
+ var AdvertisedLocalPortSchema = object({
24019
+ port: number().int().min(1).max(65535),
24020
+ source: LocalPortSourceEnum
24021
+ });
24022
+ var GetConnectionEndpointsResultSchema = object({
24023
+ endpoints: array(ConnectionEndpointSchema).readonly(),
24024
+ /**
24025
+ * The port the hub built the LAN/loopback URLs with, and where that number
24026
+ * came from.
24027
+ *
24028
+ * Returned rather than merely applied, because "the URL is right" and "the
24029
+ * client can KNOW the URL is right" are different properties. A client that
24030
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
24031
+ * a hub that echoed the port the client sent, so it cannot decide whether to
24032
+ * race the candidate or discard it. With `source` it can: anything but
24033
+ * `caller-hint` is the hub's own socket.
24034
+ *
24035
+ * Absent on hubs predating this field — a client that finds it missing is
24036
+ * talking to an echoing hub and must degrade exactly as it does for
24037
+ * `caller-hint`.
24038
+ */
24039
+ localPort: AdvertisedLocalPortSchema
24040
+ });
23946
24041
  /**
23947
24042
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
23948
24043
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -23963,8 +24058,13 @@ var AllowedAddressesSchema = object({
23963
24058
  */
23964
24059
  addresses: array(string()).readonly() });
23965
24060
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
23966
- /** Local hub HTTP port to use in base URLs. */
23967
- port: number().int().min(1).max(65535),
24061
+ /**
24062
+ * LEGACY HINT — do not send from new code. Kept optional so clients
24063
+ * written against the echoing contract keep working; the hub uses it
24064
+ * only when it cannot read its own port, and says so via
24065
+ * `localPort.source === 'caller-hint'`.
24066
+ */
24067
+ port: number().int().min(1).max(65535).optional(),
23968
24068
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
23969
24069
  * candidate. Default `true`. */
23970
24070
  includeLoopback: boolean().optional(),
@@ -37152,7 +37252,6 @@ var NC_AUDIO_DEFAULTS = {
37152
37252
  hitPercent: 60,
37153
37253
  samplingSeconds: 10
37154
37254
  };
37155
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37156
37255
  new Set(["devices", "classes"]);
37157
37256
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
37158
37257
  /**
@@ -37386,6 +37485,39 @@ TimelapseRuleInputSchema.extend({
37386
37485
  createdAt: number(),
37387
37486
  updatedAt: number()
37388
37487
  });
37488
+ Object.freeze(Object.fromEntries([{
37489
+ stepId: "face-embedding",
37490
+ label: "Face recognition model",
37491
+ defaultModelId: "arcface-r100",
37492
+ indexName: "the enrolled face gallery",
37493
+ options: [
37494
+ {
37495
+ id: "arcface-r100",
37496
+ label: "ArcFace (ResNet34)"
37497
+ },
37498
+ {
37499
+ id: "auraface-r100",
37500
+ label: "AuraFace R100"
37501
+ },
37502
+ {
37503
+ id: "inception-resnet-v1",
37504
+ label: "Inception ResNet V1"
37505
+ }
37506
+ ]
37507
+ }, {
37508
+ stepId: "clip-embedding",
37509
+ label: "Semantic search model",
37510
+ defaultModelId: "mobileclip-s1",
37511
+ indexName: "the object semantic-search index",
37512
+ options: [{
37513
+ id: "mobileclip-s1",
37514
+ label: "MobileCLIP S1"
37515
+ }, {
37516
+ id: "mobileclip-s2",
37517
+ label: "MobileCLIP S2"
37518
+ }]
37519
+ }].map((s) => [s.stepId, s.defaultModelId])));
37520
+ string().min(1);
37389
37521
  object({
37390
37522
  /**
37391
37523
  * Fraction of the box's own size added on EACH side before cutting.
@@ -37406,10 +37538,21 @@ object({
37406
37538
  */
37407
37539
  square: boolean()
37408
37540
  });
37409
- ({
37541
+ /**
37542
+ * The convention in force when nobody has configured one — byte-for-byte the
37543
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
37544
+ */
37545
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
37410
37546
  paddingRatio: .15,
37411
37547
  square: false
37412
- }).paddingRatio;
37548
+ };
37549
+ Object.freeze({
37550
+ x: 0,
37551
+ y: 0,
37552
+ w: 1,
37553
+ h: 1
37554
+ });
37555
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
37413
37556
  /**
37414
37557
  * WHICH delivered frames the decode worker retains a native copy of.
37415
37558
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.24",
3
+ "version": "1.2.26",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",