@camstack/addon-remote-storage 1.2.15 → 1.2.17

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.
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  let node_crypto = require("node:crypto");
24
24
  let node_path = require("node:path");
25
25
  node_path = __toESM(node_path);
26
- //#region ../types/dist/event-category-Cv9dO26A.mjs
26
+ //#region ../types/dist/event-category-Bxo5yJjt.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -230,6 +230,33 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
230
230
  EventCategory["PipelineCameraAssigned"] = "pipeline.camera-assigned";
231
231
  EventCategory["PipelineCameraUnassigned"] = "pipeline.camera-unassigned";
232
232
  /**
233
+ * A node the orchestrator would otherwise place cameras on has NO usable
234
+ * inference device: the operator enabled one or more accelerators there and
235
+ * the live probe reports every one of them unavailable. Emitted once per
236
+ * TRANSITION into that state (never per dispatch), and the node is dropped
237
+ * from the placement candidate set for as long as it holds.
238
+ *
239
+ * This exists because the state was previously invisible: little-unraid
240
+ * absorbed 283k inference errors in a day while still being handed cameras,
241
+ * and nothing in the system said so.
242
+ *
243
+ * A node with no accelerators configured at all is NOT this — its devices
244
+ * are `disabled`, not `unavailable`, and the runner's default CPU pool
245
+ * serves it exactly as before.
246
+ */
247
+ EventCategory["PipelineNodeInferenceUnavailable"] = "pipeline.node-inference-unavailable";
248
+ /**
249
+ * A camera has an OPEN detection session and has produced no detection at
250
+ * all for longer than the blind threshold — the camera is being decoded and
251
+ * inferred and is returning nothing. Emitted once per transition into blind,
252
+ * per camera.
253
+ *
254
+ * The failure it reports: a 1h43 detection blackout on the entrance camera
255
+ * that nobody noticed, because "a camera that detects nothing" and "a quiet
256
+ * camera" produce byte-identical silence.
257
+ */
258
+ EventCategory["PipelineDetectionBlind"] = "pipeline.detection-blind";
259
+ /**
233
260
  * Per-camera pipeline config was mutated by the orchestrator
234
261
  * (3-level settings change via `setAgentAddonDefaults` /
235
262
  * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
@@ -3021,6 +3048,9 @@ function handlePipeResult(left, next, ctx) {
3021
3048
  fallback: left.fallback
3022
3049
  }, ctx);
3023
3050
  }
3051
+ var $ZodPreprocess = /*@__PURE__*/ $constructor("$ZodPreprocess", (inst, def) => {
3052
+ $ZodPipe.init(inst, def);
3053
+ });
3024
3054
  var $ZodReadonly = /*@__PURE__*/ $constructor("$ZodReadonly", (inst, def) => {
3025
3055
  $ZodType.init(inst, def);
3026
3056
  defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
@@ -5206,6 +5236,10 @@ function pipe(in_, out) {
5206
5236
  out
5207
5237
  });
5208
5238
  }
5239
+ var ZodPreprocess = /*@__PURE__*/ $constructor("ZodPreprocess", (inst, def) => {
5240
+ ZodPipe.init(inst, def);
5241
+ $ZodPreprocess.init(inst, def);
5242
+ });
5209
5243
  var ZodReadonly = /*@__PURE__*/ $constructor("ZodReadonly", (inst, def) => {
5210
5244
  $ZodReadonly.init(inst, def);
5211
5245
  ZodType.init(inst, def);
@@ -5264,6 +5298,13 @@ function _instanceof(cls, params = {}) {
5264
5298
  };
5265
5299
  return inst;
5266
5300
  }
5301
+ function preprocess(fn, schema) {
5302
+ return new ZodPreprocess({
5303
+ type: "pipe",
5304
+ in: transform(fn),
5305
+ out: schema
5306
+ });
5307
+ }
5267
5308
  //#endregion
5268
5309
  //#region ../../node_modules/zod/v4/classic/compat.js
5269
5310
  /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
@@ -10782,6 +10823,8 @@ var QueryFilterSchema = object({
10782
10823
  where: record(string(), unknown()).optional(),
10783
10824
  whereIn: record(string(), array(unknown())).optional(),
10784
10825
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
10826
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
10827
+ whereNot: record(string(), unknown()).optional(),
10785
10828
  orderBy: object({
10786
10829
  field: string(),
10787
10830
  direction: _enum(["asc", "desc"])
@@ -10801,7 +10844,8 @@ var QueryFilterSchema = object({
10801
10844
  var MutationFilterSchema = object({
10802
10845
  where: record(string(), unknown()).optional(),
10803
10846
  whereIn: record(string(), array(unknown())).optional(),
10804
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
10847
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
10848
+ whereNot: record(string(), unknown()).optional()
10805
10849
  });
10806
10850
  /** A single stored record: `{ id, data }`. */
10807
10851
  var SettingsRecordSchema = object({
@@ -12158,6 +12202,17 @@ var LlmImageSchema = object({
12158
12202
  bytes: _instanceof(Uint8Array),
12159
12203
  mimeType: string()
12160
12204
  });
12205
+ /**
12206
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
12207
+ * the flag is what a consumer table flips, the count is what the operator tunes.
12208
+ * A retry doubles the wall time of a call, so the two gates that run inside a
12209
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
12210
+ */
12211
+ var LlmRetryPolicySchema = object({
12212
+ enabled: boolean().default(false),
12213
+ /** Total attempts INCLUDING the first. 1 = no retry. */
12214
+ maxAttempts: number().int().min(1).max(5).default(1)
12215
+ });
12161
12216
  var LlmGenerateBaseInputSchema = object({
12162
12217
  /** Collection routing (the notification-output posture). */
12163
12218
  addonId: string().optional(),
@@ -12172,7 +12227,28 @@ var LlmGenerateBaseInputSchema = object({
12172
12227
  jsonSchema: record(string(), unknown()).optional(),
12173
12228
  /** Per-call override of the profile default. */
12174
12229
  maxTokens: number().int().positive().optional(),
12175
- temperature: number().optional()
12230
+ temperature: number().optional(),
12231
+ /** Per-call override of the profile default (nucleus sampling). */
12232
+ topP: number().min(0).max(1).optional(),
12233
+ /** Per-call override of the profile default (top-k sampling). */
12234
+ topK: number().int().positive().optional(),
12235
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
12236
+ timeoutMs: number().int().positive().optional(),
12237
+ /** Per-call override; beats both the consumer table and the profile. */
12238
+ retry: LlmRetryPolicySchema.optional(),
12239
+ /**
12240
+ * Caller-minted id that makes this generation CANCELLABLE.
12241
+ *
12242
+ * Without it a caller that stops waiting cannot stop the work: the gates race
12243
+ * the call against 8 s and free their own slot when the timer wins, while the
12244
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
12245
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
12246
+ * not generations, and the real load is unbounded.
12247
+ *
12248
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
12249
+ * `llm.cancel({ requestId })` tears the socket down.
12250
+ */
12251
+ requestId: string().optional()
12176
12252
  });
12177
12253
  /**
12178
12254
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -12185,6 +12261,18 @@ var LlmGenerateBaseInputSchema = object({
12185
12261
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
12186
12262
  * watchdog — operator decision #3).
12187
12263
  */
12264
+ /**
12265
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
12266
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
12267
+ * REF rather than looked up at install time, so what the operator approved in
12268
+ * the preview is exactly what the node downloads.
12269
+ */
12270
+ var ManagedModelExtraFileSchema = object({
12271
+ url: string(),
12272
+ filename: string(),
12273
+ sizeBytes: number(),
12274
+ sha256: string().optional()
12275
+ });
12188
12276
  var ManagedModelRefSchema = discriminatedUnion("kind", [
12189
12277
  object({
12190
12278
  kind: literal("catalog"),
@@ -12193,7 +12281,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
12193
12281
  object({
12194
12282
  kind: literal("url"),
12195
12283
  url: string(),
12196
- sha256: string().optional()
12284
+ sha256: string().optional(),
12285
+ /** Picker/status label; the file basename when absent. */
12286
+ label: string().optional(),
12287
+ sizeBytes: number().optional(),
12288
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
12197
12289
  }),
12198
12290
  object({
12199
12291
  kind: literal("path"),
@@ -12211,13 +12303,82 @@ var ManagedRuntimeConfigSchema = object({
12211
12303
  gpuLayers: number().int().default(0),
12212
12304
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
12213
12305
  threads: number().int().optional(),
12214
- /** Concurrent slots. */
12306
+ /** Concurrent slots (`--parallel`). */
12215
12307
  parallel: number().int().default(1),
12308
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
12309
+ batchSize: number().int().positive().optional(),
12310
+ /** Physical batch / micro-batch (`-ub`). */
12311
+ ubatchSize: number().int().positive().optional(),
12312
+ /**
12313
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
12314
+ * is a no-op elsewhere, so it is offered rather than assumed.
12315
+ */
12316
+ flashAttention: boolean().default(false),
12317
+ /**
12318
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
12319
+ * inference. Costs the full model size in resident memory — which is exactly
12320
+ * what the RAM budget is counting.
12321
+ */
12322
+ mlock: boolean().default(false),
12323
+ /**
12324
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
12325
+ * start, but avoids the page-fault stalls a network or spinning-disk model
12326
+ * store produces on every first token.
12327
+ */
12328
+ noMmap: boolean().default(false),
12329
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
12330
+ * cheapest way to fit a longer context in the same RAM. */
12331
+ cacheTypeK: _enum([
12332
+ "f32",
12333
+ "f16",
12334
+ "q8_0",
12335
+ "q5_1",
12336
+ "q5_0",
12337
+ "q4_1",
12338
+ "q4_0"
12339
+ ]).optional(),
12340
+ cacheTypeV: _enum([
12341
+ "f32",
12342
+ "f16",
12343
+ "q8_0",
12344
+ "q5_1",
12345
+ "q5_0",
12346
+ "q4_1",
12347
+ "q4_0"
12348
+ ]).optional(),
12349
+ /**
12350
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
12351
+ * (which most vision chat templates need and some language-only models
12352
+ * dislike), `--cont-batching`, `--rope-scaling`, …
12353
+ *
12354
+ * It is NOT a second place to set the flags above. A token that collides
12355
+ * with a typed field is REJECTED at start, naming the field that owns it
12356
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
12357
+ * the "two switches that disagree" failure this repo has already shipped
12358
+ * twice (D62).
12359
+ */
12360
+ extraArgs: array(string()).default([]),
12216
12361
  /** Else lazy: first generate boots it. */
12217
12362
  autoStart: boolean().default(false),
12218
12363
  /** 0 = never; frees RAM after quiet periods. */
12219
12364
  idleStopMinutes: number().int().default(30)
12220
12365
  });
12366
+ /**
12367
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
12368
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
12369
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
12370
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
12371
+ */
12372
+ var LlmDownloadProgressSchema = object({
12373
+ phase: _enum(["downloading", "verifying"]),
12374
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
12375
+ file: string(),
12376
+ fileIndex: number().int(),
12377
+ fileCount: number().int(),
12378
+ /** Across the WHOLE install, not the current file. */
12379
+ downloadedBytes: number(),
12380
+ totalBytes: number().optional()
12381
+ });
12221
12382
  var LlmRuntimeStatusSchema = object({
12222
12383
  /** Status is ALWAYS node-qualified. */
12223
12384
  nodeId: string(),
@@ -12234,6 +12395,8 @@ var LlmRuntimeStatusSchema = object({
12234
12395
  modelPath: string().optional(),
12235
12396
  modelId: string().optional(),
12236
12397
  downloadProgress: number().min(0).max(1).optional(),
12398
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
12399
+ download: LlmDownloadProgressSchema.optional(),
12237
12400
  lastError: string().optional(),
12238
12401
  crashesInWindow: number(),
12239
12402
  /** Child RSS (sampled best-effort). */
@@ -12244,7 +12407,14 @@ var LlmNodeModelSchema = object({
12244
12407
  file: string(),
12245
12408
  sizeBytes: number(),
12246
12409
  catalogId: string().optional(),
12247
- installedAt: number().optional()
12410
+ installedAt: number().optional(),
12411
+ /**
12412
+ * Absolute path on the node. Present so a file that is on disk but matches
12413
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
12414
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
12415
+ * it the picker could list such a file and do nothing with it.
12416
+ */
12417
+ path: string().optional()
12248
12418
  });
12249
12419
  var LlmRuntimeDiskUsageSchema = object({
12250
12420
  nodeId: string(),
@@ -12300,10 +12470,47 @@ var LlmProfileSchema = object({
12300
12470
  baseUrl: string().optional(),
12301
12471
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
12302
12472
  apiKey: string().optional(),
12473
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
12474
+ * degraded to text — that shipped once and produced a confident answer to a
12475
+ * question about a picture nobody sent. */
12303
12476
  supportsVision: boolean(),
12304
12477
  temperature: number().min(0).max(2).optional(),
12478
+ /** Nucleus sampling. Every wire we speak has it. */
12479
+ topP: number().min(0).max(1).optional(),
12480
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
12481
+ * wire does, and the client drops it there (measured: the request body gets
12482
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
12483
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
12484
+ topK: number().int().positive().optional(),
12305
12485
  maxTokens: number().int().positive().optional(),
12486
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
12487
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
12488
+ * the model with, so it is the one field that changes a PROCESS. */
12489
+ contextLength: number().int().positive().optional(),
12490
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
12491
+ * two system prompts fighting is worse than either alone). */
12492
+ systemPrompt: string().optional(),
12493
+ /** Total generation bound — the only one a unary call has. */
12306
12494
  timeoutMs: number().int().positive().default(6e4),
12495
+ /** The TCP handshake only — "is the port even open". NOT the wait for
12496
+ * response headers: on the LM Studio / llama-server wire those are written
12497
+ * once the model has finished loading, so they belong to the bound below. */
12498
+ connectTimeoutMs: number().int().positive().default(1e4),
12499
+ /** Accepted, but no output yet — response headers included, because a cold
12500
+ * GPU load is exactly what happens before them. */
12501
+ firstTokenTimeoutMs: number().int().positive().default(12e4),
12502
+ /** Output started then stopped. */
12503
+ idleTimeoutMs: number().int().positive().default(6e4),
12504
+ /** Profile-level default. The per-consumer table and a per-call override
12505
+ * both beat it — see `resolveRetryPolicy`. */
12506
+ retry: LlmRetryPolicySchema.default({
12507
+ enabled: false,
12508
+ maxAttempts: 1
12509
+ }),
12510
+ /** Whether this profile may use tools. The tool-call plumbing rides the
12511
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
12512
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
12513
+ toolsEnabled: boolean().default(false),
12307
12514
  extraHeaders: record(string(), string()).optional(),
12308
12515
  /** kind === 'managed-local' only (spec §4). */
12309
12516
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -12353,6 +12560,36 @@ var ManagedModelCatalogEntrySchema = object({
12353
12560
  /** Vision models: companion projector file. */
12354
12561
  mmprojUrl: string().optional()
12355
12562
  });
12563
+ /**
12564
+ * The outcome of turning one operator-typed Hugging Face reference into a
12565
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
12566
+ * I will not pick for you" is a normal answer the UI has to render, not an
12567
+ * exception.
12568
+ *
12569
+ * `candidates` is the whole reason the refusal is usable — every string in it
12570
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
12571
+ */
12572
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
12573
+ ok: literal(true),
12574
+ /** Ready to hand to `installModel` unchanged. */
12575
+ model: ManagedModelRefSchema,
12576
+ label: string(),
12577
+ repo: string(),
12578
+ quantization: string(),
12579
+ purpose: _enum(["text", "vision"]),
12580
+ totalBytes: number(),
12581
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
12582
+ * see that 0.9 GB of it is a projector they did not name. */
12583
+ extraFilenames: array(string())
12584
+ }), object({
12585
+ ok: literal(false),
12586
+ code: string(),
12587
+ message: string(),
12588
+ candidates: array(string()).optional(),
12589
+ /** Set when the refusal was only the ceiling: re-calling with
12590
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
12591
+ requiredBytes: number().optional()
12592
+ })]);
12356
12593
  var LlmRuntimeNodeSchema = object({
12357
12594
  nodeId: string(),
12358
12595
  reachable: boolean(),
@@ -12365,7 +12602,10 @@ var ProfileRefInputSchema = object({
12365
12602
  addonId: string(),
12366
12603
  profileId: string()
12367
12604
  });
12368
- method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12605
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
12606
+ addonId: string().optional(),
12607
+ requestId: string()
12608
+ }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12369
12609
  kind: "mutation",
12370
12610
  auth: "admin"
12371
12611
  }), method(ProfileRefInputSchema, _void(), {
@@ -12386,6 +12626,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
12386
12626
  consumer: string().optional(),
12387
12627
  profileId: string().optional()
12388
12628
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
12629
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
12630
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
12631
+ ref: string(),
12632
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
12633
+ maxBytes: number().positive().optional()
12634
+ }), HfModelResolutionSchema, {
12635
+ kind: "mutation",
12636
+ auth: "admin"
12637
+ }), method(object({
12389
12638
  nodeId: string(),
12390
12639
  model: ManagedModelRefSchema
12391
12640
  }), _void(), {
@@ -12945,11 +13194,33 @@ var NotificationFormatSchema = _enum([
12945
13194
  * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
12946
13195
  * renderer's icon set; "acknowledge" survives an adapter that draws it
12947
13196
  * differently.
13197
+ *
13198
+ * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
13199
+ *
13200
+ * These names are for US. **No adapter may forward one verbatim.** Each maps
13201
+ * the whole set onto its own renderer's vocabulary through a
13202
+ * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
13203
+ * fallback, so adding a member here fails every adapter's build until someone
13204
+ * decides its glyph, which is the only place that decision can be made
13205
+ * honestly.
13206
+ *
13207
+ * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
13208
+ * `disarm` straight through; iOS feeds that string to
13209
+ * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
13210
+ * and every snooze and alarm button arrived BLANK. A pass-through is not a
13211
+ * mapping, and "the field is documented" is not "the value renders".
13212
+ *
13213
+ * Adding a member is TRAIN-BOUND. The enum lives in the published
13214
+ * `@camstack/server` closure and the cap seam validates against the HUB's copy,
13215
+ * so an addon that emits a token the running hub does not know does not lose an
13216
+ * icon — its whole `send` fails Zod validation and the notification never
13217
+ * arrives. Never emit a new token from an addon before the train carrying it.
12948
13218
  */
12949
13219
  var NotificationActionIconSchema = _enum([
12950
13220
  "acknowledge",
12951
13221
  "dismiss",
12952
13222
  "silence",
13223
+ "snooze",
12953
13224
  "view",
12954
13225
  "play",
12955
13226
  "open",
@@ -12957,9 +13228,13 @@ var NotificationActionIconSchema = _enum([
12957
13228
  "lock",
12958
13229
  "unlock",
12959
13230
  "arm",
13231
+ "arm-home",
13232
+ "arm-away",
13233
+ "arm-night",
12960
13234
  "disarm",
12961
13235
  "light",
12962
- "alert"
13236
+ "alert",
13237
+ "camera"
12963
13238
  ]);
12964
13239
  /** A single tap-through action button. */
12965
13240
  var NotificationActionSchema = object({
@@ -13159,6 +13434,24 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
13159
13434
  targetId: string(),
13160
13435
  enabled: boolean()
13161
13436
  }), _void(), { kind: "mutation" });
13437
+ new Set([
13438
+ {
13439
+ id: "person",
13440
+ name: "Person"
13441
+ },
13442
+ {
13443
+ id: "vehicle",
13444
+ name: "Vehicle"
13445
+ },
13446
+ {
13447
+ id: "animal",
13448
+ name: "Animal"
13449
+ },
13450
+ {
13451
+ id: "package",
13452
+ name: "Package"
13453
+ }
13454
+ ].map((l) => l.id));
13162
13455
  var COCO_TO_MACRO = {
13163
13456
  mapping: {
13164
13457
  person: "person",
@@ -13951,11 +14244,15 @@ var NcSystemEventKindSchema = _enum([
13951
14244
  "stream-offline",
13952
14245
  "node-online",
13953
14246
  "node-offline",
14247
+ "node-inference-unavailable",
14248
+ "detection-blind",
13954
14249
  "addon-update-available",
13955
14250
  "server-update-available",
13956
14251
  "alarm-triggered",
13957
14252
  "alarm-armed",
13958
14253
  "alarm-disarmed",
14254
+ "alarm-arming",
14255
+ "alarm-arm-refused",
13959
14256
  "camera-online",
13960
14257
  "camera-offline",
13961
14258
  "camera-disabled",
@@ -14010,7 +14307,16 @@ var NcScheduleSchema = object({
14010
14307
  });
14011
14308
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
14012
14309
  var NcPlateMatcherSchema = object({
14013
- values: array(string().min(1)).min(1),
14310
+ /**
14311
+ * Plate texts (or gallery vehicle names) to match. EMPTY = **any plate the
14312
+ * pipeline could read** — the plate half of "no selection = no narrowing",
14313
+ * and the switch that says this rule is about vehicles that were IDENTIFIED
14314
+ * rather than merely seen. A subject carrying no plate still fails.
14315
+ *
14316
+ * The `.min(1)` this used to carry made that state unauthorable; nothing has
14317
+ * ever persisted an empty list, so widening it cannot change an existing rule.
14318
+ */
14319
+ values: array(string().min(1)),
14014
14320
  /** Max Levenshtein distance after normalization (uppercase alphanumeric). */
14015
14321
  maxDistance: number().int().min(0).max(3).default(1)
14016
14322
  });
@@ -14044,28 +14350,36 @@ var NcOccupancyConditionSchema = object({
14044
14350
  /**
14045
14351
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
14046
14352
  *
14047
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
14048
- * reference notifier uses, so an operator moving between them re-uses what
14049
- * they already know): a rule matches when, over a sampling window of
14050
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
14051
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
14052
- *
14053
- * - `dbThreshold` its level is at or above this many dBFS (see
14054
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
14055
- * - `labels` the classifier put at least one of these labels on it.
14056
- *
14057
- * Both are OPTIONAL and independent, which is the point of the shape: a
14058
- * loudness rule ("something loud at 3am") needs no model to be right, and a
14059
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
14060
- * is given** a window in which every sample is trivially a hit would fire on
14061
- * silence, so the engine refuses such a condition rather than notifying on
14062
- * nothing (the schema cannot express "at least one of" without becoming a
14063
- * ZodEffects the cap path would have to special-case).
14064
- *
14065
- * `hitPercent` is over the samples the window actually HOLDS, and the window
14066
- * must be FULL before it can match a window that has been open for two
14067
- * seconds of its ten is 100% of nothing, and firing on it would make
14068
- * `samplingSeconds` decorative.
14353
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
14354
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
14355
+ * there is no second switch that can disagree with the first and every rule
14356
+ * authored before the decision migrates for free (`audioModeOf`):
14357
+ *
14358
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14359
+ * classifier labels with one of them. No window, no percentage:
14360
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14361
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
14362
+ * the analyzer's (`classificationMinScore`, per device) — a label only
14363
+ * reaches this condition if the classifier was already confident enough.
14364
+ * - **LEVEL mode `dbThreshold` present, no labels.** The sampling window IS
14365
+ * the condition: at least `hitPercent`% of the samples over
14366
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
14367
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
14368
+ * must be FULL before it can match a window open for two of its ten
14369
+ * seconds is 100% of nothing.
14370
+ *
14371
+ * **Why label mode has no window.** It had one, and it never fired: the
14372
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
14373
+ * of them per episode, even through continuous crying. The measured maximum
14374
+ * `hitPercent` over the whole live history was 40 — under the shipped default
14375
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
14376
+ * the wrong question to ask of a sparse classifier.
14377
+ *
14378
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
14379
+ * and the rule would fire on silence. The schema cannot express "exactly one
14380
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
14381
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
14382
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
14069
14383
  *
14070
14384
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
14071
14385
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -14073,13 +14387,13 @@ var NcOccupancyConditionSchema = object({
14073
14387
  * an operator who typed `dog` mean the same thing.
14074
14388
  */
14075
14389
  var NcAudioConditionSchema = object({
14076
- /** Audio macro labels; absent = any sound (level-only rule). */
14390
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
14077
14391
  labels: array(string().min(1)).min(1).optional(),
14078
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
14392
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14079
14393
  dbThreshold: number().min(-96).max(0).optional(),
14080
- /** Percentage of the window's samples that must be hits (1–100). */
14394
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14081
14395
  hitPercent: number().int().min(1).max(100).default(60),
14082
- /** Length of the sampling window in seconds. */
14396
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14083
14397
  samplingSeconds: number().int().min(1).max(300).default(10)
14084
14398
  });
14085
14399
  /**
@@ -14217,13 +14531,81 @@ var NcRuleActionsSchema = object({
14217
14531
  */
14218
14532
  buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
14219
14533
  });
14534
+ /**
14535
+ * "This rule applies only while `deviceId` is in one of `states`."
14536
+ *
14537
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
14538
+ * `on`/`off` for a switch — not a normalised set, because normalising would
14539
+ * make the condition lie about devices whose states have no equivalent.
14540
+ *
14541
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
14542
+ * condition that fired on "I could not read it" would be worse than no gate.
14543
+ */
14544
+ var NcDeviceStateConditionSchema = object({
14545
+ deviceId: number().int(),
14546
+ /** Any of these matches. */
14547
+ states: array(string().min(1)).min(1)
14548
+ });
14549
+ /**
14550
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
14551
+ *
14552
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
14553
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
14554
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
14555
+ * already has a trigger ("tell me about a person at the front door, but only
14556
+ * while the bin is still out"). That is why it composes with every delivery
14557
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
14558
+ * kind exist for it — see D159.
14559
+ *
14560
+ * ── Identity ───────────────────────────────────────────────────────────────
14561
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
14562
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
14563
+ * carried as a HINT for the editor and for the log line, never as part of the
14564
+ * lookup key: a rule whose hint drifted must still gate correctly.
14565
+ *
14566
+ * ── Which boolean ──────────────────────────────────────────────────────────
14567
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
14568
+ * already declares which boolean drives notification rules, and a second knob
14569
+ * that could disagree with it is exactly the D62 failure. Set it only to
14570
+ * override one rule against the scene's own default.
14571
+ *
14572
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
14573
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
14574
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
14575
+ * evidence, in either direction.
14576
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
14577
+ * The latch is a durable fact about the past ("it has diverged since I armed
14578
+ * it"), so a camera that has gone dark does not clear it — that is the whole
14579
+ * reason the operator asked for a latch.
14580
+ *
14581
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
14582
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
14583
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
14584
+ * said out loud in the log rather than dropped in silence.
14585
+ */
14586
+ var NcSceneConditionSchema = object({
14587
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
14588
+ sceneId: string().min(1),
14589
+ /** The camera the scene lives on. A hint for the editor and the log line. */
14590
+ deviceId: number().int().optional(),
14591
+ /** The state the scene must be in for the rule to fire. */
14592
+ requiredState: _enum(["matched", "diverged"]),
14593
+ /**
14594
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
14595
+ * scene's own `emit` field, which is the only place that decision belongs.
14596
+ */
14597
+ latched: boolean().optional()
14598
+ });
14220
14599
  var NcConditionsSchema = object({
14221
14600
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
14222
- deviceState: object({
14223
- deviceId: number().int(),
14224
- /** Any of these matches. */
14225
- states: array(string().min(1)).min(1)
14226
- }).optional(),
14601
+ deviceState: NcDeviceStateConditionSchema.optional(),
14602
+ /**
14603
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
14604
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
14605
+ * unlike `occupancy`/`audio` it discriminates nothing. See
14606
+ * {@link NcSceneCondition} and D159.
14607
+ */
14608
+ scene: NcSceneConditionSchema.optional(),
14227
14609
  /** Device scope — absent = all devices. */
14228
14610
  devices: array(number()).optional(),
14229
14611
  /** Detector class names (any overlap with the record's class set). */
@@ -14249,18 +14631,47 @@ var NcConditionsSchema = object({
14249
14631
  */
14250
14632
  labelEquals: array(string().min(1)).optional(),
14251
14633
  /**
14252
- * Identity matcher. P1 boundary: matched against the record's collapsed
14253
- * `label` (the identity display name propagated by the face pipeline) —
14254
- * identity-ID matching rides in P2 when identity ids reach the record.
14634
+ * KNOWN FACES the rule's identity scope, and the switch that says the rule
14635
+ * is about recognised people at all.
14636
+ *
14637
+ * Three states, and the empty one is the point:
14638
+ *
14639
+ * | value | meaning |
14640
+ * | --- | --- |
14641
+ * | absent | the rule does not care who it is; an unrecognised person matches |
14642
+ * | `[]` | **only known faces** — any identity in the gallery, nobody in particular |
14643
+ * | a list | only these identities |
14644
+ *
14645
+ * `[]` is the repo-wide "no selection = no narrowing" reading (an absent
14646
+ * `devices` list is every device), applied one level down: the operator has
14647
+ * turned the face scope ON and narrowed it to nothing, which is every known
14648
+ * face. No second field states the same thing — a switch that can disagree
14649
+ * with the list under it is worse than no switch (D62).
14650
+ *
14651
+ * MEMBERS ARE FACE-GALLERY `Identity.id`s (uuid), not display names. A name is
14652
+ * renameable, and a rule authored on "Gianluca" went silently dark the moment
14653
+ * the operator fixed the spelling. The id reaches the record on
14654
+ * `LabelAttribution.identityId`; the name is what the editor shows and what
14655
+ * `{{label}}` renders.
14656
+ *
14657
+ * Rules written before this carry NAMES, and are resolved to ids lazily at
14658
+ * load (`NcRuleStore.load`) against the live gallery — a name nothing answers
14659
+ * for is left as it stands and reported, never dropped. The engine also
14660
+ * accepts a display-name hit as a compatibility leg, so a rule whose
14661
+ * migration could not resolve keeps matching exactly what it matched before.
14255
14662
  */
14256
14663
  identities: array(string().min(1)).optional(),
14257
- /** Fuzzy plate matcher against the record's `label` (plate text). */
14664
+ /**
14665
+ * KNOWN PLATES / VEHICLES — the plate mirror of {@link identities}, including
14666
+ * the empty-list reading: `values: []` is "any plate the OCR could read",
14667
+ * a non-empty list is those plates (fuzzily). See {@link NcPlateMatcherSchema}.
14668
+ */
14258
14669
  plates: NcPlateMatcherSchema.optional(),
14259
14670
  /**
14260
- * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics.
14261
- * Same P1 boundary: matched against the record's collapsed `label` (the
14262
- * identity display name). A record with NO label passes (nothing to
14263
- * exclude), unlike the include variant which fails on an absent label.
14671
+ * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics, and
14672
+ * the same id members and the same lazy name→id migration. A record with NO
14673
+ * identity passes (nothing to exclude), unlike the include variant which
14674
+ * fails on an unrecognised subject. An EMPTY list excludes nobody.
14264
14675
  */
14265
14676
  identitiesExclude: array(string().min(1)).optional(),
14266
14677
  /**
@@ -14652,7 +15063,80 @@ var NcRuleInputSchema = object({
14652
15063
  * a rule that predates the gate must keep delivering byte-for-byte as it
14653
15064
  * did, and absent is the only way to say that without a migration.
14654
15065
  */
14655
- confirm: NcConfirmSchema.optional()
15066
+ confirm: NcConfirmSchema.optional(),
15067
+ /**
15068
+ * WAIT for face/plate recognition before saying anything.
15069
+ *
15070
+ * A notification's TEXT is frozen at enqueue and its media is re-resolved at
15071
+ * send; the identity is neither. A face is confirmed after `confirmFrames`
15072
+ * agreeing observations — p50 **11.4 s** after the track was first seen,
15073
+ * measured on this hub — and an `immediate` rule enqueues on the first object
15074
+ * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
15075
+ * immediate path, and no amount of media re-resolution fixes a sentence.
15076
+ *
15077
+ * Only two honest answers exist, and this flag picks between them. It has
15078
+ * effect ONLY on a rule that declares a recognition scope
15079
+ * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
15080
+ * other rule there is nothing to wait for and the flag is inert.
15081
+ *
15082
+ * | value | what happens |
15083
+ * | --- | --- |
15084
+ * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
15085
+ * | absent / `false` | it fires at once WITHOUT the name, and if recognition lands before the track closes a SECOND, "…is Gianluca" notification follows (one per track, per rule, per target) |
15086
+ *
15087
+ * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
15088
+ * on the addon cap path, and absent has to keep meaning exactly what every
15089
+ * rule authored before this field meant.
15090
+ *
15091
+ * The cost of `true` is stated here because the editor states it too: a rule
15092
+ * that waits also inherits track-close SEMANTICS — its `zones` condition
15093
+ * tests every zone the track visited and a `crossing` condition can no longer
15094
+ * be satisfied, because a closed track carries no crossing.
15095
+ */
15096
+ waitForEnhancement: boolean().optional(),
15097
+ /**
15098
+ * GROUP a burst of subjects into ONE notification that grows.
15099
+ *
15100
+ * Seconds of quiet after the last matching subject before the burst is
15101
+ * considered over. While it is open, the first subject enqueues immediately —
15102
+ * **exactly as today, with no added latency** — and every real growth (a new
15103
+ * subject, or a name confirmed on one already in it) REPLACES that
15104
+ * notification with an updated one naming everybody. The push carries the
15105
+ * group's own coalescing tag, so the phone replaces rather than stacks.
15106
+ *
15107
+ * `0` / absent = off, and off is today's behaviour byte for byte.
15108
+ *
15109
+ * ### Why an idle cutoff and not a window
15110
+ *
15111
+ * The measured seven-person arrival on device 590 spans 110 s with every
15112
+ * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
15113
+ * idle cutoff holds it as one and ends it when the arrival actually ends.
15114
+ * 30 is Frigate's shipped value for the same decision.
15115
+ *
15116
+ * ### What it replaces
15117
+ *
15118
+ * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
15119
+ * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
15120
+ * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
15121
+ * track that did fire and 7 carrying a confirmed identity nobody heard about.
15122
+ * A group collapses the same volume by MERGING, so the cooldown becomes a
15123
+ * budget over GROUPS — which is what it always meant — and a growth is never
15124
+ * throttled by the window its own first member spent.
15125
+ *
15126
+ * ### Interaction with {@link waitForEnhancement}
15127
+ *
15128
+ * They compose, and the order matters. `waitForEnhancement` defers the rule to
15129
+ * TRACK CLOSE, so with both set the group is opened by the first member to
15130
+ * CLOSE — already carrying its name — and grows as later members close. That
15131
+ * is later, and complete. With grouping alone the group opens on the first
15132
+ * object event and picks up names as they are confirmed, through the growth
15133
+ * path. Neither combination fires twice for one subject.
15134
+ *
15135
+ * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
15136
+ * on the addon cap path, so absent must keep meaning what it meant before this
15137
+ * field existed.
15138
+ */
15139
+ groupIdleSec: number().int().min(0).max(600).optional()
14656
15140
  });
14657
15141
  /**
14658
15142
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -14759,6 +15243,7 @@ var NcConditionDescriptorSchema = object({
14759
15243
  "occupancy",
14760
15244
  "audio",
14761
15245
  "deviceState",
15246
+ "scene",
14762
15247
  "systemEvent"
14763
15248
  ]),
14764
15249
  operator: _enum([
@@ -15164,7 +15649,87 @@ var MethodAccessSchema = _enum([
15164
15649
  var AllowedProviderSchema = union([literal("*"), array(string())]);
15165
15650
  var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
15166
15651
  var CapScopeSchema = _enum(["device", "system"]);
15167
- var TokenScopeSchema = discriminatedUnion("type", [
15652
+ /**
15653
+ * DeviceSelector (scope model v3 — 2026-08-12).
15654
+ *
15655
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
15656
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
15657
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
15658
+ * AFTER the grant was minted — no re-grant, no re-login.
15659
+ *
15660
+ * - `all` — every device in the deployment. The broad viewer/operator
15661
+ * lever without a `category` grant (a `category` grant also covers device
15662
+ * caps that carry no deviceId; `all` is specifically the device set).
15663
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
15664
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
15665
+ * is NOT covered until the grant is edited.
15666
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
15667
+ * DYNAMIC. A device that changes type, or a new device of the type,
15668
+ * re-resolves on the next request.
15669
+ * - `locations` — every device whose operator-assigned `location` label is
15670
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
15671
+ * null/unset location matches NO `locations` selector.
15672
+ */
15673
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
15674
+ object({ kind: literal("all") }),
15675
+ object({
15676
+ kind: literal("ids"),
15677
+ ids: array(number().int()).min(1)
15678
+ }),
15679
+ object({
15680
+ kind: literal("types"),
15681
+ types: array(_enum(DeviceType)).min(1)
15682
+ }),
15683
+ object({
15684
+ kind: literal("locations"),
15685
+ locations: array(string().min(1)).min(1)
15686
+ })
15687
+ ]);
15688
+ var DeviceTokenScopeSchema = object({
15689
+ type: literal("device"),
15690
+ /** The device SET this grant covers — resolved against the live fleet. */
15691
+ selector: DeviceSelectorSchema,
15692
+ access: array(MethodAccessSchema).min(1),
15693
+ /**
15694
+ * Whether a grant on a PARENT device transparently covers its accessory
15695
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
15696
+ * Direction is parent → children ONLY.
15697
+ *
15698
+ * Absent → the matcher DERIVES it from the access flavour: `view`
15699
+ * inherits (a camera viewer sees the camera's accessories), `create` /
15700
+ * `delete` do NOT (actuating/removing a child is an explicit act the
15701
+ * operator must grant on the child, not inherit from the parent). Set it
15702
+ * explicitly to override that default per grant.
15703
+ */
15704
+ includeLinked: boolean().optional()
15705
+ });
15706
+ /**
15707
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
15708
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
15709
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
15710
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
15711
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
15712
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
15713
+ * migration cannot reach a JWT already in a client's hands; parse-time
15714
+ * migration covers both without a flag day. No cast — the raw object is read
15715
+ * through `Reflect.get` (its static type is `unknown`).
15716
+ */
15717
+ function migrateLegacyTokenScope(raw) {
15718
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
15719
+ if (Reflect.get(raw, "type") !== "device") return raw;
15720
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
15721
+ const targets = Reflect.get(raw, "targets");
15722
+ if (!Array.isArray(targets)) return raw;
15723
+ return {
15724
+ type: "device",
15725
+ selector: {
15726
+ kind: "ids",
15727
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
15728
+ },
15729
+ access: Reflect.get(raw, "access")
15730
+ };
15731
+ }
15732
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
15168
15733
  object({
15169
15734
  type: literal("category"),
15170
15735
  target: CapScopeSchema,
@@ -15180,18 +15745,8 @@ var TokenScopeSchema = discriminatedUnion("type", [
15180
15745
  target: string(),
15181
15746
  access: array(MethodAccessSchema).min(1)
15182
15747
  }),
15183
- object({
15184
- type: literal("device"),
15185
- /**
15186
- * One or more deviceIds (serialised as strings for wire-format
15187
- * consistency with the rest of the union). Matcher accepts if
15188
- * `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
15189
- * of one scope-per-device when granting access to a set of cameras.
15190
- */
15191
- targets: array(string()).min(1),
15192
- access: array(MethodAccessSchema).min(1)
15193
- })
15194
- ]);
15748
+ DeviceTokenScopeSchema
15749
+ ]));
15195
15750
  object({
15196
15751
  id: string(),
15197
15752
  username: string(),
@@ -15508,7 +16063,7 @@ var TrackEnvelopeSchema = object({
15508
16063
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
15509
16064
  * keeps every scalar the list surfaces actually render (ids, class(es),
15510
16065
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15511
- * zonesVisited, bestEventId, envelope, hasFace) and returns `positions` /
16066
+ * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
15512
16067
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15513
16068
  * `getTrack`. Mirrors the event-store `projection` convention
15514
16069
  * (`getObjectEvents` et al.).
@@ -15644,7 +16199,21 @@ union([literal(1), literal(2)]);
15644
16199
  var LabelAttributionSchema = object({
15645
16200
  stepId: string(),
15646
16201
  modelId: string().optional(),
15647
- decidedAt: number()
16202
+ decidedAt: number(),
16203
+ /**
16204
+ * The GALLERY id behind a recognised tier-2 label — a face-gallery
16205
+ * `Identity.id` or a plate-gallery `Vehicle.id` (both `randomUUID`).
16206
+ *
16207
+ * The text alone is a DISPLAY NAME, and a display name is renameable: a
16208
+ * notification rule authored on "Gianluca" stopped matching the moment the
16209
+ * operator fixed the spelling in the gallery, and nothing said so. The id is
16210
+ * the thing that does not move, so it is what a rule matches on
16211
+ * (`NcConditions.identities`) and the text is what a human is shown.
16212
+ *
16213
+ * Absent when the label names no gallery row — a plate the OCR read but no
16214
+ * vehicle claims, a sub-class, a species, any tier-1 value.
16215
+ */
16216
+ identityId: string().optional()
15648
16217
  });
15649
16218
  /**
15650
16219
  * The TIERED label model (roadmap 4g), spread into `TrackSchema` and
@@ -15781,6 +16350,28 @@ var TrackSchema = object({
15781
16350
  * `=== true` and render nothing otherwise, never infer "no face".
15782
16351
  */
15783
16352
  hasFace: boolean().optional(),
16353
+ /**
16354
+ * This subject CONTAINS a folded rider — a person the rider-pairing step
16355
+ * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16356
+ * so the passage is tracked once and as a VEHICLE.
16357
+ *
16358
+ * It exists because the fold's record was dishonest. D34 and the code both
16359
+ * said "the person is not lost — it is reported so both entities stay on the
16360
+ * record"; in fact the pair went into a per-processor RAM field behind an
16361
+ * accessor nobody called, and every durable surface said `vehicle`, full
16362
+ * stop. This is the composition note that makes the row true.
16363
+ *
16364
+ * A COMPOSITION, never a class and never a label. "This vehicle contains a
16365
+ * person" is not an answer to "what is this" — both label tiers would refuse
16366
+ * a macro token anyway (D89), and correctly. Nothing here changes what the
16367
+ * subject IS: a cyclist stays one vehicle track, occupancy still counts one,
16368
+ * and a `person` rule still does not fire for someone cycling past.
16369
+ *
16370
+ * **Absent ≠ false**, exactly like {@link hasFace}: every row written before
16371
+ * the column, and every hub that predates the field, omits it. Test
16372
+ * `=== true` and render nothing otherwise — never infer "no rider".
16373
+ */
16374
+ hasRider: boolean().optional(),
15784
16375
  ...TrackFlagFields,
15785
16376
  ...TrackRetrainFields
15786
16377
  });
@@ -16130,7 +16721,10 @@ var RecentTracksQueryInput = object({
16130
16721
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16131
16722
  cursor: string().optional(),
16132
16723
  /** See {@link TrackProjectionSchema}. Default `full`. */
16133
- projection: TrackProjectionSchema.optional()
16724
+ projection: TrackProjectionSchema.optional(),
16725
+ /** Include stationary-promoted rows (parked objects). Default false: the
16726
+ * feed lists passages; parking records live on the stationary registry. */
16727
+ includeStationary: boolean().optional()
16134
16728
  });
16135
16729
  var RecentTracksPageSchema = object({
16136
16730
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -16348,7 +16942,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16348
16942
  zone: TrackZoneFilterSchema.optional(),
16349
16943
  /** See {@link TrackProjectionSchema}. Default `full` (backward
16350
16944
  * compatible — omitting the field keeps today's exact behaviour). */
16351
- projection: TrackProjectionSchema.optional()
16945
+ projection: TrackProjectionSchema.optional(),
16946
+ /** Include stationary-promoted rows (parked objects handed to the
16947
+ * stationary registry). Default false: the timeline lists passages,
16948
+ * not parking records (operator decision, 2026-08-15). */
16949
+ includeStationary: boolean().optional()
16352
16950
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16353
16951
  kind: "mutation",
16354
16952
  auth: "admin"
@@ -16512,11 +17110,16 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16512
17110
  auth: "admin"
16513
17111
  }), method(object({
16514
17112
  eventId: string(),
16515
- kind: MediaFileKindEnum.optional()
17113
+ kind: MediaFileKindEnum.optional(),
17114
+ deviceId: number()
16516
17115
  }), array(MediaFileSchema).readonly()), method(object({
16517
17116
  trackId: string(),
16518
- kinds: array(MediaFileKindEnum).optional()
16519
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17117
+ kinds: array(MediaFileKindEnum).optional(),
17118
+ deviceId: number()
17119
+ }), array(MediaFileSchema).readonly()), method(object({
17120
+ trackId: string(),
17121
+ deviceId: number()
17122
+ }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
16520
17123
  kind: "mutation",
16521
17124
  auth: "admin"
16522
17125
  }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
@@ -17176,6 +17779,17 @@ var maxSessionHoldMsField = {
17176
17779
  default: 12e4,
17177
17780
  step: 5e3
17178
17781
  };
17782
+ /**
17783
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
17784
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
17785
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
17786
+ */
17787
+ var audioMotionWindowMsField = {
17788
+ min: 5e3,
17789
+ max: 6e5,
17790
+ default: 9e4,
17791
+ step: 5e3
17792
+ };
17179
17793
  var motionFpsField = {
17180
17794
  min: 1,
17181
17795
  max: 30,
@@ -17188,10 +17802,26 @@ var detectionFpsField = {
17188
17802
  default: 10,
17189
17803
  step: 1
17190
17804
  };
17805
+ /**
17806
+ * The occupancy re-check interval. DEFAULT 300 s (2026-08-13 — was 30 s).
17807
+ *
17808
+ * The recheck is now on by default (a parked car is invisible to occupancy
17809
+ * rules until the stationary registry has been rebuilt by motion, which after a
17810
+ * restart may be never on a quiet camera). Each cycle re-subscribes a detection
17811
+ * session — an RTSP re-dial — so the switch is only affordable at a WIDE
17812
+ * interval: 300 s is ~12 re-dials an hour per camera, against 120 at the old
17813
+ * 30 s. A parked car is therefore counted within 5 minutes of a restart.
17814
+ *
17815
+ * Why not wider: `max` is 300 and raising it is TRAIN-BOUND, not addon-bound —
17816
+ * the host validates `attachCamera` against ITS copy of this schema, so a
17817
+ * runner asked for 600 would be rejected by the hub until a `@camstack/server`
17818
+ * carrying the wider bound is installed everywhere. 300 is the widest value
17819
+ * that ships with an addon deploy.
17820
+ */
17191
17821
  var occupancyRecheckSecField = {
17192
17822
  min: 0,
17193
17823
  max: 300,
17194
- default: 30,
17824
+ default: 300,
17195
17825
  step: 5
17196
17826
  };
17197
17827
  var occupancyRecheckFramesField = {
@@ -17336,6 +17966,27 @@ var RunnerCameraConfigSchema = object({
17336
17966
  * resolved `CameraDetectionConfig`.
17337
17967
  */
17338
17968
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
17969
+ /**
17970
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
17971
+ * 'on-motion'` audio window, measured from the LAST motion event.
17972
+ *
17973
+ * This exists because the falling edge cannot be relied on. Camera-native
17974
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
17975
+ * its email-push SMTP path both emit `detected: true` and never the
17976
+ * counterpart); only the frame-diff analyzer emits falls. So on an
17977
+ * onboard-only camera a window that closed only on `detected: false` never
17978
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
17979
+ * battery camera, the one failure mode the mode exists to prevent.
17980
+ *
17981
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
17982
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
17983
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
17984
+ *
17985
+ * Not consumed by the runner: carried here so it shares the per-camera
17986
+ * device-settings surface with `motionCooldownMs`, exactly like
17987
+ * `maxSessionHoldMs`.
17988
+ */
17989
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
17339
17990
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
17340
17991
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
17341
17992
  motionStreamId: string(),
@@ -17389,15 +18040,21 @@ var RunnerCameraConfigSchema = object({
17389
18040
  */
17390
18041
  onboardMotionDrivesAnalyzer: boolean().default(true),
17391
18042
  /**
17392
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
17393
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec`
17394
- * this is off by default because the recheck re-subscribes a detection session
17395
- * every N seconds while `watching`, a major source of pull-decoder re-dial
17396
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
18043
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
18044
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
17397
18045
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
17398
18046
  * (and only render) when this is enabled.
17399
- */
17400
- occupancyRecheckEnabled: boolean().default(false),
18047
+ *
18048
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
18049
+ * recheck re-subscribes a detection session every N seconds while `watching`
18050
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
18051
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
18052
+ * object is counted only while the stationary registry holds it, and the
18053
+ * registry rebuilds from motion, so after a restart a parked car was invisible
18054
+ * to every occupancy rule until something moved in front of it. The churn is
18055
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
18056
+ */
18057
+ occupancyRecheckEnabled: boolean().default(true),
17401
18058
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
17402
18059
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
17403
18060
  /**
@@ -17425,7 +18082,7 @@ var RunnerCameraConfigSchema = object({
17425
18082
  */
17426
18083
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
17427
18084
  });
17428
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
18085
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
17429
18086
  /**
17430
18087
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
17431
18088
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -18441,7 +19098,16 @@ targets: array(object({
18441
19098
  /** A sleeping battery camera: the frame is deliberately stale and will
18442
19099
  * NOT refresh in the background. A surface should say so rather than
18443
19100
  * present it as current. */
18444
- sleeping: boolean()
19101
+ sleeping: boolean(),
19102
+ /** Current device state rendered over the cached frame. State images
19103
+ * remain authoritative even when their photographic background is
19104
+ * old; null means the link must carry a current camera frame. */
19105
+ stateReason: _enum([
19106
+ "disabled",
19107
+ "sleeping",
19108
+ "unreachable",
19109
+ "waking"
19110
+ ]).nullable()
18445
19111
  })));
18446
19112
  /**
18447
19113
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -20008,6 +20674,25 @@ var BatteryStatusSchema = object({
20008
20674
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
20009
20675
  lastUpdated: number(),
20010
20676
  /**
20677
+ * Ms epoch of the last time the device PROVED it was reachable — a
20678
+ * completed firmware round-trip, an observed wake, or an inbound push
20679
+ * (firmware event, email). `0`/absent = never since this slice was born.
20680
+ *
20681
+ * This is the ONLY input that separates "asleep" from "gone", and it is
20682
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
20683
+ * for the radio, because a poll that confirms reachability is the same
20684
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
20685
+ * single derivation every consumer must use; no surface computes its own.
20686
+ *
20687
+ * It is deliberately NOT a clock in the
20688
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
20689
+ * observation itself, and it is the only thing a 30-hour silence is
20690
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
20691
+ * Reolink provider) so a value that means "recently" cannot cost a
20692
+ * SQLite commit per round-trip.
20693
+ */
20694
+ lastContactAt: number().optional(),
20695
+ /**
20011
20696
  * True when the source is a BINARY low-battery indicator (HA
20012
20697
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
20013
20698
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -23545,7 +24230,7 @@ method(object({
23545
24230
  toMs: number()
23546
24231
  }), RecordingAvailabilitySchema, {
23547
24232
  kind: "query",
23548
- auth: "admin"
24233
+ auth: "protected"
23549
24234
  }), method(object({
23550
24235
  deviceId: number(),
23551
24236
  fromMs: number(),
@@ -23553,14 +24238,14 @@ method(object({
23553
24238
  tzOffsetMinutes: number()
23554
24239
  }), RecordingDaysSchema, {
23555
24240
  kind: "query",
23556
- auth: "admin"
24241
+ auth: "protected"
23557
24242
  }), method(object({
23558
24243
  deviceId: number(),
23559
24244
  fromMs: number(),
23560
24245
  toMs: number()
23561
24246
  }), RecordingManifestSchema, {
23562
24247
  kind: "query",
23563
- auth: "admin"
24248
+ auth: "protected"
23564
24249
  }), method(object({}), RecordingStorageUsageSchema, {
23565
24250
  kind: "query",
23566
24251
  auth: "admin"
@@ -23850,14 +24535,77 @@ method(object({
23850
24535
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
23851
24536
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
23852
24537
  *
23853
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
23854
- * derives the device-detail contribution; the provider carries NO hand-written
23855
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
23856
- * every hysteresis flip / availability change; consumers never poll.
23857
- */
23858
- /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
23859
- * be added without a wire break (matching falls back to any-condition refs). */
24538
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
24539
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
24540
+ * for the thing: a scene is a standing question about the property ("is the bin
24541
+ * still out"), and the operator's question is "which of my scenes have tripped",
24542
+ * across every camera at once — not "what does camera 617 think". Buried one
24543
+ * camera deep it also could not be found. The surface is now a top-level admin
24544
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
24545
+ * picks the camera inside the create flow, the same shape Events and Faces have.
24546
+ *
24547
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
24548
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
24549
+ * directions, so a registration nobody declares fails exactly as loudly as a
24550
+ * declaration nobody registers. The editor is imported directly by the page.
24551
+ *
24552
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
24553
+ * availability change; consumers never poll.
24554
+ */
24555
+ /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
24556
+ * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
24557
+ * Open by design so more can be added without a wire break.
24558
+ *
24559
+ * Matching does NOT fall back across conditions: cross-condition cosines are
24560
+ * not comparable, so "I have never seen this scene in this light" is reported
24561
+ * as `unknown`, never guessed. A day reference scored against an IR frame
24562
+ * collapses the cosine and would latch a false alarm every single night. */
23860
24563
  var SceneConditionSchema = string();
24564
+ /**
24565
+ * What a scene does when the CURRENT light has no reference of its own.
24566
+ *
24567
+ * The lighting variants are not equally likely to exist. Almost every operator
24568
+ * captures daylight and then never stands outside at 22:00 to capture IR, and a
24569
+ * scene that is only ever going to be asked about a daytime question ("is the
24570
+ * bin still on the kerb at 08:00") does not need a night reference at all. The
24571
+ * night half must therefore be OPTIONAL, and optional means the scene keeps
24572
+ * working without it rather than degrading into a permanent complaint.
24573
+ *
24574
+ * - `skip` (default) — the check in that light is not made. Not a verdict, not
24575
+ * an alarm, not even an `unknown`: the live state simply stays whatever the
24576
+ * last covered light left it at, the latch is untouched, and the hysteresis
24577
+ * run is neither spent nor cleared. The scene resumes by itself at first
24578
+ * light. This is the only behaviour under which "I never captured IR" is a
24579
+ * configuration choice instead of a nightly fault.
24580
+ * - `judge-anyway` — score against the OTHER conditions' references. Available
24581
+ * for cameras whose IR frame is close enough to daylight (a floodlit
24582
+ * driveway, an always-white-light doorbell), and wrong for everything else:
24583
+ * cross-condition cosines are not comparable, so a day reference against a
24584
+ * true IR frame collapses and the scene reports a theft at 21:40.
24585
+ *
24586
+ * Never applies when the scene has NO comparable reference at all — that is
24587
+ * "not armed yet", it is reported as `no-reference-for-condition`, and silence
24588
+ * there would hide a scene the operator never finished setting up.
24589
+ */
24590
+ var SceneUncoveredPolicySchema = _enum(["skip", "judge-anyway"]);
24591
+ /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
24592
+ * `unknown` = we cannot judge (no reference for this condition, encoder model
24593
+ * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
24594
+ * and never counts toward hysteresis in either direction. */
24595
+ var SceneVerdictSchema = _enum([
24596
+ "matched",
24597
+ "diverged",
24598
+ "unknown"
24599
+ ]);
24600
+ /** Why a scene cannot judge. Named, because this feature's failure mode is
24601
+ * silence that reads as "nothing has happened". */
24602
+ var SceneUnavailableSchema = _enum([
24603
+ "no-reference-for-condition",
24604
+ "view-shifted",
24605
+ "no-vision-profile",
24606
+ "encoder-model-changed",
24607
+ "no-snapshot"
24608
+ ]);
23861
24609
  /** One captured reference — condition-tagged, model-version-gated. `embedding`
23862
24610
  * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
23863
24611
  var SceneReferenceSchema = object({
@@ -23865,7 +24613,14 @@ var SceneReferenceSchema = object({
23865
24613
  modelId: string(),
23866
24614
  condition: SceneConditionSchema,
23867
24615
  capturedAt: number(),
23868
- thumbnailMediaId: string().optional()
24616
+ thumbnailMediaId: string().optional(),
24617
+ /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
24618
+ * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
24619
+ * normalized rect frame a different piece of world, and the scene would
24620
+ * diverge forever with a perfectly plausible cosine. Checked LAZILY, only
24621
+ * when hysteresis is about to flip — one extra encode per candidate
24622
+ * transition, not per poll. */
24623
+ anchorEmbedding: array(number()).optional()
23869
24624
  });
23870
24625
  var SceneMonitorStateSchema = object({
23871
24626
  id: string(),
@@ -23887,6 +24642,28 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
23887
24642
  profileId: string().optional(),
23888
24643
  hysteresisCount: number().int().positive()
23889
24644
  })]);
24645
+ var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
24646
+ /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
24647
+ * out in silence rather than reporting a fault every night. */
24648
+ var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
24649
+ /**
24650
+ * Vision-model adjudication of a candidate flip. Field names deliberately
24651
+ * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
24652
+ *
24653
+ * `onTimeout` defaults to **'hold'**, the OPPOSITE of `NcConfirmGate`'s
24654
+ * fail-open: a notification suppressed is the worse error there, but a vision
24655
+ * model that timed out has not told us the bin is gone, and a latch is a
24656
+ * stateful claim that costs the operator a trip to reset.
24657
+ */
24658
+ var SceneConfirmSchema = object({
24659
+ enabled: boolean().default(false),
24660
+ prompt: string().min(1).max(1e3),
24661
+ profileId: string().optional(),
24662
+ timeoutMs: number().int().min(1e3).max(2e4).default(8e3),
24663
+ maxImagePx: number().int().min(64).max(2048).default(448),
24664
+ /** What a timeout / unavailable model means for the PENDING flip. */
24665
+ onTimeout: _enum(["flip", "hold"]).default("hold")
24666
+ });
23890
24667
  var SceneMonitorSchema = object({
23891
24668
  id: string(),
23892
24669
  label: string(),
@@ -23905,7 +24682,56 @@ var SceneMonitorSchema = object({
23905
24682
  lastConfidence: number().nullable(),
23906
24683
  currentCondition: SceneConditionSchema.nullable(),
23907
24684
  availability: _enum(["ok", "unavailable"]),
23908
- unavailableReason: string().nullable()
24685
+ unavailableReason: string().nullable(),
24686
+ /** Which state is "the initial screen". `null` until the first capture. */
24687
+ baselineStateId: string().nullable(),
24688
+ /** Which boolean drives notification rules and any export. */
24689
+ emit: _enum(["latched", "live"]).default("latched"),
24690
+ /** Live: does the region match the baseline RIGHT NOW. */
24691
+ verdict: SceneVerdictSchema,
24692
+ /** Has it been `diverged` at least once since `armedAt` — the operator's boolean. */
24693
+ latched: boolean(),
24694
+ /** Last reset (or creation). */
24695
+ armedAt: number(),
24696
+ divergedAt: number().nullable(),
24697
+ restoredAt: number().nullable(),
24698
+ /** A check is only COUNTED when the device has been quiet this long. Motion
24699
+ * during the window DISCARDS the observation — a car pulling up in front of
24700
+ * the bin must not be able to spend hysteresis credit. */
24701
+ quietSeconds: number().int().min(0).max(3600).default(60),
24702
+ /** An observation only advances the pending count when it is at least this
24703
+ * far from the previously counted one, so N agreeing checks span real time
24704
+ * rather than N adjacent polls inside one occlusion. */
24705
+ minObservationSpacingSec: number().int().min(0).max(3600).default(120),
24706
+ /** Vision-model adjudication of a candidate flip. Similarity primary only. */
24707
+ confirm: SceneConfirmSchema.optional(),
24708
+ /** Whole-frame anchor cosine below which a flip is REFUSED as `view-shifted`. */
24709
+ anchorThreshold: number().min(0).max(1).default(SCENE_DEFAULT_ANCHOR_THRESHOLD),
24710
+ /** Clear the latch on its own when the scene matches again? Default false —
24711
+ * `restoredAt` and the `scene-restored` edge are recorded regardless, so an
24712
+ * automation can react to the bin coming back without the operator's own
24713
+ * alarm silently clearing itself. */
24714
+ autoRestore: boolean().default(false),
24715
+ /** What to do when the current light has no reference of its own. See
24716
+ * {@link SceneUncoveredPolicySchema} — the default makes night OPTIONAL. */
24717
+ onUncoveredCondition: SceneUncoveredPolicySchema.default(SCENE_DEFAULT_UNCOVERED_POLICY),
24718
+ /**
24719
+ * The light whose checks are currently being SAT OUT under
24720
+ * `onUncoveredCondition: 'skip'` — `null` when the scene is checking normally.
24721
+ *
24722
+ * Engine-reported and advisory only: it moves no verdict, no latch and no
24723
+ * hysteresis. It exists so the card can say *"night (IR) — checks paused,
24724
+ * nothing captured in this light"* in the same calm voice as the coverage
24725
+ * line, because the alternative is a scene that silently stops answering
24726
+ * after sunset with nothing anywhere saying why. A skipped check must never
24727
+ * read as a broken one.
24728
+ */
24729
+ suspendedCondition: SceneConditionSchema.nullable().default(null),
24730
+ /** Named cause when `verdict === 'unknown'`. */
24731
+ unavailable: SceneUnavailableSchema.nullable(),
24732
+ /** Conditions that have at least one comparable reference — the coverage line
24733
+ * ("day ✓ · ir ✓ · dusk ✗") that turns a silent fallback into a visible fact. */
24734
+ coveredConditions: array(SceneConditionSchema)
23909
24735
  });
23910
24736
  var SceneMonitorStatusSchema = object({
23911
24737
  monitors: array(SceneMonitorSchema),
@@ -23938,7 +24764,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
23938
24764
  "both"
23939
24765
  ]).optional(),
23940
24766
  checkIntervalSec: number().optional(),
23941
- check: SceneCheckSchema.optional()
24767
+ check: SceneCheckSchema.optional(),
24768
+ emit: _enum(["latched", "live"]).optional(),
24769
+ quietSeconds: number().int().min(0).max(3600).optional(),
24770
+ minObservationSpacingSec: number().int().min(0).max(3600).optional(),
24771
+ anchorThreshold: number().min(0).max(1).optional(),
24772
+ autoRestore: boolean().optional(),
24773
+ onUncoveredCondition: SceneUncoveredPolicySchema.optional(),
24774
+ /** `null` clears the vision-model adjudicator. */
24775
+ confirm: SceneConfirmSchema.nullable().optional()
23942
24776
  })
23943
24777
  }), _void(), {
23944
24778
  kind: "mutation",
@@ -23975,6 +24809,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
23975
24809
  }), _void(), {
23976
24810
  kind: "mutation",
23977
24811
  auth: "admin"
24812
+ }), method(object({
24813
+ deviceId: number(),
24814
+ monitorId: string(),
24815
+ /** Defaults to TRUE at the provider seam — see `SCENE_RESET_RECAPTURES`. */
24816
+ recapture: boolean().optional()
24817
+ }), _void(), {
24818
+ kind: "mutation",
24819
+ auth: "admin"
23978
24820
  });
23979
24821
  /**
23980
24822
  * Per-stage gating mode applied to the zones a rule references.
@@ -24128,6 +24970,16 @@ var CamStreamDescriptorSchema = object({
24128
24970
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
24129
24971
  metadata: record(string(), unknown()).optional()
24130
24972
  });
24973
+ object({
24974
+ /** The descriptors as last built from a real camera response. Never a guess:
24975
+ * a failed or refused build writes NOTHING, so a restored catalog is always
24976
+ * one the camera itself once produced. */
24977
+ descriptors: array(CamStreamDescriptorSchema),
24978
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
24979
+ * path decide whether the camera's own awake window is worth spending on a
24980
+ * re-read. */
24981
+ lastFetchedAt: number()
24982
+ });
24131
24983
  DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
24132
24984
  /** One of the camera's stream profiles. */
24133
24985
  var StreamProfileSchema = _enum([
@@ -24283,12 +25135,64 @@ var NetworkAddressSchema = object({
24283
25135
  family: string(),
24284
25136
  internal: boolean()
24285
25137
  });
25138
+ /**
25139
+ * Provenance of the site coordinates, and the whole reason this is not just two
25140
+ * numbers.
25141
+ *
25142
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
25143
+ * nothing overwrites it.
25144
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
25145
+ * default that is right to a few kilometres beats the coarse UTC clock split
25146
+ * the sun-times consumers otherwise fall back to.
25147
+ *
25148
+ * The UI shows which one it is. An operator who cannot tell a guess from their
25149
+ * own input will eventually trust the guess.
25150
+ */
25151
+ var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
25152
+ /**
25153
+ * The read shape: the location plus the honest state of the one-shot derivation.
25154
+ *
25155
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
25156
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
25157
+ * failed; the hub will NOT try again on its own — the fallback is declared
25158
+ * (consumers degrade to their own last resort) and the operator either types the
25159
+ * coordinates or presses detect.
25160
+ */
25161
+ var SiteLocationStatusSchema = object({
25162
+ location: object({
25163
+ /** WGS84 decimal degrees. */
25164
+ latitude: number().min(-90).max(90),
25165
+ longitude: number().min(-180).max(180),
25166
+ source: SiteLocationSourceSchema,
25167
+ /** Epoch ms the value was last written. */
25168
+ updatedAt: number(),
25169
+ /**
25170
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
25171
+ * only — never parsed, never matched on. Absent for an operator-typed value.
25172
+ */
25173
+ label: string().optional()
25174
+ }).nullable(),
25175
+ derivationAttemptedAt: number().nullable(),
25176
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
25177
+ derivationError: string().nullable()
25178
+ });
25179
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
25180
+ var SetSiteLocationInputSchema = object({
25181
+ latitude: number().min(-90).max(90),
25182
+ longitude: number().min(-180).max(180)
25183
+ }).nullable();
24286
25184
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
24287
25185
  kind: "mutation",
24288
25186
  auth: "admin"
24289
25187
  }), method(_void(), _void(), {
24290
25188
  kind: "mutation",
24291
25189
  auth: "admin"
25190
+ }), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
25191
+ kind: "mutation",
25192
+ auth: "admin"
25193
+ }), method(_void(), SiteLocationStatusSchema, {
25194
+ kind: "mutation",
25195
+ auth: "admin"
24292
25196
  });
24293
25197
  object({
24294
25198
  /** True when the device's tamper switch / case-open contact is
@@ -27018,6 +27922,12 @@ Object.freeze({
27018
27922
  addonId: null,
27019
27923
  access: "create"
27020
27924
  },
27925
+ "llm.cancel": {
27926
+ capName: "llm",
27927
+ capScope: "system",
27928
+ addonId: null,
27929
+ access: "create"
27930
+ },
27021
27931
  "llm.deleteModel": {
27022
27932
  capName: "llm",
27023
27933
  capScope: "system",
@@ -27102,6 +28012,12 @@ Object.freeze({
27102
28012
  addonId: null,
27103
28013
  access: "view"
27104
28014
  },
28015
+ "llm.resolveModelRef": {
28016
+ capName: "llm",
28017
+ capScope: "system",
28018
+ addonId: null,
28019
+ access: "create"
28020
+ },
27105
28021
  "llm.setDefault": {
27106
28022
  capName: "llm",
27107
28023
  capScope: "system",
@@ -29268,6 +30184,12 @@ Object.freeze({
29268
30184
  addonId: null,
29269
30185
  access: "create"
29270
30186
  },
30187
+ "sceneMonitor.resetScene": {
30188
+ capName: "scene-monitor",
30189
+ capScope: "device",
30190
+ addonId: null,
30191
+ access: "delete"
30192
+ },
29271
30193
  "sceneMonitor.updateScene": {
29272
30194
  capName: "scene-monitor",
29273
30195
  capScope: "device",
@@ -29946,6 +30868,12 @@ Object.freeze({
29946
30868
  addonId: null,
29947
30869
  access: "create"
29948
30870
  },
30871
+ "system.detectSiteLocation": {
30872
+ capName: "system",
30873
+ capScope: "system",
30874
+ addonId: null,
30875
+ access: "create"
30876
+ },
29949
30877
  "system.featureFlags": {
29950
30878
  capName: "system",
29951
30879
  capScope: "system",
@@ -29964,6 +30892,12 @@ Object.freeze({
29964
30892
  addonId: null,
29965
30893
  access: "view"
29966
30894
  },
30895
+ "system.getSiteLocation": {
30896
+ capName: "system",
30897
+ capScope: "system",
30898
+ addonId: null,
30899
+ access: "view"
30900
+ },
29967
30901
  "system.health": {
29968
30902
  capName: "system",
29969
30903
  capScope: "system",
@@ -29988,6 +30922,12 @@ Object.freeze({
29988
30922
  addonId: null,
29989
30923
  access: "create"
29990
30924
  },
30925
+ "system.setSiteLocation": {
30926
+ capName: "system",
30927
+ capScope: "system",
30928
+ addonId: null,
30929
+ access: "create"
30930
+ },
29991
30931
  "terminalSession.adoptLegacyMonitor": {
29992
30932
  capName: "terminal-session",
29993
30933
  capScope: "system",
@@ -30559,6 +31499,1704 @@ Object.freeze({
30559
31499
  access: "create"
30560
31500
  }
30561
31501
  });
31502
+ Object.freeze({
31503
+ "accessories.setChildHidden": [{
31504
+ name: "childDeviceId",
31505
+ form: "single",
31506
+ optional: false
31507
+ }, {
31508
+ name: "deviceId",
31509
+ form: "single",
31510
+ optional: false
31511
+ }],
31512
+ "addonSettings.getDeviceSettings": [{
31513
+ name: "deviceId",
31514
+ form: "single",
31515
+ optional: false
31516
+ }],
31517
+ "addonSettings.updateDeviceSettings": [{
31518
+ name: "deviceId",
31519
+ form: "single",
31520
+ optional: false
31521
+ }],
31522
+ "alarmPanel.arm": [{
31523
+ name: "deviceId",
31524
+ form: "single",
31525
+ optional: false
31526
+ }],
31527
+ "alarmPanel.disarm": [{
31528
+ name: "deviceId",
31529
+ form: "single",
31530
+ optional: false
31531
+ }],
31532
+ "alarmPanel.trigger": [{
31533
+ name: "deviceId",
31534
+ form: "single",
31535
+ optional: false
31536
+ }],
31537
+ "audioAnalysis.resolveDeviceSettings": [{
31538
+ name: "deviceId",
31539
+ form: "single",
31540
+ optional: false
31541
+ }],
31542
+ "audioAnalyzer.classify": [{
31543
+ name: "deviceId",
31544
+ form: "single",
31545
+ optional: true
31546
+ }],
31547
+ "audioMetrics.getCurrentSnapshot": [{
31548
+ name: "deviceId",
31549
+ form: "single",
31550
+ optional: false
31551
+ }],
31552
+ "audioMetrics.getHistory": [{
31553
+ name: "deviceId",
31554
+ form: "single",
31555
+ optional: false
31556
+ }],
31557
+ "automationControl.disable": [{
31558
+ name: "deviceId",
31559
+ form: "single",
31560
+ optional: false
31561
+ }],
31562
+ "automationControl.enable": [{
31563
+ name: "deviceId",
31564
+ form: "single",
31565
+ optional: false
31566
+ }],
31567
+ "automationControl.trigger": [{
31568
+ name: "deviceId",
31569
+ form: "single",
31570
+ optional: false
31571
+ }],
31572
+ "battery.wakeForStream": [{
31573
+ name: "deviceId",
31574
+ form: "single",
31575
+ optional: false
31576
+ }],
31577
+ "brightness.setBrightness": [{
31578
+ name: "deviceId",
31579
+ form: "single",
31580
+ optional: false
31581
+ }],
31582
+ "button.press": [{
31583
+ name: "deviceId",
31584
+ form: "single",
31585
+ optional: false
31586
+ }],
31587
+ "cameraCredentials.getCredentials": [{
31588
+ name: "deviceId",
31589
+ form: "single",
31590
+ optional: false
31591
+ }],
31592
+ "cameraStreams.getBrokerStreams": [{
31593
+ name: "deviceId",
31594
+ form: "single",
31595
+ optional: false
31596
+ }],
31597
+ "cameraStreams.getCameraStreams": [{
31598
+ name: "deviceId",
31599
+ form: "single",
31600
+ optional: false
31601
+ }],
31602
+ "cameraStreams.getProfileRtspEntries": [{
31603
+ name: "deviceId",
31604
+ form: "single",
31605
+ optional: false
31606
+ }],
31607
+ "cameraStreams.getRtspEntries": [{
31608
+ name: "deviceId",
31609
+ form: "single",
31610
+ optional: false
31611
+ }],
31612
+ "cameraStreams.pickStream": [{
31613
+ name: "deviceId",
31614
+ form: "single",
31615
+ optional: false
31616
+ }],
31617
+ "climateControl.setFanMode": [{
31618
+ name: "deviceId",
31619
+ form: "single",
31620
+ optional: false
31621
+ }],
31622
+ "climateControl.setMode": [{
31623
+ name: "deviceId",
31624
+ form: "single",
31625
+ optional: false
31626
+ }],
31627
+ "climateControl.setPreset": [{
31628
+ name: "deviceId",
31629
+ form: "single",
31630
+ optional: false
31631
+ }],
31632
+ "climateControl.setSwingHorizontal": [{
31633
+ name: "deviceId",
31634
+ form: "single",
31635
+ optional: false
31636
+ }],
31637
+ "climateControl.setSwingVertical": [{
31638
+ name: "deviceId",
31639
+ form: "single",
31640
+ optional: false
31641
+ }],
31642
+ "climateControl.setTarget": [{
31643
+ name: "deviceId",
31644
+ form: "single",
31645
+ optional: false
31646
+ }],
31647
+ "climateControl.setTargetHumidity": [{
31648
+ name: "deviceId",
31649
+ form: "single",
31650
+ optional: false
31651
+ }],
31652
+ "climateControl.setTargetRange": [{
31653
+ name: "deviceId",
31654
+ form: "single",
31655
+ optional: false
31656
+ }],
31657
+ "color.setColor": [{
31658
+ name: "deviceId",
31659
+ form: "single",
31660
+ optional: false
31661
+ }],
31662
+ "consumables.reset": [{
31663
+ name: "deviceId",
31664
+ form: "single",
31665
+ optional: false
31666
+ }],
31667
+ "control.setValue": [{
31668
+ name: "deviceId",
31669
+ form: "single",
31670
+ optional: false
31671
+ }],
31672
+ "cover.close": [{
31673
+ name: "deviceId",
31674
+ form: "single",
31675
+ optional: false
31676
+ }],
31677
+ "cover.open": [{
31678
+ name: "deviceId",
31679
+ form: "single",
31680
+ optional: false
31681
+ }],
31682
+ "cover.setPosition": [{
31683
+ name: "deviceId",
31684
+ form: "single",
31685
+ optional: false
31686
+ }],
31687
+ "cover.setTiltPosition": [{
31688
+ name: "deviceId",
31689
+ form: "single",
31690
+ optional: false
31691
+ }],
31692
+ "cover.stop": [{
31693
+ name: "deviceId",
31694
+ form: "single",
31695
+ optional: false
31696
+ }],
31697
+ "dayNight.getOptions": [{
31698
+ name: "deviceId",
31699
+ form: "single",
31700
+ optional: false
31701
+ }],
31702
+ "dayNight.setSettings": [{
31703
+ name: "deviceId",
31704
+ form: "single",
31705
+ optional: false
31706
+ }],
31707
+ "decoder.createSession": [{
31708
+ name: "deviceId",
31709
+ form: "single",
31710
+ optional: true
31711
+ }],
31712
+ "deviceAdoption.release": [{
31713
+ name: "camDeviceId",
31714
+ form: "single",
31715
+ optional: false
31716
+ }],
31717
+ "deviceAdoption.resync": [{
31718
+ name: "camDeviceId",
31719
+ form: "single",
31720
+ optional: false
31721
+ }],
31722
+ "deviceDiscovery.adoptDevice": [{
31723
+ name: "deviceId",
31724
+ form: "single",
31725
+ optional: false
31726
+ }],
31727
+ "deviceDiscovery.listDiscovered": [{
31728
+ name: "deviceId",
31729
+ form: "single",
31730
+ optional: false
31731
+ }],
31732
+ "deviceDiscovery.refreshDiscovery": [{
31733
+ name: "deviceId",
31734
+ form: "single",
31735
+ optional: false
31736
+ }],
31737
+ "deviceDiscovery.releaseDevice": [{
31738
+ name: "childDeviceId",
31739
+ form: "single",
31740
+ optional: false
31741
+ }, {
31742
+ name: "deviceId",
31743
+ form: "single",
31744
+ optional: false
31745
+ }],
31746
+ "deviceManager.adoptionRelease": [{
31747
+ name: "camDeviceId",
31748
+ form: "single",
31749
+ optional: false
31750
+ }],
31751
+ "deviceManager.adoptionResync": [{
31752
+ name: "camDeviceId",
31753
+ form: "single",
31754
+ optional: false
31755
+ }],
31756
+ "deviceManager.applyInitialMeta": [{
31757
+ name: "deviceId",
31758
+ form: "single",
31759
+ optional: false
31760
+ }, {
31761
+ name: "linkDeviceId",
31762
+ form: "single",
31763
+ optional: true
31764
+ }],
31765
+ "deviceManager.disable": [{
31766
+ name: "deviceId",
31767
+ form: "single",
31768
+ optional: false
31769
+ }],
31770
+ "deviceManager.enable": [{
31771
+ name: "deviceId",
31772
+ form: "single",
31773
+ optional: false
31774
+ }],
31775
+ "deviceManager.getBindings": [{
31776
+ name: "deviceId",
31777
+ form: "single",
31778
+ optional: false
31779
+ }],
31780
+ "deviceManager.getChildren": [{
31781
+ name: "parentDeviceId",
31782
+ form: "single",
31783
+ optional: false
31784
+ }],
31785
+ "deviceManager.getConfigSchema": [{
31786
+ name: "deviceId",
31787
+ form: "single",
31788
+ optional: false
31789
+ }],
31790
+ "deviceManager.getDevice": [{
31791
+ name: "deviceId",
31792
+ form: "single",
31793
+ optional: false
31794
+ }],
31795
+ "deviceManager.getDeviceAggregate": [{
31796
+ name: "deviceId",
31797
+ form: "single",
31798
+ optional: false
31799
+ }],
31800
+ "deviceManager.getDeviceLiveInfoAggregate": [{
31801
+ name: "deviceId",
31802
+ form: "single",
31803
+ optional: false
31804
+ }],
31805
+ "deviceManager.getDeviceSettingsAggregate": [{
31806
+ name: "deviceId",
31807
+ form: "single",
31808
+ optional: false
31809
+ }],
31810
+ "deviceManager.getDeviceStatusAggregate": [{
31811
+ name: "deviceId",
31812
+ form: "single",
31813
+ optional: false
31814
+ }],
31815
+ "deviceManager.getDeviceStatusAggregateBatch": [{
31816
+ name: "deviceIds",
31817
+ form: "array",
31818
+ optional: false
31819
+ }],
31820
+ "deviceManager.getLinkedDevices": [{
31821
+ name: "deviceId",
31822
+ form: "single",
31823
+ optional: false
31824
+ }],
31825
+ "deviceManager.getSettingsSchema": [{
31826
+ name: "deviceId",
31827
+ form: "single",
31828
+ optional: false
31829
+ }],
31830
+ "deviceManager.getStreamProfileMap": [{
31831
+ name: "deviceId",
31832
+ form: "single",
31833
+ optional: false
31834
+ }],
31835
+ "deviceManager.getStreamSources": [{
31836
+ name: "deviceId",
31837
+ form: "single",
31838
+ optional: false
31839
+ }],
31840
+ "deviceManager.getWireableFields": [{
31841
+ name: "deviceId",
31842
+ form: "single",
31843
+ optional: false
31844
+ }],
31845
+ "deviceManager.loadConfig": [{
31846
+ name: "deviceId",
31847
+ form: "single",
31848
+ optional: false
31849
+ }],
31850
+ "deviceManager.loadMeta": [{
31851
+ name: "deviceId",
31852
+ form: "single",
31853
+ optional: false
31854
+ }],
31855
+ "deviceManager.loadRuntimeState": [{
31856
+ name: "deviceId",
31857
+ form: "single",
31858
+ optional: false
31859
+ }],
31860
+ "deviceManager.persistConfig": [{
31861
+ name: "deviceId",
31862
+ form: "single",
31863
+ optional: false
31864
+ }],
31865
+ "deviceManager.probeStreams": [{
31866
+ name: "deviceId",
31867
+ form: "single",
31868
+ optional: false
31869
+ }],
31870
+ "deviceManager.registerDevice": [{
31871
+ name: "parentDeviceId",
31872
+ form: "single",
31873
+ optional: true
31874
+ }],
31875
+ "deviceManager.remove": [{
31876
+ name: "deviceId",
31877
+ form: "single",
31878
+ optional: false
31879
+ }],
31880
+ "deviceManager.removeDevice": [{
31881
+ name: "deviceId",
31882
+ form: "single",
31883
+ optional: false
31884
+ }],
31885
+ "deviceManager.runDeviceAction": [{
31886
+ name: "deviceId",
31887
+ form: "single",
31888
+ optional: false
31889
+ }],
31890
+ "deviceManager.setChildLayout": [{
31891
+ name: "deviceId",
31892
+ form: "single",
31893
+ optional: false
31894
+ }],
31895
+ "deviceManager.setDisabled": [{
31896
+ name: "deviceId",
31897
+ form: "single",
31898
+ optional: false
31899
+ }],
31900
+ "deviceManager.setDisplay": [{
31901
+ name: "deviceId",
31902
+ form: "single",
31903
+ optional: false
31904
+ }],
31905
+ "deviceManager.setIntegrationId": [{
31906
+ name: "deviceId",
31907
+ form: "single",
31908
+ optional: false
31909
+ }],
31910
+ "deviceManager.setLinkDeviceId": [{
31911
+ name: "deviceId",
31912
+ form: "single",
31913
+ optional: false
31914
+ }, {
31915
+ name: "linkDeviceId",
31916
+ form: "single",
31917
+ optional: true
31918
+ }],
31919
+ "deviceManager.setLocation": [{
31920
+ name: "deviceId",
31921
+ form: "single",
31922
+ optional: false
31923
+ }],
31924
+ "deviceManager.setMetadata": [{
31925
+ name: "deviceId",
31926
+ form: "single",
31927
+ optional: false
31928
+ }],
31929
+ "deviceManager.setName": [{
31930
+ name: "deviceId",
31931
+ form: "single",
31932
+ optional: false
31933
+ }],
31934
+ "deviceManager.setPrimaryChildEntityId": [{
31935
+ name: "deviceId",
31936
+ form: "single",
31937
+ optional: false
31938
+ }],
31939
+ "deviceManager.setRole": [{
31940
+ name: "deviceId",
31941
+ form: "single",
31942
+ optional: false
31943
+ }],
31944
+ "deviceManager.setStreamProfileMap": [{
31945
+ name: "deviceId",
31946
+ form: "single",
31947
+ optional: false
31948
+ }],
31949
+ "deviceManager.setType": [{
31950
+ name: "deviceId",
31951
+ form: "single",
31952
+ optional: false
31953
+ }],
31954
+ "deviceManager.setWrapperActive": [{
31955
+ name: "deviceId",
31956
+ form: "single",
31957
+ optional: false
31958
+ }],
31959
+ "deviceManager.testField": [{
31960
+ name: "deviceId",
31961
+ form: "single",
31962
+ optional: false
31963
+ }],
31964
+ "deviceManager.updateConfig": [{
31965
+ name: "deviceId",
31966
+ form: "single",
31967
+ optional: false
31968
+ }],
31969
+ "deviceManager.updateDeviceField": [{
31970
+ name: "deviceId",
31971
+ form: "single",
31972
+ optional: false
31973
+ }],
31974
+ "deviceManager.updateDeviceFieldsBatch": [{
31975
+ name: "deviceId",
31976
+ form: "single",
31977
+ optional: false
31978
+ }],
31979
+ "deviceOps.getConfigEntries": [{
31980
+ name: "deviceId",
31981
+ form: "single",
31982
+ optional: false
31983
+ }],
31984
+ "deviceOps.getRawState": [{
31985
+ name: "deviceId",
31986
+ form: "single",
31987
+ optional: false
31988
+ }],
31989
+ "deviceOps.getSettingsSchema": [{
31990
+ name: "deviceId",
31991
+ form: "single",
31992
+ optional: false
31993
+ }],
31994
+ "deviceOps.getStreamSources": [{
31995
+ name: "deviceId",
31996
+ form: "single",
31997
+ optional: false
31998
+ }],
31999
+ "deviceOps.removeDevice": [{
32000
+ name: "deviceId",
32001
+ form: "single",
32002
+ optional: false
32003
+ }],
32004
+ "deviceOps.runAction": [{
32005
+ name: "deviceId",
32006
+ form: "single",
32007
+ optional: false
32008
+ }],
32009
+ "deviceOps.setConfig": [{
32010
+ name: "deviceId",
32011
+ form: "single",
32012
+ optional: false
32013
+ }],
32014
+ "deviceState.getCapSlice": [{
32015
+ name: "deviceId",
32016
+ form: "single",
32017
+ optional: false
32018
+ }],
32019
+ "deviceState.getSnapshot": [{
32020
+ name: "deviceId",
32021
+ form: "single",
32022
+ optional: false
32023
+ }],
32024
+ "deviceState.setCapSlice": [{
32025
+ name: "deviceId",
32026
+ form: "single",
32027
+ optional: false
32028
+ }],
32029
+ "events.getEventClipUrl": [{
32030
+ name: "deviceId",
32031
+ form: "single",
32032
+ optional: false
32033
+ }],
32034
+ "events.getEvents": [{
32035
+ name: "deviceId",
32036
+ form: "single",
32037
+ optional: false
32038
+ }],
32039
+ "events.getEventThumbnail": [{
32040
+ name: "deviceId",
32041
+ form: "single",
32042
+ optional: false
32043
+ }],
32044
+ "faceGallery.getFaceByTrack": [{
32045
+ name: "deviceId",
32046
+ form: "single",
32047
+ optional: false
32048
+ }],
32049
+ "faceGallery.listRecentFaces": [{
32050
+ name: "deviceId",
32051
+ form: "single",
32052
+ optional: true
32053
+ }],
32054
+ "fanControl.setDirection": [{
32055
+ name: "deviceId",
32056
+ form: "single",
32057
+ optional: false
32058
+ }],
32059
+ "fanControl.setOscillating": [{
32060
+ name: "deviceId",
32061
+ form: "single",
32062
+ optional: false
32063
+ }],
32064
+ "fanControl.setPercentage": [{
32065
+ name: "deviceId",
32066
+ form: "single",
32067
+ optional: false
32068
+ }],
32069
+ "fanControl.setPreset": [{
32070
+ name: "deviceId",
32071
+ form: "single",
32072
+ optional: false
32073
+ }],
32074
+ "humidifier.setMode": [{
32075
+ name: "deviceId",
32076
+ form: "single",
32077
+ optional: false
32078
+ }],
32079
+ "humidifier.setOn": [{
32080
+ name: "deviceId",
32081
+ form: "single",
32082
+ optional: false
32083
+ }],
32084
+ "humidifier.setTargetHumidity": [{
32085
+ name: "deviceId",
32086
+ form: "single",
32087
+ optional: false
32088
+ }],
32089
+ "imageSettings.getOptions": [{
32090
+ name: "deviceId",
32091
+ form: "single",
32092
+ optional: false
32093
+ }],
32094
+ "imageSettings.setSettings": [{
32095
+ name: "deviceId",
32096
+ form: "single",
32097
+ optional: false
32098
+ }],
32099
+ "intercom.endTalkSession": [{
32100
+ name: "deviceId",
32101
+ form: "single",
32102
+ optional: false
32103
+ }],
32104
+ "intercom.handleAnswer": [{
32105
+ name: "deviceId",
32106
+ form: "single",
32107
+ optional: false
32108
+ }],
32109
+ "intercom.pushTalkAudio": [{
32110
+ name: "deviceId",
32111
+ form: "single",
32112
+ optional: false
32113
+ }],
32114
+ "intercom.startSession": [{
32115
+ name: "deviceId",
32116
+ form: "single",
32117
+ optional: false
32118
+ }],
32119
+ "intercom.startTalkSession": [{
32120
+ name: "deviceId",
32121
+ form: "single",
32122
+ optional: false
32123
+ }],
32124
+ "intercom.stopSession": [{
32125
+ name: "deviceId",
32126
+ form: "single",
32127
+ optional: false
32128
+ }],
32129
+ "lawnMowerControl.dock": [{
32130
+ name: "deviceId",
32131
+ form: "single",
32132
+ optional: false
32133
+ }],
32134
+ "lawnMowerControl.pause": [{
32135
+ name: "deviceId",
32136
+ form: "single",
32137
+ optional: false
32138
+ }],
32139
+ "lawnMowerControl.startMowing": [{
32140
+ name: "deviceId",
32141
+ form: "single",
32142
+ optional: false
32143
+ }],
32144
+ "lockControl.lock": [{
32145
+ name: "deviceId",
32146
+ form: "single",
32147
+ optional: false
32148
+ }],
32149
+ "lockControl.open": [{
32150
+ name: "deviceId",
32151
+ form: "single",
32152
+ optional: false
32153
+ }],
32154
+ "lockControl.unlock": [{
32155
+ name: "deviceId",
32156
+ form: "single",
32157
+ optional: false
32158
+ }],
32159
+ "mediaPlayer.next": [{
32160
+ name: "deviceId",
32161
+ form: "single",
32162
+ optional: false
32163
+ }],
32164
+ "mediaPlayer.pause": [{
32165
+ name: "deviceId",
32166
+ form: "single",
32167
+ optional: false
32168
+ }],
32169
+ "mediaPlayer.play": [{
32170
+ name: "deviceId",
32171
+ form: "single",
32172
+ optional: false
32173
+ }],
32174
+ "mediaPlayer.playMedia": [{
32175
+ name: "deviceId",
32176
+ form: "single",
32177
+ optional: false
32178
+ }],
32179
+ "mediaPlayer.previous": [{
32180
+ name: "deviceId",
32181
+ form: "single",
32182
+ optional: false
32183
+ }],
32184
+ "mediaPlayer.seek": [{
32185
+ name: "deviceId",
32186
+ form: "single",
32187
+ optional: false
32188
+ }],
32189
+ "mediaPlayer.selectSource": [{
32190
+ name: "deviceId",
32191
+ form: "single",
32192
+ optional: false
32193
+ }],
32194
+ "mediaPlayer.setMute": [{
32195
+ name: "deviceId",
32196
+ form: "single",
32197
+ optional: false
32198
+ }],
32199
+ "mediaPlayer.setRepeat": [{
32200
+ name: "deviceId",
32201
+ form: "single",
32202
+ optional: false
32203
+ }],
32204
+ "mediaPlayer.setShuffle": [{
32205
+ name: "deviceId",
32206
+ form: "single",
32207
+ optional: false
32208
+ }],
32209
+ "mediaPlayer.setVolume": [{
32210
+ name: "deviceId",
32211
+ form: "single",
32212
+ optional: false
32213
+ }],
32214
+ "mediaPlayer.stop": [{
32215
+ name: "deviceId",
32216
+ form: "single",
32217
+ optional: false
32218
+ }],
32219
+ "motion.isDetected": [{
32220
+ name: "deviceId",
32221
+ form: "single",
32222
+ optional: false
32223
+ }],
32224
+ "motionDetection.analyze": [{
32225
+ name: "deviceId",
32226
+ form: "single",
32227
+ optional: false
32228
+ }],
32229
+ "motionDetection.removeCamera": [{
32230
+ name: "deviceId",
32231
+ form: "single",
32232
+ optional: false
32233
+ }],
32234
+ "motionTrigger.setMotionTrigger": [{
32235
+ name: "deviceId",
32236
+ form: "single",
32237
+ optional: false
32238
+ }],
32239
+ "motionZones.getOptions": [{
32240
+ name: "deviceId",
32241
+ form: "single",
32242
+ optional: false
32243
+ }],
32244
+ "motionZones.setZone": [{
32245
+ name: "deviceId",
32246
+ form: "single",
32247
+ optional: false
32248
+ }],
32249
+ "nativeObjectDetection.setEnabled": [{
32250
+ name: "deviceId",
32251
+ form: "single",
32252
+ optional: false
32253
+ }],
32254
+ "networkQuality.getDeviceStats": [{
32255
+ name: "deviceId",
32256
+ form: "single",
32257
+ optional: false
32258
+ }],
32259
+ "networkQuality.reportClientStats": [{
32260
+ name: "deviceId",
32261
+ form: "single",
32262
+ optional: false
32263
+ }],
32264
+ "notificationRules.setDeviceMuted": [{
32265
+ name: "deviceId",
32266
+ form: "single",
32267
+ optional: false
32268
+ }],
32269
+ "notifier.cancel": [{
32270
+ name: "deviceId",
32271
+ form: "single",
32272
+ optional: false
32273
+ }],
32274
+ "notifier.send": [{
32275
+ name: "deviceId",
32276
+ form: "single",
32277
+ optional: false
32278
+ }],
32279
+ "osd.setOverlay": [{
32280
+ name: "deviceId",
32281
+ form: "single",
32282
+ optional: false
32283
+ }],
32284
+ "osdManager.clearSlotBinding": [{
32285
+ name: "deviceId",
32286
+ form: "single",
32287
+ optional: false
32288
+ }],
32289
+ "osdManager.copyDeviceConfiguration": [{
32290
+ name: "sourceDeviceId",
32291
+ form: "single",
32292
+ optional: false
32293
+ }, {
32294
+ name: "targetDeviceId",
32295
+ form: "single",
32296
+ optional: false
32297
+ }],
32298
+ "osdManager.getDeviceOsd": [{
32299
+ name: "deviceId",
32300
+ form: "single",
32301
+ optional: false
32302
+ }],
32303
+ "osdManager.getSourceCatalog": [{
32304
+ name: "deviceId",
32305
+ form: "single",
32306
+ optional: false
32307
+ }],
32308
+ "osdManager.previewSlot": [{
32309
+ name: "deviceId",
32310
+ form: "single",
32311
+ optional: false
32312
+ }],
32313
+ "osdManager.renderDevice": [{
32314
+ name: "deviceId",
32315
+ form: "single",
32316
+ optional: false
32317
+ }],
32318
+ "osdManager.setSlotBinding": [{
32319
+ name: "deviceId",
32320
+ form: "single",
32321
+ optional: false
32322
+ }],
32323
+ "petFeeder.callPet": [{
32324
+ name: "deviceId",
32325
+ form: "single",
32326
+ optional: false
32327
+ }],
32328
+ "petFeeder.cancelFeed": [{
32329
+ name: "deviceId",
32330
+ form: "single",
32331
+ optional: false
32332
+ }],
32333
+ "petFeeder.feed": [{
32334
+ name: "deviceId",
32335
+ form: "single",
32336
+ optional: false
32337
+ }],
32338
+ "petFeeder.markFoodReplenished": [{
32339
+ name: "deviceId",
32340
+ form: "single",
32341
+ optional: false
32342
+ }],
32343
+ "petFeeder.playSound": [{
32344
+ name: "deviceId",
32345
+ form: "single",
32346
+ optional: false
32347
+ }],
32348
+ "petFeeder.resetDesiccant": [{
32349
+ name: "deviceId",
32350
+ form: "single",
32351
+ optional: false
32352
+ }],
32353
+ "petFeeder.setChildLock": [{
32354
+ name: "deviceId",
32355
+ form: "single",
32356
+ optional: false
32357
+ }],
32358
+ "petFeeder.setFeedSound": [{
32359
+ name: "deviceId",
32360
+ form: "single",
32361
+ optional: false
32362
+ }],
32363
+ "petFeeder.setIndicatorLight": [{
32364
+ name: "deviceId",
32365
+ form: "single",
32366
+ optional: false
32367
+ }],
32368
+ "petFeeder.setVolume": [{
32369
+ name: "deviceId",
32370
+ form: "single",
32371
+ optional: false
32372
+ }],
32373
+ "pipelineAnalytics.clearTracks": [{
32374
+ name: "deviceId",
32375
+ form: "single",
32376
+ optional: false
32377
+ }],
32378
+ "pipelineAnalytics.completeRetrainTrack": [{
32379
+ name: "deviceId",
32380
+ form: "single",
32381
+ optional: false
32382
+ }],
32383
+ "pipelineAnalytics.deleteDeviceEvents": [{
32384
+ name: "deviceId",
32385
+ form: "single",
32386
+ optional: false
32387
+ }],
32388
+ "pipelineAnalytics.deleteTracks": [{
32389
+ name: "deviceId",
32390
+ form: "single",
32391
+ optional: false
32392
+ }],
32393
+ "pipelineAnalytics.deselectRetrainFrame": [{
32394
+ name: "deviceId",
32395
+ form: "single",
32396
+ optional: false
32397
+ }],
32398
+ "pipelineAnalytics.getActiveTracks": [{
32399
+ name: "deviceId",
32400
+ form: "single",
32401
+ optional: false
32402
+ }],
32403
+ "pipelineAnalytics.getAudioEvents": [{
32404
+ name: "deviceId",
32405
+ form: "single",
32406
+ optional: false
32407
+ }],
32408
+ "pipelineAnalytics.getEventDensity": [{
32409
+ name: "deviceId",
32410
+ form: "single",
32411
+ optional: false
32412
+ }],
32413
+ "pipelineAnalytics.getEventMedia": [{
32414
+ name: "deviceId",
32415
+ form: "single",
32416
+ optional: false
32417
+ }],
32418
+ "pipelineAnalytics.getKeyEvents": [{
32419
+ name: "deviceId",
32420
+ form: "single",
32421
+ optional: false
32422
+ }],
32423
+ "pipelineAnalytics.getMotionEvents": [{
32424
+ name: "deviceId",
32425
+ form: "single",
32426
+ optional: false
32427
+ }],
32428
+ "pipelineAnalytics.getObjectEvents": [{
32429
+ name: "deviceId",
32430
+ form: "single",
32431
+ optional: false
32432
+ }],
32433
+ "pipelineAnalytics.getRetrainExportUrl": [{
32434
+ name: "deviceIds",
32435
+ form: "array",
32436
+ optional: true
32437
+ }],
32438
+ "pipelineAnalytics.getSensorEvents": [{
32439
+ name: "deviceId",
32440
+ form: "single",
32441
+ optional: false
32442
+ }],
32443
+ "pipelineAnalytics.getTrack": [{
32444
+ name: "deviceId",
32445
+ form: "single",
32446
+ optional: false
32447
+ }],
32448
+ "pipelineAnalytics.getTrackMedia": [{
32449
+ name: "deviceId",
32450
+ form: "single",
32451
+ optional: false
32452
+ }],
32453
+ "pipelineAnalytics.getTrainingExportSummary": [{
32454
+ name: "deviceIds",
32455
+ form: "array",
32456
+ optional: true
32457
+ }],
32458
+ "pipelineAnalytics.getTrainingExportUrl": [{
32459
+ name: "deviceIds",
32460
+ form: "array",
32461
+ optional: true
32462
+ }],
32463
+ "pipelineAnalytics.listEventKinds": [{
32464
+ name: "deviceId",
32465
+ form: "single",
32466
+ optional: false
32467
+ }],
32468
+ "pipelineAnalytics.listEventKindsBatch": [{
32469
+ name: "deviceIds",
32470
+ form: "array",
32471
+ optional: false
32472
+ }],
32473
+ "pipelineAnalytics.listOpsLog": [{
32474
+ name: "deviceId",
32475
+ form: "single",
32476
+ optional: true
32477
+ }],
32478
+ "pipelineAnalytics.listRecentTracks": [{
32479
+ name: "deviceIds",
32480
+ form: "array",
32481
+ optional: false
32482
+ }],
32483
+ "pipelineAnalytics.listRetrainStaging": [{
32484
+ name: "deviceIds",
32485
+ form: "array",
32486
+ optional: true
32487
+ }],
32488
+ "pipelineAnalytics.listTrackMedia": [{
32489
+ name: "deviceId",
32490
+ form: "single",
32491
+ optional: false
32492
+ }],
32493
+ "pipelineAnalytics.listTracks": [{
32494
+ name: "deviceId",
32495
+ form: "single",
32496
+ optional: false
32497
+ }],
32498
+ "pipelineAnalytics.proposeRetrainAnnotations": [{
32499
+ name: "deviceId",
32500
+ form: "single",
32501
+ optional: false
32502
+ }],
32503
+ "pipelineAnalytics.pruneEventsBefore": [{
32504
+ name: "deviceId",
32505
+ form: "single",
32506
+ optional: false
32507
+ }],
32508
+ "pipelineAnalytics.pruneTracksBefore": [{
32509
+ name: "deviceId",
32510
+ form: "single",
32511
+ optional: false
32512
+ }],
32513
+ "pipelineAnalytics.rebuildObjectEmbeddings": [{
32514
+ name: "deviceId",
32515
+ form: "single",
32516
+ optional: true
32517
+ }],
32518
+ "pipelineAnalytics.restageRetrainTrack": [{
32519
+ name: "deviceId",
32520
+ form: "single",
32521
+ optional: false
32522
+ }],
32523
+ "pipelineAnalytics.saveRetrainAnnotations": [{
32524
+ name: "deviceId",
32525
+ form: "single",
32526
+ optional: false
32527
+ }],
32528
+ "pipelineAnalytics.searchObjectEvents": [{
32529
+ name: "deviceId",
32530
+ form: "single",
32531
+ optional: true
32532
+ }],
32533
+ "pipelineAnalytics.selectRetrainFrames": [{
32534
+ name: "deviceId",
32535
+ form: "single",
32536
+ optional: false
32537
+ }],
32538
+ "pipelineAnalytics.setTrackFlags": [{
32539
+ name: "deviceId",
32540
+ form: "single",
32541
+ optional: false
32542
+ }],
32543
+ "pipelineAnalytics.wipeAllAnalytics": [{
32544
+ name: "deviceId",
32545
+ form: "single",
32546
+ optional: false
32547
+ }],
32548
+ "pipelineExecutor.runPipeline": [{
32549
+ name: "deviceId",
32550
+ form: "single",
32551
+ optional: true
32552
+ }],
32553
+ "pipelineExecutor.runPipelineBatch": [{
32554
+ name: "deviceId",
32555
+ form: "single",
32556
+ optional: true
32557
+ }],
32558
+ "pipelineOrchestrator.assignAudio": [{
32559
+ name: "deviceId",
32560
+ form: "single",
32561
+ optional: false
32562
+ }],
32563
+ "pipelineOrchestrator.assignPipeline": [{
32564
+ name: "deviceId",
32565
+ form: "single",
32566
+ optional: false
32567
+ }],
32568
+ "pipelineOrchestrator.getAudioAssignment": [{
32569
+ name: "deviceId",
32570
+ form: "single",
32571
+ optional: false
32572
+ }],
32573
+ "pipelineOrchestrator.getCameraMetrics": [{
32574
+ name: "deviceId",
32575
+ form: "single",
32576
+ optional: false
32577
+ }],
32578
+ "pipelineOrchestrator.getCameraSettings": [{
32579
+ name: "deviceId",
32580
+ form: "single",
32581
+ optional: false
32582
+ }],
32583
+ "pipelineOrchestrator.getCameraStatus": [{
32584
+ name: "deviceId",
32585
+ form: "single",
32586
+ optional: false
32587
+ }],
32588
+ "pipelineOrchestrator.getCameraStatuses": [{
32589
+ name: "deviceIds",
32590
+ form: "array",
32591
+ optional: true
32592
+ }],
32593
+ "pipelineOrchestrator.getCameraStepOverrides": [{
32594
+ name: "deviceId",
32595
+ form: "single",
32596
+ optional: false
32597
+ }],
32598
+ "pipelineOrchestrator.getCameraSwitches": [{
32599
+ name: "deviceId",
32600
+ form: "single",
32601
+ optional: false
32602
+ }],
32603
+ "pipelineOrchestrator.getPipelineAssignment": [{
32604
+ name: "deviceId",
32605
+ form: "single",
32606
+ optional: false
32607
+ }],
32608
+ "pipelineOrchestrator.getPipelineDevicePin": [{
32609
+ name: "deviceId",
32610
+ form: "single",
32611
+ optional: false
32612
+ }],
32613
+ "pipelineOrchestrator.resolvePipeline": [{
32614
+ name: "deviceId",
32615
+ form: "single",
32616
+ optional: false
32617
+ }],
32618
+ "pipelineOrchestrator.setCameraPipelineForAgent": [{
32619
+ name: "deviceId",
32620
+ form: "single",
32621
+ optional: false
32622
+ }],
32623
+ "pipelineOrchestrator.setCameraStepOverride": [{
32624
+ name: "deviceId",
32625
+ form: "single",
32626
+ optional: false
32627
+ }],
32628
+ "pipelineOrchestrator.setCameraStepToggle": [{
32629
+ name: "deviceId",
32630
+ form: "single",
32631
+ optional: false
32632
+ }],
32633
+ "pipelineOrchestrator.setCameraSwitch": [{
32634
+ name: "deviceId",
32635
+ form: "single",
32636
+ optional: false
32637
+ }],
32638
+ "pipelineOrchestrator.setPipelineDevicePin": [{
32639
+ name: "deviceId",
32640
+ form: "single",
32641
+ optional: false
32642
+ }],
32643
+ "pipelineOrchestrator.unassignAudio": [{
32644
+ name: "deviceId",
32645
+ form: "single",
32646
+ optional: false
32647
+ }],
32648
+ "pipelineOrchestrator.unassignPipeline": [{
32649
+ name: "deviceId",
32650
+ form: "single",
32651
+ optional: false
32652
+ }],
32653
+ "pipelineRunner.attachCamera": [{
32654
+ name: "deviceId",
32655
+ form: "single",
32656
+ optional: false
32657
+ }],
32658
+ "pipelineRunner.detachCamera": [{
32659
+ name: "deviceId",
32660
+ form: "single",
32661
+ optional: false
32662
+ }],
32663
+ "pipelineRunner.getCameraMetrics": [{
32664
+ name: "deviceId",
32665
+ form: "single",
32666
+ optional: false
32667
+ }],
32668
+ "pipelineRunner.reportMotion": [{
32669
+ name: "deviceId",
32670
+ form: "single",
32671
+ optional: false
32672
+ }],
32673
+ "pipelineRunner.runDetailSubtree": [{
32674
+ name: "deviceId",
32675
+ form: "single",
32676
+ optional: false
32677
+ }],
32678
+ "pipelineRunner.runStatelessStep": [{
32679
+ name: "sourceDeviceId",
32680
+ form: "single",
32681
+ optional: false
32682
+ }],
32683
+ "plateGallery.getPlateByTrack": [{
32684
+ name: "deviceId",
32685
+ form: "single",
32686
+ optional: false
32687
+ }],
32688
+ "plateGallery.listPlates": [{
32689
+ name: "deviceId",
32690
+ form: "single",
32691
+ optional: true
32692
+ }],
32693
+ "privacyMask.getOptions": [{
32694
+ name: "deviceId",
32695
+ form: "single",
32696
+ optional: false
32697
+ }],
32698
+ "privacyMask.setAudioEnabled": [{
32699
+ name: "deviceId",
32700
+ form: "single",
32701
+ optional: false
32702
+ }],
32703
+ "privacyMask.setMask": [{
32704
+ name: "deviceId",
32705
+ form: "single",
32706
+ optional: false
32707
+ }],
32708
+ "ptz.continuousMove": [{
32709
+ name: "deviceId",
32710
+ form: "single",
32711
+ optional: false
32712
+ }],
32713
+ "ptz.deletePreset": [{
32714
+ name: "deviceId",
32715
+ form: "single",
32716
+ optional: false
32717
+ }],
32718
+ "ptz.getOptions": [{
32719
+ name: "deviceId",
32720
+ form: "single",
32721
+ optional: false
32722
+ }],
32723
+ "ptz.getPosition": [{
32724
+ name: "deviceId",
32725
+ form: "single",
32726
+ optional: false
32727
+ }],
32728
+ "ptz.getPresets": [{
32729
+ name: "deviceId",
32730
+ form: "single",
32731
+ optional: false
32732
+ }],
32733
+ "ptz.goHome": [{
32734
+ name: "deviceId",
32735
+ form: "single",
32736
+ optional: false
32737
+ }],
32738
+ "ptz.goToPreset": [{
32739
+ name: "deviceId",
32740
+ form: "single",
32741
+ optional: false
32742
+ }],
32743
+ "ptz.move": [{
32744
+ name: "deviceId",
32745
+ form: "single",
32746
+ optional: false
32747
+ }],
32748
+ "ptz.savePreset": [{
32749
+ name: "deviceId",
32750
+ form: "single",
32751
+ optional: false
32752
+ }],
32753
+ "ptz.setAutofocus": [{
32754
+ name: "deviceId",
32755
+ form: "single",
32756
+ optional: false
32757
+ }],
32758
+ "ptz.stop": [{
32759
+ name: "deviceId",
32760
+ form: "single",
32761
+ optional: false
32762
+ }],
32763
+ "ptzAutotrack.getSettings": [{
32764
+ name: "deviceId",
32765
+ form: "single",
32766
+ optional: false
32767
+ }],
32768
+ "ptzAutotrack.getStatus": [{
32769
+ name: "deviceId",
32770
+ form: "single",
32771
+ optional: false
32772
+ }],
32773
+ "ptzAutotrack.setEnabled": [{
32774
+ name: "deviceId",
32775
+ form: "single",
32776
+ optional: false
32777
+ }],
32778
+ "ptzAutotrack.setSettings": [{
32779
+ name: "deviceId",
32780
+ form: "single",
32781
+ optional: false
32782
+ }],
32783
+ "reboot.reboot": [{
32784
+ name: "deviceId",
32785
+ form: "single",
32786
+ optional: false
32787
+ }],
32788
+ "recording.deleteFootprint": [{
32789
+ name: "deviceId",
32790
+ form: "single",
32791
+ optional: false
32792
+ }],
32793
+ "recording.getAvailability": [{
32794
+ name: "deviceId",
32795
+ form: "single",
32796
+ optional: false
32797
+ }],
32798
+ "recording.getDaysWithRecordings": [{
32799
+ name: "deviceId",
32800
+ form: "single",
32801
+ optional: false
32802
+ }],
32803
+ "recording.getDeviceConfig": [{
32804
+ name: "deviceId",
32805
+ form: "single",
32806
+ optional: false
32807
+ }],
32808
+ "recording.getPlaybackManifest": [{
32809
+ name: "deviceId",
32810
+ form: "single",
32811
+ optional: false
32812
+ }],
32813
+ "recording.listOpsLog": [{
32814
+ name: "deviceId",
32815
+ form: "single",
32816
+ optional: true
32817
+ }],
32818
+ "recording.locateSegment": [{
32819
+ name: "deviceId",
32820
+ form: "single",
32821
+ optional: false
32822
+ }],
32823
+ "recording.pruneFootage": [{
32824
+ name: "deviceId",
32825
+ form: "single",
32826
+ optional: false
32827
+ }],
32828
+ "recording.readGopBytes": [{
32829
+ name: "deviceId",
32830
+ form: "single",
32831
+ optional: false
32832
+ }],
32833
+ "recording.readSegmentBytes": [{
32834
+ name: "deviceId",
32835
+ form: "single",
32836
+ optional: false
32837
+ }],
32838
+ "recording.relocateFootage": [{
32839
+ name: "deviceId",
32840
+ form: "single",
32841
+ optional: true
32842
+ }],
32843
+ "recording.renderClip": [{
32844
+ name: "deviceId",
32845
+ form: "single",
32846
+ optional: false
32847
+ }],
32848
+ "recording.renderGif": [{
32849
+ name: "deviceId",
32850
+ form: "single",
32851
+ optional: false
32852
+ }],
32853
+ "recording.rescanStorage": [{
32854
+ name: "deviceId",
32855
+ form: "single",
32856
+ optional: false
32857
+ }],
32858
+ "recording.setDeviceConfig": [{
32859
+ name: "deviceId",
32860
+ form: "single",
32861
+ optional: false
32862
+ }],
32863
+ "recording.startStorageMigrationMove": [{
32864
+ name: "deviceId",
32865
+ form: "single",
32866
+ optional: true
32867
+ }],
32868
+ "recordingExport.createExport": [{
32869
+ name: "deviceId",
32870
+ form: "single",
32871
+ optional: false
32872
+ }],
32873
+ "recordingExport.listExports": [{
32874
+ name: "deviceId",
32875
+ form: "single",
32876
+ optional: true
32877
+ }],
32878
+ "sceneMonitor.captureReference": [{
32879
+ name: "deviceId",
32880
+ form: "single",
32881
+ optional: false
32882
+ }],
32883
+ "sceneMonitor.createScene": [{
32884
+ name: "deviceId",
32885
+ form: "single",
32886
+ optional: false
32887
+ }],
32888
+ "sceneMonitor.deleteReference": [{
32889
+ name: "deviceId",
32890
+ form: "single",
32891
+ optional: false
32892
+ }],
32893
+ "sceneMonitor.deleteScene": [{
32894
+ name: "deviceId",
32895
+ form: "single",
32896
+ optional: false
32897
+ }],
32898
+ "sceneMonitor.listScenes": [{
32899
+ name: "deviceId",
32900
+ form: "single",
32901
+ optional: false
32902
+ }],
32903
+ "sceneMonitor.recheckNow": [{
32904
+ name: "deviceId",
32905
+ form: "single",
32906
+ optional: false
32907
+ }],
32908
+ "sceneMonitor.resetScene": [{
32909
+ name: "deviceId",
32910
+ form: "single",
32911
+ optional: false
32912
+ }],
32913
+ "sceneMonitor.updateScene": [{
32914
+ name: "deviceId",
32915
+ form: "single",
32916
+ optional: false
32917
+ }],
32918
+ "scriptRunner.run": [{
32919
+ name: "deviceId",
32920
+ form: "single",
32921
+ optional: false
32922
+ }],
32923
+ "scriptRunner.stop": [{
32924
+ name: "deviceId",
32925
+ form: "single",
32926
+ optional: false
32927
+ }],
32928
+ "snapshot.getSnapshot": [{
32929
+ name: "deviceId",
32930
+ form: "single",
32931
+ optional: false
32932
+ }],
32933
+ "snapshot.getSnapshotLinks": [{
32934
+ name: "targets",
32935
+ form: "object-array",
32936
+ optional: false,
32937
+ itemField: "deviceId"
32938
+ }],
32939
+ "snapshot.getSnapshotOverview": [{
32940
+ name: "deviceIds",
32941
+ form: "array",
32942
+ optional: false
32943
+ }],
32944
+ "snapshot.invalidateCache": [{
32945
+ name: "deviceId",
32946
+ form: "single",
32947
+ optional: false
32948
+ }],
32949
+ "streamBroker.acquireEgressTranscode": [{
32950
+ name: "deviceId",
32951
+ form: "single",
32952
+ optional: false
32953
+ }],
32954
+ "streamBroker.assignProfile": [{
32955
+ name: "deviceId",
32956
+ form: "single",
32957
+ optional: false
32958
+ }],
32959
+ "streamBroker.getDeviceAudioMute": [{
32960
+ name: "deviceId",
32961
+ form: "single",
32962
+ optional: false
32963
+ }],
32964
+ "streamBroker.getStreamWithCodec": [{
32965
+ name: "deviceId",
32966
+ form: "single",
32967
+ optional: false
32968
+ }],
32969
+ "streamBroker.produceEventMedia": [{
32970
+ name: "deviceId",
32971
+ form: "single",
32972
+ optional: false
32973
+ }],
32974
+ "streamBroker.publishCameraStream": [{
32975
+ name: "deviceId",
32976
+ form: "single",
32977
+ optional: false
32978
+ }],
32979
+ "streamBroker.renderPreBufferClip": [{
32980
+ name: "deviceId",
32981
+ form: "single",
32982
+ optional: false
32983
+ }],
32984
+ "streamBroker.restartProfile": [{
32985
+ name: "deviceId",
32986
+ form: "single",
32987
+ optional: false
32988
+ }],
32989
+ "streamBroker.retractCameraStream": [{
32990
+ name: "deviceId",
32991
+ form: "single",
32992
+ optional: false
32993
+ }],
32994
+ "streamBroker.setDeviceAudioMute": [{
32995
+ name: "deviceId",
32996
+ form: "single",
32997
+ optional: false
32998
+ }],
32999
+ "streamBroker.unassignProfile": [{
33000
+ name: "deviceId",
33001
+ form: "single",
33002
+ optional: false
33003
+ }],
33004
+ "streamCatalog.getCatalog": [{
33005
+ name: "deviceId",
33006
+ form: "single",
33007
+ optional: false
33008
+ }],
33009
+ "streamParams.getConfigSchema": [{
33010
+ name: "deviceId",
33011
+ form: "single",
33012
+ optional: false
33013
+ }],
33014
+ "streamParams.getOptions": [{
33015
+ name: "deviceId",
33016
+ form: "single",
33017
+ optional: false
33018
+ }],
33019
+ "streamParams.setProfile": [{
33020
+ name: "deviceId",
33021
+ form: "single",
33022
+ optional: false
33023
+ }],
33024
+ "switch.setState": [{
33025
+ name: "deviceId",
33026
+ form: "single",
33027
+ optional: false
33028
+ }],
33029
+ "vacuumControl.locate": [{
33030
+ name: "deviceId",
33031
+ form: "single",
33032
+ optional: false
33033
+ }],
33034
+ "vacuumControl.pause": [{
33035
+ name: "deviceId",
33036
+ form: "single",
33037
+ optional: false
33038
+ }],
33039
+ "vacuumControl.returnToBase": [{
33040
+ name: "deviceId",
33041
+ form: "single",
33042
+ optional: false
33043
+ }],
33044
+ "vacuumControl.setFanSpeed": [{
33045
+ name: "deviceId",
33046
+ form: "single",
33047
+ optional: false
33048
+ }],
33049
+ "vacuumControl.start": [{
33050
+ name: "deviceId",
33051
+ form: "single",
33052
+ optional: false
33053
+ }],
33054
+ "vacuumControl.stop": [{
33055
+ name: "deviceId",
33056
+ form: "single",
33057
+ optional: false
33058
+ }],
33059
+ "valve.close": [{
33060
+ name: "deviceId",
33061
+ form: "single",
33062
+ optional: false
33063
+ }],
33064
+ "valve.open": [{
33065
+ name: "deviceId",
33066
+ form: "single",
33067
+ optional: false
33068
+ }],
33069
+ "valve.setPosition": [{
33070
+ name: "deviceId",
33071
+ form: "single",
33072
+ optional: false
33073
+ }],
33074
+ "valve.stop": [{
33075
+ name: "deviceId",
33076
+ form: "single",
33077
+ optional: false
33078
+ }],
33079
+ "videoclips.getClipPlayback": [{
33080
+ name: "deviceId",
33081
+ form: "single",
33082
+ optional: false
33083
+ }],
33084
+ "videoclips.listClips": [{
33085
+ name: "deviceId",
33086
+ form: "single",
33087
+ optional: false
33088
+ }],
33089
+ "waterHeater.setAway": [{
33090
+ name: "deviceId",
33091
+ form: "single",
33092
+ optional: false
33093
+ }],
33094
+ "waterHeater.setOperationMode": [{
33095
+ name: "deviceId",
33096
+ form: "single",
33097
+ optional: false
33098
+ }],
33099
+ "waterHeater.setTargetTemp": [{
33100
+ name: "deviceId",
33101
+ form: "single",
33102
+ optional: false
33103
+ }],
33104
+ "webrtcSession.addIceCandidate": [{
33105
+ name: "deviceId",
33106
+ form: "single",
33107
+ optional: false
33108
+ }],
33109
+ "webrtcSession.closeSession": [{
33110
+ name: "deviceId",
33111
+ form: "single",
33112
+ optional: false
33113
+ }],
33114
+ "webrtcSession.createSession": [{
33115
+ name: "deviceId",
33116
+ form: "single",
33117
+ optional: false
33118
+ }],
33119
+ "webrtcSession.getIceCandidates": [{
33120
+ name: "deviceId",
33121
+ form: "single",
33122
+ optional: false
33123
+ }],
33124
+ "webrtcSession.getSessionState": [{
33125
+ name: "deviceId",
33126
+ form: "single",
33127
+ optional: false
33128
+ }],
33129
+ "webrtcSession.handleAnswer": [{
33130
+ name: "deviceId",
33131
+ form: "single",
33132
+ optional: false
33133
+ }],
33134
+ "webrtcSession.handleOffer": [{
33135
+ name: "deviceId",
33136
+ form: "single",
33137
+ optional: false
33138
+ }],
33139
+ "webrtcSession.hasAdaptiveBitrate": [{
33140
+ name: "deviceId",
33141
+ form: "single",
33142
+ optional: false
33143
+ }],
33144
+ "webrtcSession.listStreams": [{
33145
+ name: "deviceId",
33146
+ form: "single",
33147
+ optional: false
33148
+ }],
33149
+ "zoneAnalytics.getCameraHistory": [{
33150
+ name: "deviceId",
33151
+ form: "single",
33152
+ optional: false
33153
+ }],
33154
+ "zoneAnalytics.getCurrentSnapshot": [{
33155
+ name: "deviceId",
33156
+ form: "single",
33157
+ optional: false
33158
+ }],
33159
+ "zoneAnalytics.getUnzonedHistory": [{
33160
+ name: "deviceId",
33161
+ form: "single",
33162
+ optional: false
33163
+ }],
33164
+ "zoneAnalytics.getZoneHistory": [{
33165
+ name: "deviceId",
33166
+ form: "single",
33167
+ optional: false
33168
+ }],
33169
+ "zoneRules.listRules": [{
33170
+ name: "deviceId",
33171
+ form: "single",
33172
+ optional: false
33173
+ }],
33174
+ "zoneRules.setRules": [{
33175
+ name: "deviceId",
33176
+ form: "single",
33177
+ optional: false
33178
+ }],
33179
+ "zones.addZone": [{
33180
+ name: "deviceId",
33181
+ form: "single",
33182
+ optional: false
33183
+ }],
33184
+ "zones.listZones": [{
33185
+ name: "deviceId",
33186
+ form: "single",
33187
+ optional: false
33188
+ }],
33189
+ "zones.removeZone": [{
33190
+ name: "deviceId",
33191
+ form: "single",
33192
+ optional: false
33193
+ }],
33194
+ "zones.updateZone": [{
33195
+ name: "deviceId",
33196
+ form: "single",
33197
+ optional: false
33198
+ }]
33199
+ });
30562
33200
  Object.freeze({
30563
33201
  "broker": "broker",
30564
33202
  "device-export": "device-export",