@camstack/addon-provider-dreame 0.2.123 → 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 +203 -17
  2. package/dist/addon.mjs +203 -17
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -63368,6 +63368,15 @@ var ClipSearchStrictnessSchema = _enum([
63368
63368
  "balanced",
63369
63369
  "strict"
63370
63370
  ]);
63371
+ /** Steps whose effect a root label can BE. */
63372
+ var LABEL_SUPPLY_STEP_IDS = [
63373
+ "animal-classifier",
63374
+ "vehicle-classifier",
63375
+ "package-detection"
63376
+ ];
63377
+ /** Steps whose box a root detection can BE. */
63378
+ var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
63379
+ [...LABEL_SUPPLY_STEP_IDS, ...CROP_SUPPLY_STEP_IDS];
63371
63380
  var MODEL_FORMATS = [
63372
63381
  "onnx",
63373
63382
  "coreml",
@@ -63500,6 +63509,35 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
63500
63509
  /** True when the weights or their data forbid commercial use. */
63501
63510
  nonCommercial: boolean().optional()
63502
63511
  }));
63512
+ /**
63513
+ * One step a ROOT detector can supply from its own output (D730) — see
63514
+ * `ModelCatalogEntry.suppliesSteps`.
63515
+ *
63516
+ * - `label`: the root's raw class IS the step's answer. `labelToEffect` maps
63517
+ * the root's label to the step's vocabulary; `uncovered` is the vocabulary
63518
+ * the root cannot produce (shown as unavailable, never a blocker).
63519
+ * - `crop`: the root draws the box the step would have found (`license_plate`
63520
+ * for plate-detection, `face` for face-detection). The box becomes a detail
63521
+ * child of the `parentMacro` detection that contains it — never a track.
63522
+ */
63523
+ var RootStepSupplySchema = discriminatedUnion("kind", [object({
63524
+ kind: literal("label"),
63525
+ stepId: _enum(LABEL_SUPPLY_STEP_IDS),
63526
+ labelToEffect: record(string(), string()),
63527
+ uncovered: array(string()).readonly()
63528
+ }), object({
63529
+ kind: literal("crop"),
63530
+ stepId: _enum(CROP_SUPPLY_STEP_IDS),
63531
+ rootLabels: array(string()).readonly(),
63532
+ parentMacro: _enum(["vehicle", "person"]),
63533
+ childClass: _enum(["plate", "face"]),
63534
+ /**
63535
+ * The root's smaller input side, stamped on read by `rootSuppliesOf` from
63536
+ * the model entry — what a step's `minRootInputPx` gate judges. Never
63537
+ * stored by an import.
63538
+ */
63539
+ rootInputPx: number().int().positive().optional()
63540
+ })]);
63503
63541
  var ModelCatalogEntrySchema = object({
63504
63542
  id: string(),
63505
63543
  name: string(),
@@ -63626,6 +63664,16 @@ var ModelCatalogEntrySchema = object({
63626
63664
  */
63627
63665
  classMap: DetectionCatalogClassMapSchema.optional(),
63628
63666
  /**
63667
+ * The pipeline steps this ROOT detector can supply from its own output
63668
+ * (D730): its labels name a step's effect (`deer` for `animal-classifier`)
63669
+ * or draw a step's box (`license_plate` for `plate-detection`). Computed at
63670
+ * import from THIS model's labelmap by `deriveRootSupplies`, never written
63671
+ * by hand and never assumed per family; a row without it is derived on read
63672
+ * (`rootSuppliesOf`). Whether a camera uses it is the step's own
63673
+ * `useRootModel` flag (`resolveRootSupply`).
63674
+ */
63675
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional(),
63676
+ /**
63629
63677
  * The model's CLIP feature-space contract (text encoder, dimension, vector
63630
63678
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
63631
63679
  * CLIP image encoder; its absence is what "not a CLIP model" means to the
@@ -69091,6 +69139,18 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
69091
69139
  kind: "mutation",
69092
69140
  auth: "admin"
69093
69141
  });
69142
+ function hostnameOf(baseUrl) {
69143
+ try {
69144
+ const host = new URL(baseUrl).hostname;
69145
+ return host.length > 0 ? host : null;
69146
+ } catch {
69147
+ return null;
69148
+ }
69149
+ }
69150
+ /** The bare hostname of a base URL, or `null` when it does not parse. */
69151
+ function hubAddressHostname(baseUrl) {
69152
+ return baseUrl === null ? null : hostnameOf(baseUrl);
69153
+ }
69094
69154
  /**
69095
69155
  * Transport bounds for AI capability methods.
69096
69156
  *
@@ -71693,6 +71753,8 @@ var NcSystemEventKindSchema = _enum([
71693
71753
  "stream-offline",
71694
71754
  "node-online",
71695
71755
  "node-offline",
71756
+ "node-restarting",
71757
+ "node-updating",
71696
71758
  "node-inference-unavailable",
71697
71759
  "detection-blind",
71698
71760
  "addon-crash-loop",
@@ -76210,6 +76272,34 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
76210
76272
  timestamp: number()
76211
76273
  });
76212
76274
  /**
76275
+ * A root model's boxes standing in for one crop-kind step's own inference
76276
+ * (D730) — see `runPipeline.suppliedRoot`.
76277
+ */
76278
+ var SuppliedRootOutputSchema = object({
76279
+ stepId: string(),
76280
+ /** The root model that drew the boxes — stamped on the result. */
76281
+ modelId: string(),
76282
+ detections: array(object({
76283
+ /** `[x1, y1, x2, y2]` in the pixels of the call's image. */
76284
+ bbox: tuple([
76285
+ number(),
76286
+ number(),
76287
+ number(),
76288
+ number()
76289
+ ]),
76290
+ score: number(),
76291
+ /**
76292
+ * Five facial landmarks in the same pixels — a face the step's own
76293
+ * detector already re-found inside a root face box (D730), so its
76294
+ * embedder child aligns it exactly as it aligns any detected face.
76295
+ */
76296
+ landmarks: array(object({
76297
+ x: number(),
76298
+ y: number()
76299
+ })).optional()
76300
+ })).min(1)
76301
+ });
76302
+ /**
76213
76303
  * Reference to the frame's retained NATIVE surface + the parent crop's placement
76214
76304
  * within the frame, so the executor can re-cut a leaf child ROI at native
76215
76305
  * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
@@ -76339,7 +76429,8 @@ var PipelineModelOptionSchema = object({
76339
76429
  group: ModelVariantGroupSchema.optional(),
76340
76430
  legacy: boolean().optional(),
76341
76431
  provider: ModelProviderIdSchema.optional(),
76342
- defaultConfidence: number().optional()
76432
+ defaultConfidence: number().optional(),
76433
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional()
76343
76434
  });
76344
76435
  var ConfigFieldBridge = custom();
76345
76436
  var PipelineAddonSchemaSchema = object({
@@ -76607,7 +76698,16 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
76607
76698
  * `device-not-servable`), and nothing reaches the camera's live
76608
76699
  * trace stream. Absent ⇒ unchanged behaviour.
76609
76700
  */
76610
- replay: literal(true).optional()
76701
+ replay: literal(true).optional(),
76702
+ /**
76703
+ * Root-supplied detections for the tree's ROOT step (D730). Set by
76704
+ * `runDetailSubtree` when the camera's root model supplies a crop-kind
76705
+ * step (a Frigate+ `license_plate` for `plate-detection`): the step is
76706
+ * NOT inferred, these boxes are its output, and its children (the plate
76707
+ * reader) run on them through their own crop policy. Boxes are in the
76708
+ * PIXELS of the image this call carries. Absent ⇒ the root step runs.
76709
+ */
76710
+ suppliedRoot: SuppliedRootOutputSchema.optional()
76611
76711
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
76612
76712
  steps: array(PipelineStepInputSchema).min(1),
76613
76713
  frames: array(FrameInputSchema).min(1).max(255),
@@ -77749,7 +77849,22 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
77749
77849
  */
77750
77850
  cropJpeg: string().optional(),
77751
77851
  parent: DetailParentSchema,
77752
- steps: array(string()).optional()
77852
+ steps: array(string()).optional(),
77853
+ /**
77854
+ * The camera's root model already drew this step's boxes (D730): a
77855
+ * `license_plate` inside the parent vehicle. The named step is NOT
77856
+ * inferred — these FRAME-space boxes are its output — and its
77857
+ * requested children (the plate reader) run on them. Absent ⇒ every
77858
+ * requested step infers as before.
77859
+ */
77860
+ supplied: object({
77861
+ stepId: string(),
77862
+ modelId: string(),
77863
+ boxes: array(object({
77864
+ bbox: NativeCropBboxSchema,
77865
+ score: number()
77866
+ })).min(1)
77867
+ }).optional()
77753
77868
  }), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" }), method(object({
77754
77869
  /** Catalog step id, e.g. `clip-embedding`. */
77755
77870
  stepId: string(),
@@ -85017,6 +85132,27 @@ var ViewerEndpointsSchema = object({
85017
85132
  /** What the ranking currently resolves to (may be empty if nothing is up). */
85018
85133
  resolved: array(string()).readonly()
85019
85134
  });
85135
+ /**
85136
+ * One audience's hub address (see `hub-address.ts`): the operator's stored
85137
+ * override, the address in use, and where it came from.
85138
+ */
85139
+ var HubAddressSnapshotSchema = object({
85140
+ /** The operator's override, or null for automatic. */
85141
+ override: string().nullable(),
85142
+ /** The address in use, or null when nothing qualifies (never loopback). */
85143
+ resolved: string().nullable(),
85144
+ source: _enum([
85145
+ "override",
85146
+ "auto",
85147
+ "none"
85148
+ ])
85149
+ });
85150
+ var HubAddressesSchema = object({
85151
+ /** "Hub address for agents" — agents' bundle/model pulls, LAN devices. */
85152
+ cluster: HubAddressSnapshotSchema,
85153
+ /** "Public address for sign-in links" — e-mail links, OIDC, WebAuthn, OAuth2. */
85154
+ browser: HubAddressSnapshotSchema
85155
+ });
85020
85156
  var AllowedAddressesSchema = object({
85021
85157
  /**
85022
85158
  * Allowlist of interface addresses operators have explicitly opted
@@ -85063,7 +85199,13 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
85063
85199
  * an address notification links can be flagged with, not a hub
85064
85200
  * route. Default `false`: racers and rankers never see it. */
85065
85201
  includeAppLink: boolean().optional()
85066
- }), 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, {
85202
+ }), 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({
85203
+ audience: _enum(["cluster", "browser"]),
85204
+ baseUrl: string().nullable()
85205
+ }), HubAddressesSchema, {
85206
+ kind: "mutation",
85207
+ auth: "admin"
85208
+ }), 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, {
85067
85209
  kind: "mutation",
85068
85210
  auth: "admin"
85069
85211
  }), method(object({
@@ -85521,7 +85663,8 @@ var MotionStatusSchema = object({
85521
85663
  * writes this slice (not even a boot seed), and neither does the runner's
85522
85664
  * phase machine: its phase is "a session is attached" for every on-motion
85523
85665
  * session, not motion, and it now has its own `DetectionPhaseTransition`.
85524
- * A provider's `isDetected` answers from the hub's slice.
85666
+ * A provider's `isDetected` and `getStatus` answer from the hub's slice
85667
+ * (`hubMotionNativeProvider`).
85525
85668
  * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
85526
85669
  * imports) — the provider pushes both edges itself, from the upstream
85527
85670
  * system's own state change. That is the sensor's own fact.
@@ -96686,6 +96829,12 @@ Object.freeze({
96686
96829
  addonId: null,
96687
96830
  access: "view"
96688
96831
  },
96832
+ "localNetwork.getHubAddresses": {
96833
+ capName: "local-network",
96834
+ capScope: "system",
96835
+ addonId: null,
96836
+ access: "view"
96837
+ },
96689
96838
  "localNetwork.getNotificationEndpoint": {
96690
96839
  capName: "local-network",
96691
96840
  capScope: "system",
@@ -96740,6 +96889,12 @@ Object.freeze({
96740
96889
  addonId: null,
96741
96890
  access: "create"
96742
96891
  },
96892
+ "localNetwork.setHubAddress": {
96893
+ capName: "local-network",
96894
+ capScope: "system",
96895
+ addonId: null,
96896
+ access: "create"
96897
+ },
96743
96898
  "localNetwork.setNotificationEndpoint": {
96744
96899
  capName: "local-network",
96745
96900
  capScope: "system",
@@ -105139,6 +105294,8 @@ new Map(Object.entries({
105139
105294
  "detection-blind": "device-events",
105140
105295
  "node-online": "system",
105141
105296
  "node-offline": "system",
105297
+ "node-restarting": "system",
105298
+ "node-updating": "system",
105142
105299
  "node-inference-unavailable": "system",
105143
105300
  "addon-crash-loop": "system",
105144
105301
  "addon-update-available": "system",
@@ -105216,6 +105373,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105216
105373
  kinds: [
105217
105374
  "node-offline",
105218
105375
  "node-online",
105376
+ "node-restarting",
105219
105377
  "node-inference-unavailable"
105220
105378
  ]
105221
105379
  },
@@ -105237,6 +105395,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105237
105395
  label: "Server & container updates",
105238
105396
  kinds: [
105239
105397
  "server-update-available",
105398
+ "node-updating",
105240
105399
  "server-updated",
105241
105400
  "wrapper-update-available"
105242
105401
  ]
@@ -105384,6 +105543,7 @@ var STATE_SUMMARY_SECTION_VARS = [
105384
105543
  kinds: [
105385
105544
  "node-offline",
105386
105545
  "node-online",
105546
+ "node-restarting",
105387
105547
  "node-inference-unavailable"
105388
105548
  ]
105389
105549
  },
@@ -105405,6 +105565,7 @@ var STATE_SUMMARY_SECTION_VARS = [
105405
105565
  example: "server 1.2.377 → 1.2.378",
105406
105566
  kinds: [
105407
105567
  "server-update-available",
105568
+ "node-updating",
105408
105569
  "server-updated",
105409
105570
  "wrapper-update-available"
105410
105571
  ]
@@ -105437,7 +105598,7 @@ var STATE_SUMMARY_VARS = [
105437
105598
  {
105438
105599
  name: "summarySection",
105439
105600
  label: "Summary: this section",
105440
- description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
105601
+ description: "The section a per-section summary row is about; on a summary sent as one message, every section it reports, comma-separated.",
105441
105602
  example: "Batterie scariche",
105442
105603
  group: "system",
105443
105604
  families: ["rule"],
@@ -108438,6 +108599,18 @@ function nextVacuumControlSlice(prev, next) {
108438
108599
  }
108439
108600
  //#endregion
108440
108601
  //#region src/devices/dreame-camera-stream.ts
108602
+ /**
108603
+ * The robot camera as a stream the broker can pull — the pure facts.
108604
+ *
108605
+ * A Dreame relay carries video only between `open()` and `close()`, and the
108606
+ * robot runs on a battery, so the camera cannot sit in the catalog with a
108607
+ * URL the way a mains RTSP camera does. The seam is the broker's own battery
108608
+ * path: `battery.wakeForStream` opens the monitor, and the catalog then
108609
+ * names the local RFC 4571 server (`dreame-rfc4571-server.ts`) the broker
108610
+ * pulls. While the monitor is CLOSED the
108611
+ * catalog carries the broker's lazy sentinel (Reolink's shape): the stream is
108612
+ * listed, and the first dial asks the publisher to materialize it.
108613
+ */
108441
108614
  /** The one stream the robot camera offers. Half of the broker id (`<deviceId>/live`). */
108442
108615
  var CAMERA_STREAM_ID = "live";
108443
108616
  /** The plugin's cap: a monitor never outlives this, whoever is watching. */
@@ -108517,17 +108690,13 @@ function cameraStreamCatalog(session) {
108517
108690
  /**
108518
108691
  * The host the broker is told to dial. The provider and the ingest owner
108519
108692
  * share the hub today, but a broker on another node reaches the hub only
108520
- * through its public address — which is what `CAMSTACK_HUB_PUBLIC_URL` is
108521
- * for. Loopback when nothing better is known.
108693
+ * through the hub's address for agents — `local-network.getHubAddresses()
108694
+ * .cluster` (the Network page's override, else the detected LAN address).
108695
+ * Loopback when that names nothing: then the only broker that can dial at
108696
+ * all is this node's own.
108522
108697
  */
108523
- function advertisedRfcHost(hubPublicUrl) {
108524
- if (hubPublicUrl === void 0 || hubPublicUrl.length === 0) return "127.0.0.1";
108525
- try {
108526
- const host = new URL(hubPublicUrl).hostname;
108527
- return host.length > 0 ? host : "127.0.0.1";
108528
- } catch {
108529
- return "127.0.0.1";
108530
- }
108698
+ function advertisedRfcHost(clusterBaseUrl) {
108699
+ return hubAddressHostname(clusterBaseUrl) ?? "127.0.0.1";
108531
108700
  }
108532
108701
  function rfcUrl(host, port) {
108533
108702
  return `tcp://${host}:${String(port)}`;
@@ -154917,6 +155086,23 @@ var DreameCameraDevice = class extends DreameChildDevice {
154917
155086
  }
154918
155087
  unsubscribeRefresh = null;
154919
155088
  /** Register the stream on the broker — the server, or the lazy sentinel (push semantics; the catalog is the durable copy). */
155089
+ /**
155090
+ * The hub's address for agents (`local-network.getHubAddresses().cluster`:
155091
+ * the Network page's override, else the detected LAN address). A failed
155092
+ * read is `null` — `advertisedRfcHost` then answers loopback, which this
155093
+ * node's own broker can still dial.
155094
+ */
155095
+ async hubClusterAddress() {
155096
+ try {
155097
+ return (await this.ctx.api.localNetwork.getHubAddresses.query()).cluster.resolved;
155098
+ } catch (err) {
155099
+ this.ctx.logger.warn("robot camera: hub address unavailable — advertising loopback", {
155100
+ tags: { deviceId: this.id },
155101
+ meta: { error: err instanceof Error ? err.message : String(err) }
155102
+ });
155103
+ return null;
155104
+ }
155105
+ }
154920
155106
  async publishRelay(session) {
154921
155107
  const [descriptor] = cameraStreamCatalog(session);
154922
155108
  if (descriptor === void 0) return;
@@ -155002,7 +155188,7 @@ var DreameCameraDevice = class extends DreameChildDevice {
155002
155188
  rfc
155003
155189
  };
155004
155190
  this.session = {
155005
- url: rfcUrl(advertisedRfcHost(process.env.CAMSTACK_HUB_PUBLIC_URL), rfc.port),
155191
+ url: rfcUrl(advertisedRfcHost(await this.hubClusterAddress()), rfc.port),
155006
155192
  sdp: rfc.sdp
155007
155193
  };
155008
155194
  this.sessionOpenedAt = Date.now();
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",
@@ -63497,6 +63506,35 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
63497
63506
  /** True when the weights or their data forbid commercial use. */
63498
63507
  nonCommercial: boolean().optional()
63499
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
+ })]);
63500
63538
  var ModelCatalogEntrySchema = object({
63501
63539
  id: string(),
63502
63540
  name: string(),
@@ -63623,6 +63661,16 @@ var ModelCatalogEntrySchema = object({
63623
63661
  */
63624
63662
  classMap: DetectionCatalogClassMapSchema.optional(),
63625
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
+ /**
63626
63674
  * The model's CLIP feature-space contract (text encoder, dimension, vector
63627
63675
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
63628
63676
  * CLIP image encoder; its absence is what "not a CLIP model" means to the
@@ -69088,6 +69136,18 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
69088
69136
  kind: "mutation",
69089
69137
  auth: "admin"
69090
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
+ }
69091
69151
  /**
69092
69152
  * Transport bounds for AI capability methods.
69093
69153
  *
@@ -71690,6 +71750,8 @@ var NcSystemEventKindSchema = _enum([
71690
71750
  "stream-offline",
71691
71751
  "node-online",
71692
71752
  "node-offline",
71753
+ "node-restarting",
71754
+ "node-updating",
71693
71755
  "node-inference-unavailable",
71694
71756
  "detection-blind",
71695
71757
  "addon-crash-loop",
@@ -76207,6 +76269,34 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
76207
76269
  timestamp: number()
76208
76270
  });
76209
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
+ /**
76210
76300
  * Reference to the frame's retained NATIVE surface + the parent crop's placement
76211
76301
  * within the frame, so the executor can re-cut a leaf child ROI at native
76212
76302
  * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
@@ -76336,7 +76426,8 @@ var PipelineModelOptionSchema = object({
76336
76426
  group: ModelVariantGroupSchema.optional(),
76337
76427
  legacy: boolean().optional(),
76338
76428
  provider: ModelProviderIdSchema.optional(),
76339
- defaultConfidence: number().optional()
76429
+ defaultConfidence: number().optional(),
76430
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional()
76340
76431
  });
76341
76432
  var ConfigFieldBridge = custom();
76342
76433
  var PipelineAddonSchemaSchema = object({
@@ -76604,7 +76695,16 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
76604
76695
  * `device-not-servable`), and nothing reaches the camera's live
76605
76696
  * trace stream. Absent ⇒ unchanged behaviour.
76606
76697
  */
76607
- 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()
76608
76708
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
76609
76709
  steps: array(PipelineStepInputSchema).min(1),
76610
76710
  frames: array(FrameInputSchema).min(1).max(255),
@@ -77746,7 +77846,22 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
77746
77846
  */
77747
77847
  cropJpeg: string().optional(),
77748
77848
  parent: DetailParentSchema,
77749
- 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()
77750
77865
  }), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" }), method(object({
77751
77866
  /** Catalog step id, e.g. `clip-embedding`. */
77752
77867
  stepId: string(),
@@ -85014,6 +85129,27 @@ var ViewerEndpointsSchema = object({
85014
85129
  /** What the ranking currently resolves to (may be empty if nothing is up). */
85015
85130
  resolved: array(string()).readonly()
85016
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
+ });
85017
85153
  var AllowedAddressesSchema = object({
85018
85154
  /**
85019
85155
  * Allowlist of interface addresses operators have explicitly opted
@@ -85060,7 +85196,13 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
85060
85196
  * an address notification links can be flagged with, not a hub
85061
85197
  * route. Default `false`: racers and rankers never see it. */
85062
85198
  includeAppLink: boolean().optional()
85063
- }), 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, {
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, {
85064
85206
  kind: "mutation",
85065
85207
  auth: "admin"
85066
85208
  }), method(object({
@@ -85518,7 +85660,8 @@ var MotionStatusSchema = object({
85518
85660
  * writes this slice (not even a boot seed), and neither does the runner's
85519
85661
  * phase machine: its phase is "a session is attached" for every on-motion
85520
85662
  * session, not motion, and it now has its own `DetectionPhaseTransition`.
85521
- * 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`).
85522
85665
  * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
85523
85666
  * imports) — the provider pushes both edges itself, from the upstream
85524
85667
  * system's own state change. That is the sensor's own fact.
@@ -96683,6 +96826,12 @@ Object.freeze({
96683
96826
  addonId: null,
96684
96827
  access: "view"
96685
96828
  },
96829
+ "localNetwork.getHubAddresses": {
96830
+ capName: "local-network",
96831
+ capScope: "system",
96832
+ addonId: null,
96833
+ access: "view"
96834
+ },
96686
96835
  "localNetwork.getNotificationEndpoint": {
96687
96836
  capName: "local-network",
96688
96837
  capScope: "system",
@@ -96737,6 +96886,12 @@ Object.freeze({
96737
96886
  addonId: null,
96738
96887
  access: "create"
96739
96888
  },
96889
+ "localNetwork.setHubAddress": {
96890
+ capName: "local-network",
96891
+ capScope: "system",
96892
+ addonId: null,
96893
+ access: "create"
96894
+ },
96740
96895
  "localNetwork.setNotificationEndpoint": {
96741
96896
  capName: "local-network",
96742
96897
  capScope: "system",
@@ -105136,6 +105291,8 @@ new Map(Object.entries({
105136
105291
  "detection-blind": "device-events",
105137
105292
  "node-online": "system",
105138
105293
  "node-offline": "system",
105294
+ "node-restarting": "system",
105295
+ "node-updating": "system",
105139
105296
  "node-inference-unavailable": "system",
105140
105297
  "addon-crash-loop": "system",
105141
105298
  "addon-update-available": "system",
@@ -105213,6 +105370,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105213
105370
  kinds: [
105214
105371
  "node-offline",
105215
105372
  "node-online",
105373
+ "node-restarting",
105216
105374
  "node-inference-unavailable"
105217
105375
  ]
105218
105376
  },
@@ -105234,6 +105392,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105234
105392
  label: "Server & container updates",
105235
105393
  kinds: [
105236
105394
  "server-update-available",
105395
+ "node-updating",
105237
105396
  "server-updated",
105238
105397
  "wrapper-update-available"
105239
105398
  ]
@@ -105381,6 +105540,7 @@ var STATE_SUMMARY_SECTION_VARS = [
105381
105540
  kinds: [
105382
105541
  "node-offline",
105383
105542
  "node-online",
105543
+ "node-restarting",
105384
105544
  "node-inference-unavailable"
105385
105545
  ]
105386
105546
  },
@@ -105402,6 +105562,7 @@ var STATE_SUMMARY_SECTION_VARS = [
105402
105562
  example: "server 1.2.377 → 1.2.378",
105403
105563
  kinds: [
105404
105564
  "server-update-available",
105565
+ "node-updating",
105405
105566
  "server-updated",
105406
105567
  "wrapper-update-available"
105407
105568
  ]
@@ -105434,7 +105595,7 @@ var STATE_SUMMARY_VARS = [
105434
105595
  {
105435
105596
  name: "summarySection",
105436
105597
  label: "Summary: this section",
105437
- 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.",
105438
105599
  example: "Batterie scariche",
105439
105600
  group: "system",
105440
105601
  families: ["rule"],
@@ -108435,6 +108596,18 @@ function nextVacuumControlSlice(prev, next) {
108435
108596
  }
108436
108597
  //#endregion
108437
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
+ */
108438
108611
  /** The one stream the robot camera offers. Half of the broker id (`<deviceId>/live`). */
108439
108612
  var CAMERA_STREAM_ID = "live";
108440
108613
  /** The plugin's cap: a monitor never outlives this, whoever is watching. */
@@ -108514,17 +108687,13 @@ function cameraStreamCatalog(session) {
108514
108687
  /**
108515
108688
  * The host the broker is told to dial. The provider and the ingest owner
108516
108689
  * share the hub today, but a broker on another node reaches the hub only
108517
- * through its public address — which is what `CAMSTACK_HUB_PUBLIC_URL` is
108518
- * 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.
108519
108694
  */
108520
- function advertisedRfcHost(hubPublicUrl) {
108521
- if (hubPublicUrl === void 0 || hubPublicUrl.length === 0) return "127.0.0.1";
108522
- try {
108523
- const host = new URL(hubPublicUrl).hostname;
108524
- return host.length > 0 ? host : "127.0.0.1";
108525
- } catch {
108526
- return "127.0.0.1";
108527
- }
108695
+ function advertisedRfcHost(clusterBaseUrl) {
108696
+ return hubAddressHostname(clusterBaseUrl) ?? "127.0.0.1";
108528
108697
  }
108529
108698
  function rfcUrl(host, port) {
108530
108699
  return `tcp://${host}:${String(port)}`;
@@ -154914,6 +155083,23 @@ var DreameCameraDevice = class extends DreameChildDevice {
154914
155083
  }
154915
155084
  unsubscribeRefresh = null;
154916
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
+ }
154917
155103
  async publishRelay(session) {
154918
155104
  const [descriptor] = cameraStreamCatalog(session);
154919
155105
  if (descriptor === void 0) return;
@@ -154999,7 +155185,7 @@ var DreameCameraDevice = class extends DreameChildDevice {
154999
155185
  rfc
155000
155186
  };
155001
155187
  this.session = {
155002
- url: rfcUrl(advertisedRfcHost(process.env.CAMSTACK_HUB_PUBLIC_URL), rfc.port),
155188
+ url: rfcUrl(advertisedRfcHost(await this.hubClusterAddress()), rfc.port),
155003
155189
  sdp: rfc.sdp
155004
155190
  };
155005
155191
  this.sessionOpenedAt = Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreame",
3
- "version": "0.2.123",
3
+ "version": "0.2.124",
4
4
  "description": "Dreame robot-vacuum / lawn-mower device-provider addon for CamStack — wraps the @apocaliss92/nodedreame Dreamehome cloud client",
5
5
  "keywords": [
6
6
  "camstack",