@camstack/addon-provider-bosch 0.1.48 → 0.1.50

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.
@@ -802,7 +802,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
802
802
  return EventCategory;
803
803
  }({});
804
804
  //#endregion
805
- //#region ../types/dist/composed-stable-id-jnvhwGoH.mjs
805
+ //#region ../types/dist/composed-stable-id-DFNyOFzz.mjs
806
806
  /**
807
807
  import { errMsg } from '@camstack/types'
808
808
  * Extract a human-readable message from an unknown error value.
@@ -815,6 +815,7 @@ function errMsg(err) {
815
815
  }
816
816
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
817
817
  DeviceType["Camera"] = "camera";
818
+ /** Legacy stored value, read only by the hub-to-integration migrations; no live device carries it. */
818
819
  DeviceType["Hub"] = "hub";
819
820
  DeviceType["Light"] = "light";
820
821
  DeviceType["Siren"] = "siren";
@@ -6026,7 +6027,7 @@ var ZodIssueCode = {
6026
6027
  var ZodFirstPartyTypeKind;
6027
6028
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6028
6029
  //#endregion
6029
- //#region ../types/dist/sleep-Dz1P6--1.mjs
6030
+ //#region ../types/dist/sleep-D_NpiLFP.mjs
6030
6031
  /**
6031
6032
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6032
6033
  * window to float samples (D455).
@@ -13892,10 +13893,6 @@ var CAMERA_CAP_VOCABULARY = {
13892
13893
  scope: "device",
13893
13894
  noun: t("Admin link", "Link di amministrazione")
13894
13895
  },
13895
- "device-discovery": {
13896
- scope: "device",
13897
- noun: t("Discovery", "Rilevamento dispositivi")
13898
- },
13899
13896
  "device-extension": {
13900
13897
  scope: "device",
13901
13898
  noun: t("Device extension", "Estensione del dispositivo")
@@ -15067,6 +15064,7 @@ var SYSTEM_CAP_VOCABULARY = {
15067
15064
  backup: system("system backups — run and restored from the Backups page"),
15068
15065
  broker: system("message-broker connections — configured, not actuated"),
15069
15066
  "calendar-source": system(INTERNAL),
15067
+ "cluster-update": system(CLUSTER),
15070
15068
  calendars: system("calendars feed schedules and conditions, not device actions"),
15071
15069
  composer: system("device composition — an authoring tool, not an action"),
15072
15070
  "core-blocks": system(INTERNAL),
@@ -15356,6 +15354,7 @@ var CAP_NAMES = Object.freeze([
15356
15354
  "camera-streams",
15357
15355
  "carbon-monoxide",
15358
15356
  "climate-control",
15357
+ "cluster-update",
15359
15358
  "color",
15360
15359
  "composer",
15361
15360
  "connectivity",
@@ -15373,7 +15372,6 @@ var CAP_NAMES = Object.freeze([
15373
15372
  "device-admin-link",
15374
15373
  "device-adoption",
15375
15374
  "device-credentials",
15376
- "device-discovery",
15377
15375
  "device-export",
15378
15376
  "device-extension",
15379
15377
  "device-history",
@@ -15995,13 +15993,6 @@ Object.freeze({
15995
15993
  "valueType": "string"
15996
15994
  }
15997
15995
  ],
15998
- "device-discovery": [{
15999
- "path": "lastDiscoveryAt",
16000
- "valueType": "number"
16001
- }, {
16002
- "path": "lastError",
16003
- "valueType": "string"
16004
- }],
16005
15996
  "device-export": [
16006
15997
  {
16007
15998
  "path": "linkState",
@@ -17453,7 +17444,7 @@ Object.freeze({
17453
17444
  }
17454
17445
  });
17455
17446
  //#endregion
17456
- //#region ../types/dist/implemented-methods-OitwyO1Y.mjs
17447
+ //#region ../types/dist/implemented-methods-M5aguGon.mjs
17457
17448
  /**
17458
17449
  * The ffmpeg build every CamStack node downloads, per platform and arch.
17459
17450
  *
@@ -44093,6 +44084,62 @@ var WeekdayKeySchema = _enum([
44093
44084
  "sat",
44094
44085
  "sun"
44095
44086
  ]);
44087
+ /**
44088
+ * Hold semantics — "for N seconds" on a leaf of the `when` tree (#498).
44089
+ *
44090
+ * ONE primitive, `for: { ms, mode, tolerance? }`, shared by `device-becomes`,
44091
+ * `device-is` and `expression`:
44092
+ *
44093
+ * - `held`: the condition stays true for `ms`;
44094
+ * - `stable`: the value does not move by more than `tolerance` (absolute, in
44095
+ * the field's base unit) for `ms` while the condition holds.
44096
+ *
44097
+ * {@link HoldCore} is the pure state machine (an input observation → a reading).
44098
+ * {@link HoldTracker} keys cores by a leaf path for the STATE leaves the tree
44099
+ * evaluator judges. {@link BecomesWatch} wraps a core with a timer for the
44100
+ * `device-becomes` EVENT leaf: the deadline is armed when the hold starts and
44101
+ * cancelled when it breaks, and the leaf fires ONCE per episode.
44102
+ *
44103
+ * Hold state is memory only. A hold that is already running when the watcher
44104
+ * first looks (a restart) is INHERITED: nobody knows when it began, so it never
44105
+ * fires for that episode and a state leaf reads it as unknown until the window
44106
+ * has elapsed from the moment the watcher started (D49: not-known is not false).
44107
+ */
44108
+ var HOLD_MAX_MS = 864e5;
44109
+ var HoldModeSchema = _enum(["held", "stable"]);
44110
+ var HoldSpecSchema = object({
44111
+ /** How long the condition (or the value) must hold. */
44112
+ ms: number$1().int().min(1).max(HOLD_MAX_MS),
44113
+ mode: HoldModeSchema,
44114
+ /** `stable` only: the largest move of the value that still counts as unchanged (absolute, base unit). */
44115
+ tolerance: number$1().min(0).optional()
44116
+ });
44117
+ /** What a trace says about a hold. */
44118
+ var HoldTraceSchema = object({
44119
+ phase: _enum([
44120
+ "pending",
44121
+ "cancelled",
44122
+ "fired",
44123
+ "reseeded"
44124
+ ]),
44125
+ ms: number$1().int(),
44126
+ mode: HoldModeSchema,
44127
+ /** When the hold started (ms epoch). */
44128
+ since: number$1().optional(),
44129
+ /** When it completes (ms epoch), for a pending hold. */
44130
+ deadline: number$1().optional()
44131
+ });
44132
+ /** The operators a `device-becomes` leaf may carry: a threshold or a range. */
44133
+ var BecomesOperatorSchema = _enum([
44134
+ "eq",
44135
+ "ne",
44136
+ "gt",
44137
+ "gte",
44138
+ "lt",
44139
+ "lte",
44140
+ "between",
44141
+ "outside"
44142
+ ]);
44096
44143
  /** How a state leaf compares a device field to a value. Derived from the
44097
44144
  * field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
44098
44145
  var AutomationConditionOperatorSchema = _enum([
@@ -44116,18 +44163,52 @@ var DeviceBecomesLeafSchema = object({
44116
44163
  deviceId: number$1().int().nonnegative(),
44117
44164
  cap: string().min(1),
44118
44165
  fieldPath: string().min(1),
44119
- /** Fire when the field takes this value. Omit to fire on any change. */
44166
+ /** Fire when the field takes this value. Omit (with no `operator`) to fire on any change. */
44120
44167
  becomes: union([
44121
44168
  string(),
44122
44169
  number$1(),
44123
44170
  boolean()
44124
44171
  ]).optional(),
44125
- /** Only on a CHANGE of value, not on every re-report. */
44172
+ /**
44173
+ * A threshold or a range instead of an equality: "crosses above 3000", "enters
44174
+ * 18..24". The leaf fires on the TRANSITION into the predicate, never on a re-report.
44175
+ * `value` is in the field's base unit; `between` / `outside` take `[low, high]`.
44176
+ */
44177
+ operator: BecomesOperatorSchema.optional(),
44178
+ value: union([
44179
+ number$1(),
44180
+ string(),
44181
+ boolean(),
44182
+ array(number$1()).length(2)
44183
+ ]).optional(),
44184
+ /**
44185
+ * Hold: the transition (or, with no predicate, the new value) must stay for
44186
+ * `for.ms` before the leaf fires, ONCE per episode. See `hold.ts`.
44187
+ */
44188
+ for: HoldSpecSchema.optional(),
44189
+ /** @deprecated every leaf fires on a transition now; read as always on. */
44126
44190
  edge: boolean().optional(),
44127
- /** The new value must hold this long before it counts. */
44128
- forMs: number$1().int().min(0).max(864e5).optional(),
44191
+ /** @deprecated legacy `held` window — read through `holdOf` as `for: { ms, mode: 'held' }`. */
44192
+ forMs: number$1().int().min(0).max(HOLD_MAX_MS).optional(),
44129
44193
  /** Collapse a burst into one occurrence. */
44130
44194
  debounceMs: number$1().int().min(0).max(6e5).optional()
44195
+ }).superRefine((leaf, ctx) => {
44196
+ if (leaf.becomes !== void 0 && leaf.operator !== void 0) ctx.addIssue({
44197
+ code: "custom",
44198
+ message: "a leaf names `becomes` or `operator`, not both",
44199
+ path: ["operator"]
44200
+ });
44201
+ if (leaf.operator !== void 0 && leaf.value === void 0) ctx.addIssue({
44202
+ code: "custom",
44203
+ message: "an operator needs the value to compare to",
44204
+ path: ["value"]
44205
+ });
44206
+ const range = leaf.operator === "between" || leaf.operator === "outside";
44207
+ if (leaf.operator !== void 0 && range !== Array.isArray(leaf.value)) ctx.addIssue({
44208
+ code: "custom",
44209
+ message: range ? "a range takes [low, high]" : "only a range takes [low, high]",
44210
+ path: ["value"]
44211
+ });
44131
44212
  });
44132
44213
  var DeviceEventLeafSchema = object({
44133
44214
  kind: literal("device-event"),
@@ -44192,7 +44273,9 @@ var DeviceIsLeafSchema = object({
44192
44273
  QuantityValueSchema
44193
44274
  ]),
44194
44275
  /** The unit the person picked for `value` (display only; `value` is in the base unit). */
44195
- unit: string().min(1).optional()
44276
+ unit: string().min(1).optional(),
44277
+ /** The comparison must hold (`held`) or the value stay put (`stable`) this long. */
44278
+ for: HoldSpecSchema.optional()
44196
44279
  });
44197
44280
  /**
44198
44281
  * True when the camera currently has a PARKED object matching `subjects` in at
@@ -44215,7 +44298,9 @@ var StationaryPresentLeafSchema = object({
44215
44298
  var ExpressionLeafSchema = object({
44216
44299
  kind: literal("expression"),
44217
44300
  expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
44218
- bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
44301
+ bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema),
44302
+ /** The expression must stay true this long. An expression has no single value, so only `held`. */
44303
+ for: HoldSpecSchema.optional()
44219
44304
  }).superRefine((src, ctx) => {
44220
44305
  const err = validateExpressionSource(src);
44221
44306
  if (err !== null) ctx.addIssue({
@@ -44223,6 +44308,11 @@ var ExpressionLeafSchema = object({
44223
44308
  message: err,
44224
44309
  path: ["expr"]
44225
44310
  });
44311
+ if (src.for?.mode === "stable") ctx.addIssue({
44312
+ code: "custom",
44313
+ message: "an expression is held, not stable: it has no single value to keep still",
44314
+ path: ["for", "mode"]
44315
+ });
44226
44316
  });
44227
44317
  var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
44228
44318
  object({
@@ -44982,7 +45072,9 @@ var AutomationTriggerTraceSchema = object({
44982
45072
  /** Why an event was rejected ("class person not in [dog]") or what fired. */
44983
45073
  reason: string(),
44984
45074
  /** deviceId, className, trackId, score … — only what the trigger exposes. */
44985
- vars: record(string(), TraceValueSchema)
45075
+ vars: record(string(), TraceValueSchema),
45076
+ /** A hold (`for`) this occurrence went through: pending, cancelled, fired or re-seeded. */
45077
+ hold: HoldTraceSchema.optional()
44986
45078
  });
44987
45079
  var AutomationConditionTraceNodeSchema = object({
44988
45080
  /** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
@@ -45328,7 +45420,7 @@ object({
45328
45420
  * is irrelevant; the array is treated as a set by consumers. */
45329
45421
  hiddenChildIds: array(number$1()).readonly()
45330
45422
  });
45331
- DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Light, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Cover, DeviceType.Lock, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Generic, method(object({
45423
+ DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Cover, DeviceType.Lock, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Generic, method(object({
45332
45424
  deviceId: number$1().int().nonnegative(),
45333
45425
  childDeviceId: number$1().int().nonnegative(),
45334
45426
  hidden: boolean()
@@ -49499,6 +49591,140 @@ var climateControlCapability = {
49499
49591
  } }
49500
49592
  };
49501
49593
  /**
49594
+ * cluster-update — the ONE flow that brings the whole cluster to a release
49595
+ * (planning#493). Hub-provided (`server-provided`), hub-only: the hub runs
49596
+ * the sequence itself, so the admin UI, the MCP and the CLI all start the same
49597
+ * job and read the same report instead of each driving their own loop.
49598
+ *
49599
+ * 1. backup (`backup.trigger`) — skipped only when the caller says so
49600
+ * 2. the hub's server closure (`applyServerUpdate`); the hub restarts and the
49601
+ * job RESUMES on the new boot, verified against the hub's running version
49602
+ * 3. every online agent behind the target, one at a time, verified: runs the
49603
+ * target, settled, online, and its capabilities registered (D848) — an
49604
+ * agent that comes back capless is a FAILED step, never a done one
49605
+ * 4. every stale addon on the hub (`addons.updatePackage`); agents follow by
49606
+ * reconciliation, which is verified, never pushed
49607
+ *
49608
+ * The job stops on the first failed step with a typed reason (D877). Its state
49609
+ * is durable on the hub and read by RPC (`getClusterUpdateJob`) — events are
49610
+ * telemetry and never carry it (D8, D11).
49611
+ */
49612
+ /** The steps, in the order they run. */
49613
+ var ClusterUpdateStepKindSchema = _enum([
49614
+ "backup",
49615
+ "hub-server",
49616
+ "agent-server",
49617
+ "addons"
49618
+ ]);
49619
+ /**
49620
+ * - `pending` — not reached yet.
49621
+ * - `running` — in progress (for `hub-server`: possibly across the restart).
49622
+ * - `ok` — done AND verified.
49623
+ * - `failed` — did not land; `reason` + `detail` say why. Stops the job.
49624
+ * - `skipped` — not run: nothing to do, opted out, the node is offline, or a
49625
+ * step before it failed. `detail` says which.
49626
+ */
49627
+ var ClusterUpdateStepStatusSchema = _enum([
49628
+ "pending",
49629
+ "running",
49630
+ "ok",
49631
+ "failed",
49632
+ "skipped"
49633
+ ]);
49634
+ /**
49635
+ * Why a step (and with it the job) failed. Stable, greppable; `detail` carries
49636
+ * the prose.
49637
+ */
49638
+ var ClusterUpdateFailureReasonSchema = _enum([
49639
+ "backup-unavailable",
49640
+ "backup-failed",
49641
+ "server-refused",
49642
+ "server-not-on-target",
49643
+ "node-capless",
49644
+ "status-unreadable",
49645
+ "addon-update-failed",
49646
+ "addons-not-converged",
49647
+ "interrupted"
49648
+ ]);
49649
+ var ClusterUpdateStepSchema = object({
49650
+ kind: ClusterUpdateStepKindSchema,
49651
+ /** The node the step acts on; absent for `backup` and `addons`. */
49652
+ nodeId: string().optional(),
49653
+ status: ClusterUpdateStepStatusSchema,
49654
+ /** One line for the operator: what happened, or why not. */
49655
+ detail: string(),
49656
+ reason: ClusterUpdateFailureReasonSchema.optional(),
49657
+ /** `hub-server` / `agent-server`: the version the node ran before. */
49658
+ fromVersion: string().nullable().optional(),
49659
+ /** `addons`: the packages this step updates, `name from -> to`. */
49660
+ packages: array(string()).readonly().optional(),
49661
+ startedAt: number$1().optional(),
49662
+ finishedAt: number$1().optional()
49663
+ });
49664
+ var ClusterUpdateJobStateSchema = _enum([
49665
+ "running",
49666
+ "succeeded",
49667
+ "failed"
49668
+ ]);
49669
+ var ClusterUpdateJobSchema = object({
49670
+ id: string(),
49671
+ state: ClusterUpdateJobStateSchema,
49672
+ /** The server version every node is brought to. */
49673
+ targetVersion: string(),
49674
+ /** Who started it (the user name, or `system`). */
49675
+ requestedBy: string(),
49676
+ startedAt: number$1(),
49677
+ finishedAt: number$1().optional(),
49678
+ /** Every step, in run order; later steps stay `pending` while it runs. */
49679
+ steps: array(ClusterUpdateStepSchema).readonly(),
49680
+ /** On `failed`: the step that stopped it (`nodeId` or the step kind). */
49681
+ stoppedAt: string().optional(),
49682
+ reason: ClusterUpdateFailureReasonSchema.optional(),
49683
+ /** Agents offline when the plan was read: never touched. */
49684
+ skippedOffline: array(string()).readonly()
49685
+ });
49686
+ var ClusterUpdateOptionsSchema = object({
49687
+ /** Explicit server target; omitted = the hub's latest registry check. */
49688
+ version: string().optional(),
49689
+ /** Update the addons too (default true). */
49690
+ addons: boolean().optional(),
49691
+ /**
49692
+ * Back up before touching anything (default true). `false` is an explicit
49693
+ * operator opt-out; a missing backup provider is otherwise a failure.
49694
+ */
49695
+ backup: boolean().optional()
49696
+ });
49697
+ /** Why a plan cannot start. */
49698
+ var ClusterUpdateBlockerSchema = _enum([
49699
+ "no-target",
49700
+ "node-busy",
49701
+ "job-running",
49702
+ "nothing-to-do",
49703
+ "status-unreadable"
49704
+ ]);
49705
+ var ClusterUpdatePlanSchema = object({
49706
+ /** `null` when no target could be resolved (`blocker: 'no-target'`). */
49707
+ targetVersion: string().nullable(),
49708
+ /** The steps the job WOULD run, all `pending` (or `skipped` with a reason). */
49709
+ steps: array(ClusterUpdateStepSchema).readonly(),
49710
+ skippedOffline: array(string()).readonly(),
49711
+ /** `null` = the plan can start. */
49712
+ blocker: ClusterUpdateBlockerSchema.nullable(),
49713
+ /** Prose for `blocker`. */
49714
+ blockerDetail: string().nullable()
49715
+ });
49716
+ var StartClusterUpdateResultSchema = object({
49717
+ accepted: boolean(),
49718
+ /** The job to follow with `getClusterUpdateJob`; `null` when refused. */
49719
+ jobId: string().nullable(),
49720
+ blocker: ClusterUpdateBlockerSchema.nullable(),
49721
+ message: string()
49722
+ });
49723
+ method(ClusterUpdateOptionsSchema, ClusterUpdatePlanSchema, { auth: "admin" }), method(ClusterUpdateOptionsSchema, StartClusterUpdateResultSchema, {
49724
+ kind: "mutation",
49725
+ auth: "admin"
49726
+ }), method(object({ jobId: string().optional() }), ClusterUpdateJobSchema.nullable(), { auth: "admin" });
49727
+ /**
49502
49728
  * Color-light cap. Coexists with `switch` (on/off) and `brightness`
49503
49729
  * (level) on the same device — `switch` toggles the bulb, `brightness`
49504
49730
  * sets the dim level, this cap sets the chromaticity.
@@ -50829,7 +51055,7 @@ var mediaPlayerCapability = {
50829
51055
  isMuted: { kind: "boolean" }
50830
51056
  } }
50831
51057
  };
50832
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.MediaPlayer;
51058
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.MediaPlayer;
50833
51059
  var NUMBER = {
50834
51060
  kind: "fixed",
50835
51061
  result: ["number"]
@@ -52301,27 +52527,35 @@ var DeviceAdminLinkSchema = object({
52301
52527
  });
52302
52528
  method(object({ deviceId: number$1().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" });
52303
52529
  /**
52304
- * device-discovery — device-scoped capability for parents that host /
52305
- * enumerate child devices (Reolink Hub / NVR, ONVIF gateway, future
52306
- * integrations). The parent advertises a list of discoverable children,
52307
- * and the operator promotes a subset to real devices via `adoptDevice`.
52308
- * Child lifecycle (kernel device create/remove, parent reference) is
52309
- * owned by the framework so a single shared `<DiscoveryPanel />` can
52310
- * drive any integration.
52311
- *
52312
- * Boundaries:
52313
- * - The cap exposes ENUMERATION + ADOPT/RELEASE only. The protocol
52314
- * used to discover children (CGI poll, mDNS, push from hub, ...)
52315
- * is the integration's business; results land in `runtimeState`
52316
- * so the panel can render without a fresh round-trip.
52317
- * - `childNativeId` is integration-defined and must be stable across
52318
- * reboots so re-discovery doesn't duplicate adopted children.
52319
- * - `adoptDevice` returns the new `deviceId` so the UI can navigate
52320
- * directly to the freshly-created child's settings page.
52321
- */
52322
- /**
52323
- * Status enum for a discovered device. Mirrors the Reolink simpleEvent
52324
- * lifecycle (`online` / `sleeping` / `offline`) — generic enough for
52530
+ * `device-adoption` — generic discovery + adoption surface,
52531
+ * system-scoped collection (one provider per integration addon), routed
52532
+ * by explicit `{ addonId }` on direct `deviceAdoption.*` calls — the same
52533
+ * pin model as `connection-test`. The admin UI keeps using
52534
+ * `deviceManager.adoption*` (which already threads `addonId`). The de-HA-ified
52535
+ * successor to `ha-discovery`:
52536
+ * keyed by `integrationId` (the addon resolves integrationId → broker
52537
+ * internally) rather than a connection-specific `brokerId`, and it
52538
+ * OWNS the shared `DiscoveredChildDevice` candidate type
52539
+ * (declared below) so a single shared adoption modal renders any integration's
52540
+ * candidate tree.
52541
+ *
52542
+ * UI flow (Integrations page → integration detail):
52543
+ * 1. `listCandidates({integrationId, page, pageSize, filter})` →
52544
+ * paginated candidate list, each row collapsed.
52545
+ * 2. User expands a row → `getCandidate({integrationId,
52546
+ * childNativeId})` returns the single candidate (with its nested
52547
+ * `children`) for the accordion render.
52548
+ * 3. User clicks "Adopt" on one or more candidates →
52549
+ * `adopt({integrationId, childNativeIds, perCandidate?})`
52550
+ * materialises one parent CamStack device per candidate plus its
52551
+ * accessory children. `perCandidate[childNativeId]` carries an
52552
+ * optional display-name override + a `hiddenChildIds` pre-hide set
52553
+ * (the parent's `accessories.hiddenChildIds` gates UI visibility).
52554
+ * 4. User clicks "Release" → `release({integrationId, camDeviceId})`
52555
+ * removes the parent + every child from the kernel registry.
52556
+ */
52557
+ /**
52558
+ * Status enum for a discovered device. Generic enough for
52325
52559
  * battery cams, awake cams, and unreachable cams alike.
52326
52560
  */
52327
52561
  var DiscoveredChildStatusSchema = _enum([
@@ -52338,12 +52572,12 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
52338
52572
  /** DeviceType the child should be created with on adopt. */
52339
52573
  type: _enum(DeviceType),
52340
52574
  status: DiscoveredChildStatusSchema,
52341
- /** Free-form integration-specific metadata surfaced in the panel. */
52575
+ /** Free-form integration-specific metadata surfaced in the adoption modal. */
52342
52576
  metadata: object({
52343
52577
  model: string().optional(),
52344
52578
  serialNumber: string().optional(),
52345
52579
  uid: string().optional(),
52346
- /** Reolink: 0-based channel index inside the parent NVR/Hub. */
52580
+ /** Reolink: 0-based channel index inside the parent NVR. */
52347
52581
  rtspChannel: number$1().int().nonnegative().optional(),
52348
52582
  isBattery: boolean().optional(),
52349
52583
  isDoorbell: boolean().optional(),
@@ -52359,137 +52593,24 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
52359
52593
  }).default({}),
52360
52594
  /**
52361
52595
  * `true` when the framework already created a child device for this
52362
- * `childNativeId` under the current parent. The panel uses it to
52596
+ * `childNativeId` under the current parent. The adoption modal uses it to
52363
52597
  * gate the Add/Remove button and surface the existing `deviceId`.
52364
52598
  */
52365
52599
  alreadyAdopted: boolean(),
52366
52600
  /** When `alreadyAdopted=true`, the framework-assigned child device id. */
52367
52601
  adoptedDeviceId: number$1().int().nonnegative().nullable(),
52368
- /** Capability names the candidate advertises — integrations populate, Hubs omit. */
52602
+ /** Capability names the candidate advertises — integrations populate, flat enumerations omit. */
52369
52603
  capabilities: array(string()).readonly().optional(),
52370
- /** Feature flags the candidate advertises — integrations populate, Hubs omit. */
52604
+ /** Feature flags the candidate advertises — integrations populate, flat enumerations omit. */
52371
52605
  features: array(string()).readonly().optional(),
52372
52606
  /** Upstream-system identity + rendering hints for the candidate. */
52373
52607
  sourceInfo: SourceInfoSchema.optional(),
52374
- /** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
52608
+ /** Nested entity-children for accordion preview — integrations populate, flat enumerations omit. */
52375
52609
  children: array(DiscoveredChildDeviceSchema).readonly().optional(),
52376
52610
  /** `childNativeId` of the gateway candidate in the same list this device sits behind. */
52377
52611
  parentStableId: string().optional()
52378
52612
  }));
52379
52613
  /**
52380
- * Status block — populated by the integration after each
52381
- * `refreshDiscovery` (or push-driven update). The runtime-state slice
52382
- * is what the panel reads via the per-device proxy; `getStatus`
52383
- * returns the same payload via the codegen'd auto-injection.
52384
- */
52385
- var DeviceDiscoveryStatusSchema = object({
52386
- discovered: array(DiscoveredChildDeviceSchema),
52387
- /** Wall-clock ms of the last successful enumeration. */
52388
- lastDiscoveryAt: number$1().int().nonnegative().nullable(),
52389
- /** Last error surfaced from the source (rendered as a banner). */
52390
- lastError: string().nullable()
52391
- });
52392
- var deviceDiscoveryCapability = {
52393
- name: "device-discovery",
52394
- scope: "device",
52395
- deviceNative: true,
52396
- mode: "singleton",
52397
- deviceTypes: [DeviceType.Hub],
52398
- status: {
52399
- schema: DeviceDiscoveryStatusSchema,
52400
- kind: "poll"
52401
- },
52402
- runtimeState: DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number$1().int().nonnegative() }),
52403
- /**
52404
- * Runtime-state durability: **session** — 5.7 KB of scan output on the largest device, fully re-derivable by re-scanning.
52405
- *
52406
- * See `RuntimeStateDurability`. Enforced by
52407
- * `scripts/check-runtime-state-durability.ts`.
52408
- */
52409
- durability: "session",
52410
- /**
52411
- * History (D850): none — inventory.
52412
- */
52413
- history: "none",
52414
- methods: {
52415
- /**
52416
- * Snapshot of the current `discovered` list. Returns the
52417
- * runtime-state cache — call `refreshDiscovery` first if a
52418
- * fresh round-trip to the source is required.
52419
- */
52420
- listDiscovered: method(object({ deviceId: number$1().int().nonnegative() }), array(DiscoveredChildDeviceSchema).readonly()),
52421
- /**
52422
- * Force the integration to re-enumerate and update the
52423
- * runtime-state slice. Returns the freshly-enumerated list (also
52424
- * available via `listDiscovered` post-call).
52425
- */
52426
- refreshDiscovery: method(object({ deviceId: number$1().int().nonnegative() }), array(DiscoveredChildDeviceSchema).readonly(), {
52427
- kind: "mutation",
52428
- auth: "admin"
52429
- }),
52430
- /**
52431
- * Promote a discovered entry to a real child device. The framework
52432
- * creates the child via `kernel.devices.create()` with
52433
- * `parentDeviceId = parent.id` and seeds the child's config from
52434
- * `childInitialConfig` (driver-defined; usually carries channel +
52435
- * uid + parent reference). Returns the kernel-assigned numeric id.
52436
- */
52437
- adoptDevice: method(object({
52438
- deviceId: number$1().int().nonnegative(),
52439
- childNativeId: string(),
52440
- /** Optional override for the child's display name. */
52441
- name: string().optional()
52442
- }), object({
52443
- deviceId: number$1().int().nonnegative(),
52444
- stableId: string()
52445
- }), {
52446
- kind: "mutation",
52447
- auth: "admin"
52448
- }),
52449
- /**
52450
- * Inverse of `adoptDevice`: removes the child device from the
52451
- * kernel registry. The discovered entry remains in the
52452
- * enumeration (status updates resume) so the operator can re-adopt
52453
- * it later without a fresh refresh.
52454
- */
52455
- releaseDevice: method(object({
52456
- deviceId: number$1().int().nonnegative(),
52457
- childDeviceId: number$1().int().nonnegative()
52458
- }), _void(), {
52459
- kind: "mutation",
52460
- auth: "admin"
52461
- })
52462
- }
52463
- };
52464
- /**
52465
- * `device-adoption` — generic discovery + adoption surface,
52466
- * system-scoped collection (one provider per integration addon), routed
52467
- * by explicit `{ addonId }` on direct `deviceAdoption.*` calls — the same
52468
- * pin model as `connection-test`. The admin UI keeps using
52469
- * `deviceManager.adoption*` (which already threads `addonId`). The de-HA-ified
52470
- * successor to `ha-discovery`:
52471
- * keyed by `integrationId` (the addon resolves integrationId → broker
52472
- * internally) rather than a connection-specific `brokerId`, and it
52473
- * REUSES the shared `DiscoveredChildDevice` candidate type from
52474
- * `device-discovery.cap.ts` so a single shared adoption panel renders
52475
- * any integration's candidate tree.
52476
- *
52477
- * UI flow (Integrations page → integration detail):
52478
- * 1. `listCandidates({integrationId, page, pageSize, filter})` →
52479
- * paginated candidate list, each row collapsed.
52480
- * 2. User expands a row → `getCandidate({integrationId,
52481
- * childNativeId})` returns the single candidate (with its nested
52482
- * `children`) for the accordion render.
52483
- * 3. User clicks "Adopt" on one or more candidates →
52484
- * `adopt({integrationId, childNativeIds, perCandidate?})`
52485
- * materialises one parent CamStack device per candidate plus its
52486
- * accessory children. `perCandidate[childNativeId]` carries an
52487
- * optional display-name override + a `hiddenChildIds` pre-hide set
52488
- * (the parent's `accessories.hiddenChildIds` gates UI visibility).
52489
- * 4. User clicks "Release" → `release({integrationId, camDeviceId})`
52490
- * removes the parent + every child from the kernel registry.
52491
- */
52492
- /**
52493
52614
  * Provider-declared discovery *granularity* filter (the keystone of the
52494
52615
  * single-entity-import design). A provider advertises which discovery
52495
52616
  * granularities it supports via `listCandidateFilters`; the operator picks
@@ -52950,6 +53071,90 @@ var SiteZoneSchema = object({
52950
53071
  * the ones that most want grouping. */
52951
53072
  locationKeys: array(string())
52952
53073
  });
53074
+ var DeviceSearchRequiresSchema = object({
53075
+ /** Capability names the field wants a value of. */
53076
+ caps: array(string()).readonly(),
53077
+ /** `any` (default): a device with one of them is compatible. `all`: it must hold every one. */
53078
+ mode: _enum(["any", "all"]).optional()
53079
+ });
53080
+ var DeviceSearchFiltersSchema = object({
53081
+ locations: array(string()).readonly().optional(),
53082
+ integrationIds: array(string()).readonly().optional(),
53083
+ types: array(string()).readonly().optional()
53084
+ });
53085
+ var DeviceSearchInputSchema = object({
53086
+ /** Matches name, location, type, integration (id and label) and id. Case-insensitive. */
53087
+ text: string().optional(),
53088
+ requires: DeviceSearchRequiresSchema.optional(),
53089
+ filters: DeviceSearchFiltersSchema.optional(),
53090
+ /** List the devices that do not provide the required cap too (greyed, not pickable). */
53091
+ includeIncompatible: boolean().optional(),
53092
+ /** The device the field belongs to: its location drives `suggested`. */
53093
+ contextDeviceId: number$1().optional(),
53094
+ /**
53095
+ * Resolve exactly these devices, regardless of every filter, so a picked
53096
+ * value can be rendered. When given, `items` is these devices in the order
53097
+ * asked (no paging, `nextCursor` null) and the ones that do not exist are
53098
+ * listed in `missingIds`.
53099
+ */
53100
+ ids: array(number$1()).readonly().optional(),
53101
+ cursor: string().nullable().optional(),
53102
+ limit: number$1().int().min(1).max(200).optional()
53103
+ });
53104
+ var DeviceValueOptionSchema = object({
53105
+ cap: string(),
53106
+ /** Status-schema field path inside the cap (same paths as `getWireableFields`). */
53107
+ path: string(),
53108
+ label: string(),
53109
+ /** The current value as text; `null` when it is unknown or the read FAILED — never `0`. */
53110
+ reading: string().nullable(),
53111
+ unit: string().nullable().optional(),
53112
+ /** The field's measured quantity, when the cap declares one, so a client can format the reading. */
53113
+ quantity: ReadingQuantitySchema.optional()
53114
+ });
53115
+ var DeviceSearchItemSchema = object({
53116
+ id: number$1(),
53117
+ name: string(),
53118
+ type: _enum(DeviceType),
53119
+ location: string().nullable(),
53120
+ integrationId: string().nullable(),
53121
+ integrationLabel: string().nullable(),
53122
+ online: boolean().nullable(),
53123
+ disabled: boolean(),
53124
+ compatible: boolean(),
53125
+ /** Only the required caps' fields; empty when nothing was required or the device is incompatible. */
53126
+ values: array(DeviceValueOptionSchema).readonly()
53127
+ });
53128
+ var DeviceSearchFacetValueSchema = object({
53129
+ value: string(),
53130
+ count: number$1()
53131
+ });
53132
+ var DeviceSearchFacetIntegrationSchema = object({
53133
+ id: string(),
53134
+ label: string(),
53135
+ count: number$1()
53136
+ });
53137
+ var DeviceSearchFacetsSchema = object({
53138
+ locations: array(DeviceSearchFacetValueSchema).readonly(),
53139
+ integrations: array(DeviceSearchFacetIntegrationSchema).readonly(),
53140
+ types: array(DeviceSearchFacetValueSchema).readonly()
53141
+ });
53142
+ var DeviceSearchResultSchema = object({
53143
+ items: array(DeviceSearchItemSchema).readonly(),
53144
+ nextCursor: string().nullable(),
53145
+ /** Devices listed by this query across all pages (filters + text applied; incompatible counted only when `includeIncompatible`). */
53146
+ total: number$1(),
53147
+ /** Compatible devices after filters + text. */
53148
+ compatibleTotal: number$1(),
53149
+ /** Every device the caller's fleet holds, before anything is applied. */
53150
+ fleetTotal: number$1(),
53151
+ /** Incompatible devices after filters + text — the K of "show the K that don't". */
53152
+ hiddenIncompatible: number$1(),
53153
+ facets: DeviceSearchFacetsSchema,
53154
+ /** Same location as `contextDeviceId`; first page only. */
53155
+ suggested: array(DeviceSearchItemSchema).readonly(),
53156
+ missingIds: array(number$1()).readonly()
53157
+ });
52953
53158
  var ProviderStatusSchema = object({
52954
53159
  connected: boolean(),
52955
53160
  deviceCount: number$1(),
@@ -53435,6 +53640,9 @@ var DeviceInfoSchema = object({
53435
53640
  * named accordion sections (with optional intra-section order). See
53436
53641
  * `DeviceMeta.childLayout`. Absent ⇒ no layout declared. */
53437
53642
  childLayout: array(ChildLayoutEntrySchema).readonly().optional(),
53643
+ /** The children a container stands for, most preferred first, by accessory
53644
+ * `childKey`. Provider-declared. See `DeviceMeta.primaryChildPreference`. */
53645
+ primaryChildPreference: array(string()).readonly().optional(),
53438
53646
  /** Operator-authored per-device display override. See `DeviceMeta.display`. */
53439
53647
  display: DeviceDisplayOverrideSchema.optional()
53440
53648
  });
@@ -53583,6 +53791,9 @@ var DeviceMetaSchema = object({
53583
53791
  * accordion sections (with optional intra-section order). See
53584
53792
  * `DeviceMeta.childLayout`. Absent ⇒ no layout declared. */
53585
53793
  childLayout: array(ChildLayoutEntrySchema).readonly().optional(),
53794
+ /** The children a container stands for, most preferred first, by accessory
53795
+ * `childKey`. Provider-declared. See `DeviceMeta.primaryChildPreference`. */
53796
+ primaryChildPreference: array(string()).readonly().optional(),
53586
53797
  /** Semantic role string (`DeviceRole`) — propagated from the spawn pre-seed.
53587
53798
  * Optional: only present for accessory children that carry a known role. */
53588
53799
  role: string().nullable().optional(),
@@ -53798,6 +54009,12 @@ method(object({
53798
54009
  }), _void(), {
53799
54010
  kind: "mutation",
53800
54011
  auth: "admin"
54012
+ }), method(object({
54013
+ deviceId: number$1(),
54014
+ primaryChildPreference: array(string()).readonly()
54015
+ }), _void(), {
54016
+ kind: "mutation",
54017
+ auth: "admin"
53801
54018
  }), method(object({
53802
54019
  deviceId: number$1(),
53803
54020
  display: DeviceDisplayOverrideSchema.nullable()
@@ -53935,7 +54152,7 @@ method(object({
53935
54152
  * it answers today and the caller filters as it already does.
53936
54153
  */
53937
54154
  deviceIds: array(number$1()).optional()
53938
- }), array(DeviceInfoSchema), { role: "viewer" }), method(object({ deviceId: number$1() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number$1() }), array(DeviceInfoSchema), { role: "viewer" }), method(object({ parentDeviceIds: array(number$1()).max(256) }), record(string(), array(DeviceInfoSchema))), method(object({ deviceId: number$1() }), object({
54155
+ }), array(DeviceInfoSchema), { role: "viewer" }), method(DeviceSearchInputSchema, DeviceSearchResultSchema, { role: "viewer" }), method(object({ deviceId: number$1() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number$1() }), array(DeviceInfoSchema), { role: "viewer" }), method(object({ parentDeviceIds: array(number$1()).max(256) }), record(string(), array(DeviceInfoSchema))), method(object({ deviceId: number$1() }), object({
53939
54156
  mode: LinkedDevicesModeSchema,
53940
54157
  devices: array(LinkedDeviceSchema)
53941
54158
  }), { role: "viewer" }), method(object({ deviceIds: array(number$1()) }), array(LinkedDevicesForDeviceSchema), { role: "viewer" }), method(object({ deviceId: number$1() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number$1() }), array(ConfigEntrySchema)), method(object({ deviceId: number$1() }), ConfigUISchemaOutput), method(object({
@@ -57603,13 +57820,11 @@ var HeroConfirmSchema = _enum([
57603
57820
  "press"
57604
57821
  ]);
57605
57822
  var HeroActionSchema = object({
57606
- /** Unique within the model; the `gated` list and the specs name it. */
57607
57823
  id: string(),
57608
57824
  label: HeroTextSchema,
57609
57825
  icon: IconKeySchema,
57610
57826
  call: HeroCallSchema,
57611
57827
  emphasis: _enum(["primary", "normal"]),
57612
- /** Ask before running it. */
57613
57828
  confirm: HeroConfirmSchema.nullable()
57614
57829
  });
57615
57830
  var HeroControlSchema = discriminatedUnion("kind", [
@@ -57660,6 +57875,8 @@ var HeroPillSchema = object({
57660
57875
  });
57661
57876
  var HeroRowSchema = object({
57662
57877
  id: HeroRowIdSchema,
57878
+ /** `scene`: the row's action and slider controls are drawn on the scene. Absent: an ordinary row. */
57879
+ placement: _enum(["row", "scene"]).optional(),
57663
57880
  /** The row's own name when the kind's label would not tell rows of one kind apart (a circuit's label). */
57664
57881
  label: HeroTextSchema.optional(),
57665
57882
  /** A group header drawn above the row when it differs from the previous row's ("System"). */
@@ -57690,9 +57907,16 @@ var HeroBadgeSchema = object({
57690
57907
  value: number$1().nullable(),
57691
57908
  label: HeroTextSchema
57692
57909
  });
57693
- var HeroRemoteSlotSchema = object({
57694
- /** Saved commands; `null` = not known yet (the button still shows). */
57695
- commands: number$1().int().nullable() });
57910
+ var HeroRemoteSlotSchema = object({
57911
+ /** Saved commands; `null` = not known yet (the button still shows). */
57912
+ commands: number$1().int().nullable(),
57913
+ /**
57914
+ * The control a list row draws as the "open the remote" icon. Recognised by its id
57915
+ * ({@link REMOTE_OPEN_ACTION_ID}); it changes no state, so a client opens the remote
57916
+ * instead of running `call`. Absent on a model from an older server.
57917
+ */
57918
+ open: HeroActionSchema.optional()
57919
+ });
57696
57920
  var HeroConsumablesSlotSchema = object({
57697
57921
  items: array(object({
57698
57922
  key: string(),
@@ -57860,6 +58084,8 @@ var HeroModelSchema = object({
57860
58084
  * the FIRST row that has any (a later row's control is never promoted). When
57861
58085
  * the model lists that part in `gated`, the row has no inline control: the
57862
58086
  * projection never falls through to another control the caller could use.
58087
+ * - a climate or thermostat row's primary is its `climate.mode` control (no power
58088
+ * toggle: "off" is a mode); when the model gates it the rule above applies as before;
57863
58089
  * - a timed run, when the model carries the slot, sits beside that primary:
57864
58090
  * Run while idle, Stop while armed. Gating the slot hides the control.
57865
58091
  * - the status line is the head (what plays, else the state) plus at most
@@ -57867,6 +58093,13 @@ var HeroModelSchema = object({
57867
58093
  * time left on an armed timed run;
57868
58094
  * - the trailing fact is the first of power, battery, consumables.
57869
58095
  *
58096
+ * - `extras` are the further inline controls of the types that carry more than one
58097
+ * ({@link ROW_EXTRAS_BY_TYPE}): climate setpoint, a robot's return to base,
58098
+ * a player's volume and remote. Each is a control the model already has, named by id,
58099
+ * in that order and at most {@link ROW_EXTRAS_MAX}; one the model lacks, or lists in
58100
+ * `gated`, is absent, and the one `primary` already is never repeats. Empty = the
58101
+ * row of an older server: `primary` alone.
58102
+ *
57870
58103
  * `projectDeviceRow` is pure and computes nothing: it selects from a
57871
58104
  * {@link HeroModel}. A row that needed a value the hero lacks would mean the
57872
58105
  * model is missing it, so the fix goes in the model.
@@ -57898,8 +58131,16 @@ var HeroRowModelSchema = object({
57898
58131
  * while armed. `null` when there is no slot, or the caller may not act.
57899
58132
  */
57900
58133
  timedRun: HeroRowTimedRunSchema.nullable(),
58134
+ /**
58135
+ * The further inline controls after `primary`, in order (see the header). The remote
58136
+ * is an `action` with id `remote.open`: the client opens the remote, it does not run
58137
+ * the call. Absent or empty = `primary` alone.
58138
+ */
58139
+ extras: array(HeroControlSchema).max(2).optional(),
57901
58140
  trailing: HeroFactSchema.nullable()
57902
58141
  });
58142
+ DeviceType.Climate, DeviceType.Thermostat;
58143
+ DeviceType.Climate, DeviceType.Thermostat, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer;
57903
58144
  /**
57904
58145
  * hero-layout — what a device's hero says (D1023). Read-only: the FIXED
57905
58146
  * layouts live in `@camstack/types` and nothing here stores or edits one.
@@ -69449,13 +69690,23 @@ var NcRuleTargetSchema = object({
69449
69690
  */
69450
69691
  params: record(string(), unknown()).optional()
69451
69692
  });
69693
+ /** Which picture kind one target receives (#487). */
69694
+ var NcTargetAttachmentSchema = _enum([
69695
+ "none",
69696
+ "image",
69697
+ "clip",
69698
+ "gif"
69699
+ ]);
69452
69700
  var NcTargetOverrideSchema = object({
69453
69701
  template: object({
69454
69702
  title: string().max(500).optional(),
69455
69703
  body: string().max(2e3).optional()
69456
69704
  }).optional(),
69457
69705
  schedule: NcScheduleSchema.optional(),
69458
- priority: number$1().int().min(1).max(5).optional()
69706
+ priority: number$1().int().min(1).max(5).optional(),
69707
+ attachment: NcTargetAttachmentSchema.optional(),
69708
+ /** `false` = no buttons for this target; `true` is the same as absent. */
69709
+ actions: boolean().optional()
69459
69710
  });
69460
69711
  /** Per-target settings keyed by target id (#487). */
69461
69712
  var NcTargetOverridesSchema = record(string(), NcTargetOverrideSchema);
@@ -71646,9 +71897,13 @@ var petFeederCapability = {
71646
71897
  */
71647
71898
  durability: "session",
71648
71899
  /**
71649
- * History (D850): recorded series.
71900
+ * History (D850): recorded series. `feeding` / `eating` are the meals the
71901
+ * device hero's "Meals" button names — change-only booleans, so a dispense
71902
+ * or a pet at the bowl is one span on the history.
71650
71903
  */
71651
71904
  history: { fields: {
71905
+ feeding: { kind: "boolean" },
71906
+ eating: { kind: "boolean" },
71652
71907
  foodLevel: { kind: "gauge" },
71653
71908
  bowlFood: { kind: "gauge" },
71654
71909
  food1: { kind: "gauge" },
@@ -73441,7 +73696,6 @@ var PowerSourceStateSchema = object({
73441
73696
  });
73442
73697
  var POWER_SOURCE_DEVICE_TYPES = [
73443
73698
  DeviceType.Gateway,
73444
- DeviceType.Hub,
73445
73699
  DeviceType.Camera,
73446
73700
  DeviceType.Sensor,
73447
73701
  DeviceType.MediaPlayer,
@@ -73493,18 +73747,18 @@ var powerSourceCapability = {
73493
73747
  mode: "singleton",
73494
73748
  deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73495
73749
  methods: {
73496
- getStatus: systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }),
73750
+ getStatus: systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "protected" }),
73497
73751
  powerOn: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73498
73752
  kind: "mutation",
73499
- auth: "admin"
73753
+ auth: "protected"
73500
73754
  }),
73501
73755
  powerOff: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73502
73756
  kind: "mutation",
73503
- auth: "admin"
73757
+ auth: "protected"
73504
73758
  }),
73505
73759
  forceRestart: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73506
73760
  kind: "mutation",
73507
- auth: "admin"
73761
+ auth: "protected"
73508
73762
  })
73509
73763
  },
73510
73764
  runtimeState: PowerSourceStateSchema,
@@ -83022,7 +83276,6 @@ var DEVICE_LOCAL_STATE_CAPS = {
83022
83276
  control: controlCapability,
83023
83277
  cover: coverCapability,
83024
83278
  dayNight: dayNightCapability,
83025
- deviceDiscovery: deviceDiscoveryCapability,
83026
83279
  deviceStatus: deviceStatusCapability,
83027
83280
  dishwasher: dishwasherCapability,
83028
83281
  doNotDisturb: doNotDisturbCapability,
@@ -88201,6 +88454,27 @@ Object.freeze({
88201
88454
  access: "create",
88202
88455
  role: "viewer"
88203
88456
  },
88457
+ "clusterUpdate.getClusterUpdateJob": {
88458
+ capName: "cluster-update",
88459
+ capScope: "system",
88460
+ addonId: null,
88461
+ access: "view",
88462
+ role: "admin"
88463
+ },
88464
+ "clusterUpdate.previewClusterUpdate": {
88465
+ capName: "cluster-update",
88466
+ capScope: "system",
88467
+ addonId: null,
88468
+ access: "view",
88469
+ role: "admin"
88470
+ },
88471
+ "clusterUpdate.startClusterUpdate": {
88472
+ capName: "cluster-update",
88473
+ capScope: "system",
88474
+ addonId: null,
88475
+ access: "create",
88476
+ role: "admin"
88477
+ },
88204
88478
  "color.getStatus": {
88205
88479
  capName: "color",
88206
88480
  capScope: "device",
@@ -88803,41 +89077,6 @@ Object.freeze({
88803
89077
  access: "create",
88804
89078
  role: "admin"
88805
89079
  },
88806
- "deviceDiscovery.adoptDevice": {
88807
- capName: "device-discovery",
88808
- capScope: "device",
88809
- addonId: null,
88810
- access: "create",
88811
- role: "admin"
88812
- },
88813
- "deviceDiscovery.getStatus": {
88814
- capName: "device-discovery",
88815
- capScope: "device",
88816
- addonId: null,
88817
- access: "view",
88818
- role: "viewer"
88819
- },
88820
- "deviceDiscovery.listDiscovered": {
88821
- capName: "device-discovery",
88822
- capScope: "device",
88823
- addonId: null,
88824
- access: "view",
88825
- role: "viewer"
88826
- },
88827
- "deviceDiscovery.refreshDiscovery": {
88828
- capName: "device-discovery",
88829
- capScope: "device",
88830
- addonId: null,
88831
- access: "create",
88832
- role: "admin"
88833
- },
88834
- "deviceDiscovery.releaseDevice": {
88835
- capName: "device-discovery",
88836
- capScope: "device",
88837
- addonId: null,
88838
- access: "create",
88839
- role: "admin"
88840
- },
88841
89080
  "deviceExport.applyDeviceSettingsPatch": {
88842
89081
  capName: "device-export",
88843
89082
  capScope: "system",
@@ -89573,6 +89812,13 @@ Object.freeze({
89573
89812
  access: "create",
89574
89813
  role: "admin"
89575
89814
  },
89815
+ "deviceManager.search": {
89816
+ capName: "device-manager",
89817
+ capScope: "system",
89818
+ addonId: null,
89819
+ access: "view",
89820
+ role: "viewer"
89821
+ },
89576
89822
  "deviceManager.setAlias": {
89577
89823
  capName: "device-manager",
89578
89824
  capScope: "system",
@@ -89664,6 +89910,13 @@ Object.freeze({
89664
89910
  access: "create",
89665
89911
  role: "admin"
89666
89912
  },
89913
+ "deviceManager.setPrimaryChildPreference": {
89914
+ capName: "device-manager",
89915
+ capScope: "system",
89916
+ addonId: null,
89917
+ access: "create",
89918
+ role: "admin"
89919
+ },
89667
89920
  "deviceManager.setRole": {
89668
89921
  capName: "device-manager",
89669
89922
  capScope: "system",
@@ -94303,28 +94556,28 @@ Object.freeze({
94303
94556
  capScope: "device",
94304
94557
  addonId: null,
94305
94558
  access: "create",
94306
- role: "admin"
94559
+ role: "viewer"
94307
94560
  },
94308
94561
  "powerSource.getStatus": {
94309
94562
  capName: "power-source",
94310
94563
  capScope: "device",
94311
94564
  addonId: null,
94312
94565
  access: "view",
94313
- role: "admin"
94566
+ role: "viewer"
94314
94567
  },
94315
94568
  "powerSource.powerOff": {
94316
94569
  capName: "power-source",
94317
94570
  capScope: "device",
94318
94571
  addonId: null,
94319
94572
  access: "create",
94320
- role: "admin"
94573
+ role: "viewer"
94321
94574
  },
94322
94575
  "powerSource.powerOn": {
94323
94576
  capName: "power-source",
94324
94577
  capScope: "device",
94325
94578
  addonId: null,
94326
94579
  access: "create",
94327
- role: "admin"
94580
+ role: "viewer"
94328
94581
  },
94329
94582
  "presence.getStatus": {
94330
94583
  capName: "presence",
@@ -97881,35 +98134,6 @@ Object.freeze({
97881
98134
  form: "single",
97882
98135
  optional: false
97883
98136
  }],
97884
- "deviceDiscovery.adoptDevice": [{
97885
- name: "deviceId",
97886
- form: "single",
97887
- optional: false
97888
- }],
97889
- "deviceDiscovery.getStatus": [{
97890
- name: "deviceId",
97891
- form: "single",
97892
- optional: false
97893
- }],
97894
- "deviceDiscovery.listDiscovered": [{
97895
- name: "deviceId",
97896
- form: "single",
97897
- optional: false
97898
- }],
97899
- "deviceDiscovery.refreshDiscovery": [{
97900
- name: "deviceId",
97901
- form: "single",
97902
- optional: false
97903
- }],
97904
- "deviceDiscovery.releaseDevice": [{
97905
- name: "childDeviceId",
97906
- form: "single",
97907
- optional: false
97908
- }, {
97909
- name: "deviceId",
97910
- form: "single",
97911
- optional: false
97912
- }],
97913
98137
  "deviceExport.applyDeviceSettingsPatch": [{
97914
98138
  name: "deviceId",
97915
98139
  form: "single",
@@ -98188,6 +98412,11 @@ Object.freeze({
98188
98412
  form: "single",
98189
98413
  optional: false
98190
98414
  }],
98415
+ "deviceManager.setPrimaryChildPreference": [{
98416
+ name: "deviceId",
98417
+ form: "single",
98418
+ optional: false
98419
+ }],
98191
98420
  "deviceManager.setRole": [{
98192
98421
  name: "deviceId",
98193
98422
  form: "single",
@@ -101656,7 +101885,9 @@ var TimelapseRuleInputSchema = object({
101656
101885
  /** Classes the notification counts — see {@link ReportClassesField}. */
101657
101886
  reportClasses: ReportClassesField.optional(),
101658
101887
  /** Canonical notification priority ordinal (1..5); per-target overridable. */
101659
- priority: PriorityField.default(3)
101888
+ priority: PriorityField.default(3),
101889
+ /** Per-target settings keyed by target id (D1032) — absent = every target follows the rule. */
101890
+ targetOverrides: NcTargetOverridesSchema.optional()
101660
101891
  });
101661
101892
  object({
101662
101893
  name: NameField.optional(),
@@ -101672,7 +101903,9 @@ object({
101672
101903
  previewText: PreviewTextField.optional(),
101673
101904
  previewMode: PreviewModeField.optional(),
101674
101905
  reportClasses: ReportClassesField.optional(),
101675
- priority: PriorityField.optional()
101906
+ priority: PriorityField.optional(),
101907
+ /** Per-target settings keyed by target id (D1032) — absent = every target follows the rule. */
101908
+ targetOverrides: NcTargetOverridesSchema.optional()
101676
101909
  });
101677
101910
  TimelapseRuleInputSchema.extend({
101678
101911
  id: string(),
@@ -102147,6 +102380,12 @@ Object.defineProperty(exports, "_enum", {
102147
102380
  return _enum;
102148
102381
  }
102149
102382
  });
102383
+ Object.defineProperty(exports, "_null", {
102384
+ enumerable: true,
102385
+ get: function() {
102386
+ return _null;
102387
+ }
102388
+ });
102150
102389
  Object.defineProperty(exports, "array", {
102151
102390
  enumerable: true,
102152
102391
  get: function() {
@@ -102165,6 +102404,12 @@ Object.defineProperty(exports, "binaryCapability", {
102165
102404
  return binaryCapability;
102166
102405
  }
102167
102406
  });
102407
+ Object.defineProperty(exports, "boolean", {
102408
+ enumerable: true,
102409
+ get: function() {
102410
+ return boolean;
102411
+ }
102412
+ });
102168
102413
  Object.defineProperty(exports, "boschRowsMigration", {
102169
102414
  enumerable: true,
102170
102415
  get: function() {