@camstack/addon-provider-dreame 0.2.122 → 0.2.124

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 +325 -93
  2. package/dist/addon.mjs +325 -93
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -63365,6 +63365,15 @@ var ClipSearchStrictnessSchema = _enum([
63365
63365
  "balanced",
63366
63366
  "strict"
63367
63367
  ]);
63368
+ /** Steps whose effect a root label can BE. */
63369
+ var LABEL_SUPPLY_STEP_IDS = [
63370
+ "animal-classifier",
63371
+ "vehicle-classifier",
63372
+ "package-detection"
63373
+ ];
63374
+ /** Steps whose box a root detection can BE. */
63375
+ var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
63376
+ [...LABEL_SUPPLY_STEP_IDS, ...CROP_SUPPLY_STEP_IDS];
63368
63377
  var MODEL_FORMATS = [
63369
63378
  "onnx",
63370
63379
  "coreml",
@@ -63434,11 +63443,11 @@ var ModelVariantGroupSchema = object({
63434
63443
  /** Quantization axis. Omit ⇒ the fp32 base build. */
63435
63444
  precision: _enum(["fp32", "int8"]).optional(),
63436
63445
  /**
63437
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
63438
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
63439
- * future performance variants plug into.
63446
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
63447
+ * an alternate training of the same family (Scrypted `relu_test`); chip
63448
+ * label is "Test".
63440
63449
  */
63441
- optimization: _enum(["standard", "fast"]).optional(),
63450
+ optimization: _enum(["standard", "test"]).optional(),
63442
63451
  /**
63443
63452
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
63444
63453
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -63461,9 +63470,10 @@ var ModelProviderIdSchema = _enum([
63461
63470
  * are per upstream. A persisted custom-model row written when this field was a
63462
63471
  * string still parses — the string is lifted into `{ weights }`.
63463
63472
  *
63464
- * Every licence surface (the admin "Models and licenses" page,
63465
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
63466
- * these records by `scripts/gen-model-licenses.ts` (D661).
63473
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
63474
+ * model table) is GENERATED from these records by
63475
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
63476
+ * `camstack/docs` → `/docs/detection/models`.
63467
63477
  */
63468
63478
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
63469
63479
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -63496,6 +63506,35 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
63496
63506
  /** True when the weights or their data forbid commercial use. */
63497
63507
  nonCommercial: boolean().optional()
63498
63508
  }));
63509
+ /**
63510
+ * One step a ROOT detector can supply from its own output (D730) — see
63511
+ * `ModelCatalogEntry.suppliesSteps`.
63512
+ *
63513
+ * - `label`: the root's raw class IS the step's answer. `labelToEffect` maps
63514
+ * the root's label to the step's vocabulary; `uncovered` is the vocabulary
63515
+ * the root cannot produce (shown as unavailable, never a blocker).
63516
+ * - `crop`: the root draws the box the step would have found (`license_plate`
63517
+ * for plate-detection, `face` for face-detection). The box becomes a detail
63518
+ * child of the `parentMacro` detection that contains it — never a track.
63519
+ */
63520
+ var RootStepSupplySchema = discriminatedUnion("kind", [object({
63521
+ kind: literal("label"),
63522
+ stepId: _enum(LABEL_SUPPLY_STEP_IDS),
63523
+ labelToEffect: record(string(), string()),
63524
+ uncovered: array(string()).readonly()
63525
+ }), object({
63526
+ kind: literal("crop"),
63527
+ stepId: _enum(CROP_SUPPLY_STEP_IDS),
63528
+ rootLabels: array(string()).readonly(),
63529
+ parentMacro: _enum(["vehicle", "person"]),
63530
+ childClass: _enum(["plate", "face"]),
63531
+ /**
63532
+ * The root's smaller input side, stamped on read by `rootSuppliesOf` from
63533
+ * the model entry — what a step's `minRootInputPx` gate judges. Never
63534
+ * stored by an import.
63535
+ */
63536
+ rootInputPx: number().int().positive().optional()
63537
+ })]);
63499
63538
  var ModelCatalogEntrySchema = object({
63500
63539
  id: string(),
63501
63540
  name: string(),
@@ -63622,6 +63661,16 @@ var ModelCatalogEntrySchema = object({
63622
63661
  */
63623
63662
  classMap: DetectionCatalogClassMapSchema.optional(),
63624
63663
  /**
63664
+ * The pipeline steps this ROOT detector can supply from its own output
63665
+ * (D730): its labels name a step's effect (`deer` for `animal-classifier`)
63666
+ * or draw a step's box (`license_plate` for `plate-detection`). Computed at
63667
+ * import from THIS model's labelmap by `deriveRootSupplies`, never written
63668
+ * by hand and never assumed per family; a row without it is derived on read
63669
+ * (`rootSuppliesOf`). Whether a camera uses it is the step's own
63670
+ * `useRootModel` flag (`resolveRootSupply`).
63671
+ */
63672
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional(),
63673
+ /**
63625
63674
  * The model's CLIP feature-space contract (text encoder, dimension, vector
63626
63675
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
63627
63676
  * CLIP image encoder; its absence is what "not a CLIP model" means to the
@@ -67094,7 +67143,7 @@ DeviceType.Camera;
67094
67143
  * This matters here more than anywhere else in the repo, because every provider
67095
67144
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
67096
67145
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
67097
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
67146
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
67098
67147
  * camera's own page will ask for its own login. That is correct, and pre-
67099
67148
  * filling it is the operator's business, not ours.
67100
67149
  *
@@ -69087,6 +69136,18 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
69087
69136
  kind: "mutation",
69088
69137
  auth: "admin"
69089
69138
  });
69139
+ function hostnameOf(baseUrl) {
69140
+ try {
69141
+ const host = new URL(baseUrl).hostname;
69142
+ return host.length > 0 ? host : null;
69143
+ } catch {
69144
+ return null;
69145
+ }
69146
+ }
69147
+ /** The bare hostname of a base URL, or `null` when it does not parse. */
69148
+ function hubAddressHostname(baseUrl) {
69149
+ return baseUrl === null ? null : hostnameOf(baseUrl);
69150
+ }
69090
69151
  /**
69091
69152
  * Transport bounds for AI capability methods.
69092
69153
  *
@@ -70283,7 +70344,7 @@ method(object({
70283
70344
  timeoutMs: 6e5
70284
70345
  });
70285
70346
  method(object({
70286
- nodeId: string(),
70347
+ targetNodeId: string(),
70287
70348
  modelId: string(),
70288
70349
  format: _enum(MODEL_FORMATS),
70289
70350
  entry: ModelCatalogEntrySchema
@@ -71689,6 +71750,8 @@ var NcSystemEventKindSchema = _enum([
71689
71750
  "stream-offline",
71690
71751
  "node-online",
71691
71752
  "node-offline",
71753
+ "node-restarting",
71754
+ "node-updating",
71692
71755
  "node-inference-unavailable",
71693
71756
  "detection-blind",
71694
71757
  "addon-crash-loop",
@@ -72787,8 +72850,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
72787
72850
  disabledTargetIds: array(string()).default([])
72788
72851
  });
72789
72852
  /**
72790
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
72791
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
72853
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
72854
+ * report a still-true state. `addons` is the crash-looped addons only and
72855
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
72792
72856
  * job is not an open state, and the alarm stays instant.
72793
72857
  */
72794
72858
  var NcStateSummarySectionIdSchema = _enum([
@@ -72800,6 +72864,7 @@ var NcStateSummarySectionIdSchema = _enum([
72800
72864
  "detection-blind",
72801
72865
  "nodes",
72802
72866
  "addons",
72867
+ "addon-updates",
72803
72868
  "updates"
72804
72869
  ]);
72805
72870
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -73280,7 +73345,19 @@ var NcAlarmModeDelaysListSchema = array(object({
73280
73345
  * When this mode may be armed. Absent / null = always allowed (same as a
73281
73346
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
73282
73347
  */
73283
- schedule: NcScheduleSchema.optional()
73348
+ schedule: NcScheduleSchema.optional(),
73349
+ /**
73350
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
73351
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
73352
+ * only one, and the matches ride the triggered notification's mosaic (D705).
73353
+ */
73354
+ notifyDuringEntryDelay: boolean().optional(),
73355
+ /**
73356
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
73357
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
73358
+ * is the one that starts the entry delay — the entry-delay notification already names it.
73359
+ */
73360
+ notifySensorRewatched: boolean().optional()
73284
73361
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
73285
73362
  var NcAlarmModeDelaysEditListSchema = array(object({
73286
73363
  mode: AlarmArmModeSchema,
@@ -73289,7 +73366,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
73289
73366
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
73290
73367
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
73291
73368
  /** Set a schedule, or `null` to clear (mode always allowed again). */
73292
- schedule: NcScheduleSchema.nullable().optional()
73369
+ schedule: NcScheduleSchema.nullable().optional(),
73370
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
73371
+ notifyDuringEntryDelay: boolean().nullable().optional(),
73372
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
73373
+ notifySensorRewatched: boolean().nullable().optional()
73293
73374
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
73294
73375
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
73295
73376
  var NcAlarmNonBlockingEditSchema = object({
@@ -74250,39 +74331,19 @@ Object.freeze(Object.fromEntries([{
74250
74331
  label: "Face recognition model",
74251
74332
  defaultModelId: "auraface-r100",
74252
74333
  indexName: "the enrolled face gallery",
74253
- options: [
74254
- {
74255
- id: "auraface-r100",
74256
- label: "AuraFace R100"
74257
- },
74258
- {
74259
- id: "arcface-r100",
74260
- label: "ArcFace (ResNet34)"
74261
- },
74262
- {
74263
- id: "inception-resnet-v1",
74264
- label: "Inception ResNet V1"
74265
- }
74266
- ]
74334
+ options: [{
74335
+ id: "auraface-r100",
74336
+ label: "AuraFace R100"
74337
+ }]
74267
74338
  }, {
74268
74339
  stepId: "clip-embedding",
74269
74340
  label: "Semantic search model",
74270
- defaultModelId: "mobileclip-s1",
74341
+ defaultModelId: "siglip2-b16-224",
74271
74342
  indexName: "the object semantic-search index",
74272
- options: [
74273
- {
74274
- id: "mobileclip-s1",
74275
- label: "MobileCLIP S1"
74276
- },
74277
- {
74278
- id: "mobileclip-s2",
74279
- label: "MobileCLIP S2"
74280
- },
74281
- {
74282
- id: "siglip2-b16-224",
74283
- label: "SigLIP2 B/16 (Apache-2.0)"
74284
- }
74285
- ]
74343
+ options: [{
74344
+ id: "siglip2-b16-224",
74345
+ label: "SigLIP2 B/16 (Apache-2.0)"
74346
+ }]
74286
74347
  }].map((s) => [s.stepId, s.defaultModelId])));
74287
74348
  var CLUSTER_STEP_SETTING_FIELDS = [{
74288
74349
  stepId: "face-embedding",
@@ -76208,6 +76269,34 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
76208
76269
  timestamp: number()
76209
76270
  });
76210
76271
  /**
76272
+ * A root model's boxes standing in for one crop-kind step's own inference
76273
+ * (D730) — see `runPipeline.suppliedRoot`.
76274
+ */
76275
+ var SuppliedRootOutputSchema = object({
76276
+ stepId: string(),
76277
+ /** The root model that drew the boxes — stamped on the result. */
76278
+ modelId: string(),
76279
+ detections: array(object({
76280
+ /** `[x1, y1, x2, y2]` in the pixels of the call's image. */
76281
+ bbox: tuple([
76282
+ number(),
76283
+ number(),
76284
+ number(),
76285
+ number()
76286
+ ]),
76287
+ score: number(),
76288
+ /**
76289
+ * Five facial landmarks in the same pixels — a face the step's own
76290
+ * detector already re-found inside a root face box (D730), so its
76291
+ * embedder child aligns it exactly as it aligns any detected face.
76292
+ */
76293
+ landmarks: array(object({
76294
+ x: number(),
76295
+ y: number()
76296
+ })).optional()
76297
+ })).min(1)
76298
+ });
76299
+ /**
76211
76300
  * Reference to the frame's retained NATIVE surface + the parent crop's placement
76212
76301
  * within the frame, so the executor can re-cut a leaf child ROI at native
76213
76302
  * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
@@ -76337,7 +76426,8 @@ var PipelineModelOptionSchema = object({
76337
76426
  group: ModelVariantGroupSchema.optional(),
76338
76427
  legacy: boolean().optional(),
76339
76428
  provider: ModelProviderIdSchema.optional(),
76340
- defaultConfidence: number().optional()
76429
+ defaultConfidence: number().optional(),
76430
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional()
76341
76431
  });
76342
76432
  var ConfigFieldBridge = custom();
76343
76433
  var PipelineAddonSchemaSchema = object({
@@ -76605,7 +76695,16 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
76605
76695
  * `device-not-servable`), and nothing reaches the camera's live
76606
76696
  * trace stream. Absent ⇒ unchanged behaviour.
76607
76697
  */
76608
- replay: literal(true).optional()
76698
+ replay: literal(true).optional(),
76699
+ /**
76700
+ * Root-supplied detections for the tree's ROOT step (D730). Set by
76701
+ * `runDetailSubtree` when the camera's root model supplies a crop-kind
76702
+ * step (a Frigate+ `license_plate` for `plate-detection`): the step is
76703
+ * NOT inferred, these boxes are its output, and its children (the plate
76704
+ * reader) run on them through their own crop policy. Boxes are in the
76705
+ * PIXELS of the image this call carries. Absent ⇒ the root step runs.
76706
+ */
76707
+ suppliedRoot: SuppliedRootOutputSchema.optional()
76609
76708
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
76610
76709
  steps: array(PipelineStepInputSchema).min(1),
76611
76710
  frames: array(FrameInputSchema).min(1).max(255),
@@ -77170,6 +77269,22 @@ var MotionSourceEnum = _enum([
77170
77269
  "device-activity"
77171
77270
  ]);
77172
77271
  /**
77272
+ * What opened an on-motion detection SESSION: the motion source that passed the
77273
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
77274
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
77275
+ * device name as the orchestrator's camera-sensors resolution last read it;
77276
+ * absent when unknown or when the camera itself triggered.
77277
+ *
77278
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
77279
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
77280
+ * runner never decides anything on it.
77281
+ */
77282
+ var SessionTriggerSchema = object({
77283
+ source: MotionSourceEnum,
77284
+ triggeredBy: MotionTriggeredBySchema,
77285
+ sensorName: string().optional()
77286
+ });
77287
+ /**
77173
77288
  * List of motion sources active on a camera. Empty array is valid:
77174
77289
  * "no source" — happens for battery cams without firmware motion when
77175
77290
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -77421,6 +77536,14 @@ var RunnerCameraConfigSchema = object({
77421
77536
  */
77422
77537
  motionSources: MotionSourcesSchema.default(["analyzer"]),
77423
77538
  /**
77539
+ * What opened this attach, on a SESSION attach only (see
77540
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
77541
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
77542
+ * behind it, and every attach from an orchestrator older than the field — the
77543
+ * runner then names no source on the phase event.
77544
+ */
77545
+ sessionTrigger: SessionTriggerSchema.optional(),
77546
+ /**
77424
77547
  * WHICH root detector runs for this camera. The detection counterpart of
77425
77548
  * {@link motionSources}, same shape and same discipline: a per-camera list
77426
77549
  * of sources the system understands, vendor-agnostic, owned here.
@@ -77723,7 +77846,22 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
77723
77846
  */
77724
77847
  cropJpeg: string().optional(),
77725
77848
  parent: DetailParentSchema,
77726
- steps: array(string()).optional()
77849
+ steps: array(string()).optional(),
77850
+ /**
77851
+ * The camera's root model already drew this step's boxes (D730): a
77852
+ * `license_plate` inside the parent vehicle. The named step is NOT
77853
+ * inferred — these FRAME-space boxes are its output — and its
77854
+ * requested children (the plate reader) run on them. Absent ⇒ every
77855
+ * requested step infers as before.
77856
+ */
77857
+ supplied: object({
77858
+ stepId: string(),
77859
+ modelId: string(),
77860
+ boxes: array(object({
77861
+ bbox: NativeCropBboxSchema,
77862
+ score: number()
77863
+ })).min(1)
77864
+ }).optional()
77727
77865
  }), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" }), method(object({
77728
77866
  /** Catalog step id, e.g. `clip-embedding`. */
77729
77867
  stepId: string(),
@@ -84834,7 +84972,7 @@ var LocalInterfaceSchema = object({
84834
84972
  name: string(),
84835
84973
  family: _enum(["IPv4", "IPv6"]),
84836
84974
  address: string(),
84837
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
84975
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
84838
84976
  cidr: string(),
84839
84977
  /** OS netmask. Stable across calls so the SDK can hash on it. */
84840
84978
  netmask: string(),
@@ -84888,7 +85026,8 @@ var ConnectionEndpointSchema = object({
84888
85026
  "lan-ipv4",
84889
85027
  "lan-ipv6",
84890
85028
  "public",
84891
- "loopback"
85029
+ "loopback",
85030
+ "app-link"
84892
85031
  ]),
84893
85032
  /**
84894
85033
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -84957,15 +85096,27 @@ var GetConnectionEndpointsResultSchema = object({
84957
85096
  localPort: AdvertisedLocalPortSchema
84958
85097
  });
84959
85098
  /**
84960
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
84961
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
84962
- * what AUTO currently picks, so the UI can show the effective value either way.
85099
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
85100
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
85101
+ * so the UI can show it either way.
84963
85102
  */
84964
85103
  var NotificationEndpointSchema = object({
84965
85104
  /** The operator's explicit choice, or null for AUTO. */
84966
85105
  baseUrl: string().nullable(),
84967
- /** What the ranking currently resolves to (null when nothing is reachable). */
84968
- resolved: string().nullable()
85106
+ /**
85107
+ * The effective flagged address: the explicit choice, else (AUTO) the
85108
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
85109
+ * answered the public tunnel / mesh here, or null.
85110
+ */
85111
+ resolved: string().nullable(),
85112
+ /**
85113
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
85114
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
85115
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
85116
+ * hub advertises neither; the app link is then minted without `hub=`.
85117
+ * Absent on a pre-D709 hub.
85118
+ */
85119
+ appLinkHub: string().nullable()
84969
85120
  });
84970
85121
  /**
84971
85122
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -84978,6 +85129,27 @@ var ViewerEndpointsSchema = object({
84978
85129
  /** What the ranking currently resolves to (may be empty if nothing is up). */
84979
85130
  resolved: array(string()).readonly()
84980
85131
  });
85132
+ /**
85133
+ * One audience's hub address (see `hub-address.ts`): the operator's stored
85134
+ * override, the address in use, and where it came from.
85135
+ */
85136
+ var HubAddressSnapshotSchema = object({
85137
+ /** The operator's override, or null for automatic. */
85138
+ override: string().nullable(),
85139
+ /** The address in use, or null when nothing qualifies (never loopback). */
85140
+ resolved: string().nullable(),
85141
+ source: _enum([
85142
+ "override",
85143
+ "auto",
85144
+ "none"
85145
+ ])
85146
+ });
85147
+ var HubAddressesSchema = object({
85148
+ /** "Hub address for agents" — agents' bundle/model pulls, LAN devices. */
85149
+ cluster: HubAddressSnapshotSchema,
85150
+ /** "Public address for sign-in links" — e-mail links, OIDC, WebAuthn, OAuth2. */
85151
+ browser: HubAddressSnapshotSchema
85152
+ });
84981
85153
  var AllowedAddressesSchema = object({
84982
85154
  /**
84983
85155
  * Allowlist of interface addresses operators have explicitly opted
@@ -85019,8 +85191,18 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
85019
85191
  * Pass `'https'` when the caller is itself loaded over HTTPS
85020
85192
  * to avoid mixed-content blocks in the browser. The public
85021
85193
  * tunnel always emits `https://` regardless. */
85022
- scheme: _enum(["http", "https"]).optional()
85023
- }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
85194
+ scheme: _enum(["http", "https"]).optional(),
85195
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
85196
+ * an address notification links can be flagged with, not a hub
85197
+ * route. Default `false`: racers and rankers never see it. */
85198
+ includeAppLink: boolean().optional()
85199
+ }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), HubAddressesSchema), method(object({
85200
+ audience: _enum(["cluster", "browser"]),
85201
+ baseUrl: string().nullable()
85202
+ }), HubAddressesSchema, {
85203
+ kind: "mutation",
85204
+ auth: "admin"
85205
+ }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
85024
85206
  kind: "mutation",
85025
85207
  auth: "admin"
85026
85208
  }), method(object({
@@ -85348,7 +85530,7 @@ var MeshPeerSchema = object({
85348
85530
  tags: array(string()).readonly(),
85349
85531
  /** True when the peer advertises itself as an exit-node candidate. */
85350
85532
  exitNodeOption: boolean(),
85351
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
85533
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
85352
85534
  advertisedRoutes: array(string()).readonly(),
85353
85535
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
85354
85536
  userLogin: string().nullable(),
@@ -85478,7 +85660,8 @@ var MotionStatusSchema = object({
85478
85660
  * writes this slice (not even a boot seed), and neither does the runner's
85479
85661
  * phase machine: its phase is "a session is attached" for every on-motion
85480
85662
  * session, not motion, and it now has its own `DetectionPhaseTransition`.
85481
- * A provider's `isDetected` answers from the hub's slice.
85663
+ * A provider's `isDetected` and `getStatus` answer from the hub's slice
85664
+ * (`hubMotionNativeProvider`).
85482
85665
  * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
85483
85666
  * imports) — the provider pushes both edges itself, from the upstream
85484
85667
  * system's own state change. That is the sensor's own fact.
@@ -86824,7 +87007,13 @@ var OsdStatusSchema = object({ overlays: array(object({
86824
87007
  */
86825
87008
  availablePositions: array(OsdPositionEnum).readonly(),
86826
87009
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
86827
- readOnly: boolean().optional()
87010
+ readOnly: boolean().optional(),
87011
+ /**
87012
+ * Firmware text ceiling for this overlay. Absent = no known limit.
87013
+ * The OSD manager truncates here on every write — never stores its own
87014
+ * copy on the binding.
87015
+ */
87016
+ maxCharacters: number().int().positive().optional()
86828
87017
  })).readonly() });
86829
87018
  var OsdOverlayPatchSchema = object({
86830
87019
  enabled: boolean().optional(),
@@ -86875,7 +87064,9 @@ var OsdTemplateBindingSchema = object({
86875
87064
  valuePath: string().min(1).max(64),
86876
87065
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
86877
87066
  displayUnit: string().max(16).optional(),
86878
- maxDecimals: number().int().min(0).max(4).default(1)
87067
+ maxDecimals: number().int().min(0).max(4).default(1),
87068
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
87069
+ valueMap: record(string(), string()).optional()
86879
87070
  });
86880
87071
  /**
86881
87072
  * A named template: bindings (alias → leaf field) + a parser that
@@ -86925,18 +87116,11 @@ var OsdSlotBindingSchema = object({
86925
87116
  source: OsdSourceSchema,
86926
87117
  /** `${value}` and `${unit}` are substituted; every occurrence. */
86927
87118
  template: string().max(96).default("${value}"),
86928
- /** Truncate with an ellipsis past this length. Absent = no limit. */
86929
- maxCharacters: number().int().min(4).max(64).optional(),
86930
87119
  /**
86931
87120
  * Decimal places for a numeric value. `0` yields an integer — the
86932
87121
  * documented workaround for firmwares that reject `.` in overlay text.
86933
87122
  */
86934
87123
  maxDecimals: number().int().min(0).max(4).default(1),
86935
- /**
86936
- * Appended via `${unit}` when no unit conversion produces a label.
86937
- * Prefer `source.displayUnit` for converted device-state values.
86938
- */
86939
- unitLabel: string().max(8).optional(),
86940
87124
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
86941
87125
  valueMap: record(string(), string()).optional(),
86942
87126
  /** Time windows in which the slot is shown. Absent = always. */
@@ -86945,9 +87129,7 @@ var OsdSlotBindingSchema = object({
86945
87129
  * Display gate, in the notification centre's condition vocabulary.
86946
87130
  * Only the keys reported by `getConditionSupport` are accepted.
86947
87131
  */
86948
- conditions: NcConditionsSchema.optional(),
86949
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
86950
- fallbackText: string().max(64).default("")
87132
+ conditions: NcConditionsSchema.optional()
86951
87133
  });
86952
87134
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
86953
87135
  var OsdSlotViewSchema = object({
@@ -96644,6 +96826,12 @@ Object.freeze({
96644
96826
  addonId: null,
96645
96827
  access: "view"
96646
96828
  },
96829
+ "localNetwork.getHubAddresses": {
96830
+ capName: "local-network",
96831
+ capScope: "system",
96832
+ addonId: null,
96833
+ access: "view"
96834
+ },
96647
96835
  "localNetwork.getNotificationEndpoint": {
96648
96836
  capName: "local-network",
96649
96837
  capScope: "system",
@@ -96698,6 +96886,12 @@ Object.freeze({
96698
96886
  addonId: null,
96699
96887
  access: "create"
96700
96888
  },
96889
+ "localNetwork.setHubAddress": {
96890
+ capName: "local-network",
96891
+ capScope: "system",
96892
+ addonId: null,
96893
+ access: "create"
96894
+ },
96701
96895
  "localNetwork.setNotificationEndpoint": {
96702
96896
  capName: "local-network",
96703
96897
  capScope: "system",
@@ -105097,6 +105291,8 @@ new Map(Object.entries({
105097
105291
  "detection-blind": "device-events",
105098
105292
  "node-online": "system",
105099
105293
  "node-offline": "system",
105294
+ "node-restarting": "system",
105295
+ "node-updating": "system",
105100
105296
  "node-inference-unavailable": "system",
105101
105297
  "addon-crash-loop": "system",
105102
105298
  "addon-update-available": "system",
@@ -105174,18 +105370,21 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105174
105370
  kinds: [
105175
105371
  "node-offline",
105176
105372
  "node-online",
105373
+ "node-restarting",
105177
105374
  "node-inference-unavailable"
105178
105375
  ]
105179
105376
  },
105180
105377
  {
105181
105378
  id: "addons",
105182
105379
  family: "system",
105183
- label: "Addons",
105184
- kinds: [
105185
- "addon-crash-loop",
105186
- "addon-update-available",
105187
- "addon-updated"
105188
- ]
105380
+ label: "Addons stopped",
105381
+ kinds: ["addon-crash-loop"]
105382
+ },
105383
+ {
105384
+ id: "addon-updates",
105385
+ family: "system",
105386
+ label: "Addon updates",
105387
+ kinds: ["addon-update-available", "addon-updated"]
105189
105388
  },
105190
105389
  {
105191
105390
  id: "updates",
@@ -105193,6 +105392,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105193
105392
  label: "Server & container updates",
105194
105393
  kinds: [
105195
105394
  "server-update-available",
105395
+ "node-updating",
105196
105396
  "server-updated",
105197
105397
  "wrapper-update-available"
105198
105398
  ]
@@ -105340,18 +105540,21 @@ var STATE_SUMMARY_SECTION_VARS = [
105340
105540
  kinds: [
105341
105541
  "node-offline",
105342
105542
  "node-online",
105543
+ "node-restarting",
105343
105544
  "node-inference-unavailable"
105344
105545
  ]
105345
105546
  },
105346
105547
  {
105347
105548
  name: "summaryAddons",
105348
- label: "Summary: addons",
105349
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
105350
- kinds: [
105351
- "addon-crash-loop",
105352
- "addon-update-available",
105353
- "addon-updated"
105354
- ]
105549
+ label: "Summary: addons stopped",
105550
+ example: "hub — recorder stopped after repeated crashes",
105551
+ kinds: ["addon-crash-loop"]
105552
+ },
105553
+ {
105554
+ name: "summaryAddonUpdates",
105555
+ label: "Summary: addon updates",
105556
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
105557
+ kinds: ["addon-update-available", "addon-updated"]
105355
105558
  },
105356
105559
  {
105357
105560
  name: "summaryUpdates",
@@ -105359,6 +105562,7 @@ var STATE_SUMMARY_SECTION_VARS = [
105359
105562
  example: "server 1.2.377 → 1.2.378",
105360
105563
  kinds: [
105361
105564
  "server-update-available",
105565
+ "node-updating",
105362
105566
  "server-updated",
105363
105567
  "wrapper-update-available"
105364
105568
  ]
@@ -105391,7 +105595,7 @@ var STATE_SUMMARY_VARS = [
105391
105595
  {
105392
105596
  name: "summarySection",
105393
105597
  label: "Summary: this section",
105394
- description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
105598
+ description: "The section a per-section summary row is about; on a summary sent as one message, every section it reports, comma-separated.",
105395
105599
  example: "Batterie scariche",
105396
105600
  group: "system",
105397
105601
  families: ["rule"],
@@ -105435,6 +105639,7 @@ var STATE_SUMMARY_VARS = [
105435
105639
  [...RULE_BODY_ONLY],
105436
105640
  [...RULE_TEXT],
105437
105641
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
105642
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
105438
105643
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
105439
105644
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
105440
105645
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -105475,6 +105680,8 @@ var STATE_SUMMARY_VARS = [
105475
105680
  [...RULE_TEXT],
105476
105681
  [...RULE_TEXT],
105477
105682
  [...RULE_TEXT],
105683
+ [...RULE_TEXT],
105684
+ [...RULE_TEXT],
105478
105685
  ...STATE_SUMMARY_VARS
105479
105686
  ];
105480
105687
  new Set([
@@ -108389,6 +108596,18 @@ function nextVacuumControlSlice(prev, next) {
108389
108596
  }
108390
108597
  //#endregion
108391
108598
  //#region src/devices/dreame-camera-stream.ts
108599
+ /**
108600
+ * The robot camera as a stream the broker can pull — the pure facts.
108601
+ *
108602
+ * A Dreame relay carries video only between `open()` and `close()`, and the
108603
+ * robot runs on a battery, so the camera cannot sit in the catalog with a
108604
+ * URL the way a mains RTSP camera does. The seam is the broker's own battery
108605
+ * path: `battery.wakeForStream` opens the monitor, and the catalog then
108606
+ * names the local RFC 4571 server (`dreame-rfc4571-server.ts`) the broker
108607
+ * pulls. While the monitor is CLOSED the
108608
+ * catalog carries the broker's lazy sentinel (Reolink's shape): the stream is
108609
+ * listed, and the first dial asks the publisher to materialize it.
108610
+ */
108392
108611
  /** The one stream the robot camera offers. Half of the broker id (`<deviceId>/live`). */
108393
108612
  var CAMERA_STREAM_ID = "live";
108394
108613
  /** The plugin's cap: a monitor never outlives this, whoever is watching. */
@@ -108468,17 +108687,13 @@ function cameraStreamCatalog(session) {
108468
108687
  /**
108469
108688
  * The host the broker is told to dial. The provider and the ingest owner
108470
108689
  * share the hub today, but a broker on another node reaches the hub only
108471
- * through its public address — which is what `CAMSTACK_HUB_PUBLIC_URL` is
108472
- * for. Loopback when nothing better is known.
108690
+ * through the hub's address for agents — `local-network.getHubAddresses()
108691
+ * .cluster` (the Network page's override, else the detected LAN address).
108692
+ * Loopback when that names nothing: then the only broker that can dial at
108693
+ * all is this node's own.
108473
108694
  */
108474
- function advertisedRfcHost(hubPublicUrl) {
108475
- if (hubPublicUrl === void 0 || hubPublicUrl.length === 0) return "127.0.0.1";
108476
- try {
108477
- const host = new URL(hubPublicUrl).hostname;
108478
- return host.length > 0 ? host : "127.0.0.1";
108479
- } catch {
108480
- return "127.0.0.1";
108481
- }
108695
+ function advertisedRfcHost(clusterBaseUrl) {
108696
+ return hubAddressHostname(clusterBaseUrl) ?? "127.0.0.1";
108482
108697
  }
108483
108698
  function rfcUrl(host, port) {
108484
108699
  return `tcp://${host}:${String(port)}`;
@@ -154868,6 +155083,23 @@ var DreameCameraDevice = class extends DreameChildDevice {
154868
155083
  }
154869
155084
  unsubscribeRefresh = null;
154870
155085
  /** Register the stream on the broker — the server, or the lazy sentinel (push semantics; the catalog is the durable copy). */
155086
+ /**
155087
+ * The hub's address for agents (`local-network.getHubAddresses().cluster`:
155088
+ * the Network page's override, else the detected LAN address). A failed
155089
+ * read is `null` — `advertisedRfcHost` then answers loopback, which this
155090
+ * node's own broker can still dial.
155091
+ */
155092
+ async hubClusterAddress() {
155093
+ try {
155094
+ return (await this.ctx.api.localNetwork.getHubAddresses.query()).cluster.resolved;
155095
+ } catch (err) {
155096
+ this.ctx.logger.warn("robot camera: hub address unavailable — advertising loopback", {
155097
+ tags: { deviceId: this.id },
155098
+ meta: { error: err instanceof Error ? err.message : String(err) }
155099
+ });
155100
+ return null;
155101
+ }
155102
+ }
154871
155103
  async publishRelay(session) {
154872
155104
  const [descriptor] = cameraStreamCatalog(session);
154873
155105
  if (descriptor === void 0) return;
@@ -154953,7 +155185,7 @@ var DreameCameraDevice = class extends DreameChildDevice {
154953
155185
  rfc
154954
155186
  };
154955
155187
  this.session = {
154956
- url: rfcUrl(advertisedRfcHost(process.env.CAMSTACK_HUB_PUBLIC_URL), rfc.port),
155188
+ url: rfcUrl(advertisedRfcHost(await this.hubClusterAddress()), rfc.port),
154957
155189
  sdp: rfc.sdp
154958
155190
  };
154959
155191
  this.sessionOpenedAt = Date.now();