@camstack/addon-provider-dreame 0.2.17 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +2858 -139
  2. package/dist/addon.mjs +2858 -139
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -50504,7 +50504,7 @@ objectType({
50504
50504
  })
50505
50505
  });
50506
50506
  //#endregion
50507
- //#region ../types/dist/event-category-Cv9dO26A.mjs
50507
+ //#region ../types/dist/event-category-Bxo5yJjt.mjs
50508
50508
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
50509
50509
  EventCategory["SystemBoot"] = "system.boot";
50510
50510
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -50711,6 +50711,33 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
50711
50711
  EventCategory["PipelineCameraAssigned"] = "pipeline.camera-assigned";
50712
50712
  EventCategory["PipelineCameraUnassigned"] = "pipeline.camera-unassigned";
50713
50713
  /**
50714
+ * A node the orchestrator would otherwise place cameras on has NO usable
50715
+ * inference device: the operator enabled one or more accelerators there and
50716
+ * the live probe reports every one of them unavailable. Emitted once per
50717
+ * TRANSITION into that state (never per dispatch), and the node is dropped
50718
+ * from the placement candidate set for as long as it holds.
50719
+ *
50720
+ * This exists because the state was previously invisible: little-unraid
50721
+ * absorbed 283k inference errors in a day while still being handed cameras,
50722
+ * and nothing in the system said so.
50723
+ *
50724
+ * A node with no accelerators configured at all is NOT this — its devices
50725
+ * are `disabled`, not `unavailable`, and the runner's default CPU pool
50726
+ * serves it exactly as before.
50727
+ */
50728
+ EventCategory["PipelineNodeInferenceUnavailable"] = "pipeline.node-inference-unavailable";
50729
+ /**
50730
+ * A camera has an OPEN detection session and has produced no detection at
50731
+ * all for longer than the blind threshold — the camera is being decoded and
50732
+ * inferred and is returning nothing. Emitted once per transition into blind,
50733
+ * per camera.
50734
+ *
50735
+ * The failure it reports: a 1h43 detection blackout on the entrance camera
50736
+ * that nobody noticed, because "a camera that detects nothing" and "a quiet
50737
+ * camera" produce byte-identical silence.
50738
+ */
50739
+ EventCategory["PipelineDetectionBlind"] = "pipeline.detection-blind";
50740
+ /**
50714
50741
  * Per-camera pipeline config was mutated by the orchestrator
50715
50742
  * (3-level settings change via `setAgentAddonDefaults` /
50716
50743
  * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
@@ -61395,6 +61422,8 @@ var QueryFilterSchema = object({
61395
61422
  where: record(string(), unknown()).optional(),
61396
61423
  whereIn: record(string(), array(unknown())).optional(),
61397
61424
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
61425
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
61426
+ whereNot: record(string(), unknown()).optional(),
61398
61427
  orderBy: object({
61399
61428
  field: string(),
61400
61429
  direction: _enum(["asc", "desc"])
@@ -61414,7 +61443,8 @@ var QueryFilterSchema = object({
61414
61443
  var MutationFilterSchema = object({
61415
61444
  where: record(string(), unknown()).optional(),
61416
61445
  whereIn: record(string(), array(unknown())).optional(),
61417
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
61446
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
61447
+ whereNot: record(string(), unknown()).optional()
61418
61448
  });
61419
61449
  /** A single stored record: `{ id, data }`. */
61420
61450
  var SettingsRecordSchema = object({
@@ -62950,6 +62980,17 @@ var LlmImageSchema = object({
62950
62980
  bytes: _instanceof(Uint8Array),
62951
62981
  mimeType: string()
62952
62982
  });
62983
+ /**
62984
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
62985
+ * the flag is what a consumer table flips, the count is what the operator tunes.
62986
+ * A retry doubles the wall time of a call, so the two gates that run inside a
62987
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
62988
+ */
62989
+ var LlmRetryPolicySchema = object({
62990
+ enabled: boolean().default(false),
62991
+ /** Total attempts INCLUDING the first. 1 = no retry. */
62992
+ maxAttempts: number().int().min(1).max(5).default(1)
62993
+ });
62953
62994
  var LlmGenerateBaseInputSchema = object({
62954
62995
  /** Collection routing (the notification-output posture). */
62955
62996
  addonId: string().optional(),
@@ -62964,7 +63005,28 @@ var LlmGenerateBaseInputSchema = object({
62964
63005
  jsonSchema: record(string(), unknown()).optional(),
62965
63006
  /** Per-call override of the profile default. */
62966
63007
  maxTokens: number().int().positive().optional(),
62967
- temperature: number().optional()
63008
+ temperature: number().optional(),
63009
+ /** Per-call override of the profile default (nucleus sampling). */
63010
+ topP: number().min(0).max(1).optional(),
63011
+ /** Per-call override of the profile default (top-k sampling). */
63012
+ topK: number().int().positive().optional(),
63013
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
63014
+ timeoutMs: number().int().positive().optional(),
63015
+ /** Per-call override; beats both the consumer table and the profile. */
63016
+ retry: LlmRetryPolicySchema.optional(),
63017
+ /**
63018
+ * Caller-minted id that makes this generation CANCELLABLE.
63019
+ *
63020
+ * Without it a caller that stops waiting cannot stop the work: the gates race
63021
+ * the call against 8 s and free their own slot when the timer wins, while the
63022
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
63023
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
63024
+ * not generations, and the real load is unbounded.
63025
+ *
63026
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
63027
+ * `llm.cancel({ requestId })` tears the socket down.
63028
+ */
63029
+ requestId: string().optional()
62968
63030
  });
62969
63031
  /**
62970
63032
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -62977,6 +63039,18 @@ var LlmGenerateBaseInputSchema = object({
62977
63039
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
62978
63040
  * watchdog — operator decision #3).
62979
63041
  */
63042
+ /**
63043
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
63044
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
63045
+ * REF rather than looked up at install time, so what the operator approved in
63046
+ * the preview is exactly what the node downloads.
63047
+ */
63048
+ var ManagedModelExtraFileSchema = object({
63049
+ url: string(),
63050
+ filename: string(),
63051
+ sizeBytes: number(),
63052
+ sha256: string().optional()
63053
+ });
62980
63054
  var ManagedModelRefSchema = discriminatedUnion("kind", [
62981
63055
  object({
62982
63056
  kind: literal("catalog"),
@@ -62985,7 +63059,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
62985
63059
  object({
62986
63060
  kind: literal("url"),
62987
63061
  url: string(),
62988
- sha256: string().optional()
63062
+ sha256: string().optional(),
63063
+ /** Picker/status label; the file basename when absent. */
63064
+ label: string().optional(),
63065
+ sizeBytes: number().optional(),
63066
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
62989
63067
  }),
62990
63068
  object({
62991
63069
  kind: literal("path"),
@@ -63003,13 +63081,82 @@ var ManagedRuntimeConfigSchema = object({
63003
63081
  gpuLayers: number().int().default(0),
63004
63082
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
63005
63083
  threads: number().int().optional(),
63006
- /** Concurrent slots. */
63084
+ /** Concurrent slots (`--parallel`). */
63007
63085
  parallel: number().int().default(1),
63086
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
63087
+ batchSize: number().int().positive().optional(),
63088
+ /** Physical batch / micro-batch (`-ub`). */
63089
+ ubatchSize: number().int().positive().optional(),
63090
+ /**
63091
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
63092
+ * is a no-op elsewhere, so it is offered rather than assumed.
63093
+ */
63094
+ flashAttention: boolean().default(false),
63095
+ /**
63096
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
63097
+ * inference. Costs the full model size in resident memory — which is exactly
63098
+ * what the RAM budget is counting.
63099
+ */
63100
+ mlock: boolean().default(false),
63101
+ /**
63102
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
63103
+ * start, but avoids the page-fault stalls a network or spinning-disk model
63104
+ * store produces on every first token.
63105
+ */
63106
+ noMmap: boolean().default(false),
63107
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
63108
+ * cheapest way to fit a longer context in the same RAM. */
63109
+ cacheTypeK: _enum([
63110
+ "f32",
63111
+ "f16",
63112
+ "q8_0",
63113
+ "q5_1",
63114
+ "q5_0",
63115
+ "q4_1",
63116
+ "q4_0"
63117
+ ]).optional(),
63118
+ cacheTypeV: _enum([
63119
+ "f32",
63120
+ "f16",
63121
+ "q8_0",
63122
+ "q5_1",
63123
+ "q5_0",
63124
+ "q4_1",
63125
+ "q4_0"
63126
+ ]).optional(),
63127
+ /**
63128
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
63129
+ * (which most vision chat templates need and some language-only models
63130
+ * dislike), `--cont-batching`, `--rope-scaling`, …
63131
+ *
63132
+ * It is NOT a second place to set the flags above. A token that collides
63133
+ * with a typed field is REJECTED at start, naming the field that owns it
63134
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
63135
+ * the "two switches that disagree" failure this repo has already shipped
63136
+ * twice (D62).
63137
+ */
63138
+ extraArgs: array(string()).default([]),
63008
63139
  /** Else lazy: first generate boots it. */
63009
63140
  autoStart: boolean().default(false),
63010
63141
  /** 0 = never; frees RAM after quiet periods. */
63011
63142
  idleStopMinutes: number().int().default(30)
63012
63143
  });
63144
+ /**
63145
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
63146
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
63147
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
63148
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
63149
+ */
63150
+ var LlmDownloadProgressSchema = object({
63151
+ phase: _enum(["downloading", "verifying"]),
63152
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
63153
+ file: string(),
63154
+ fileIndex: number().int(),
63155
+ fileCount: number().int(),
63156
+ /** Across the WHOLE install, not the current file. */
63157
+ downloadedBytes: number(),
63158
+ totalBytes: number().optional()
63159
+ });
63013
63160
  var LlmRuntimeStatusSchema = object({
63014
63161
  /** Status is ALWAYS node-qualified. */
63015
63162
  nodeId: string(),
@@ -63026,6 +63173,8 @@ var LlmRuntimeStatusSchema = object({
63026
63173
  modelPath: string().optional(),
63027
63174
  modelId: string().optional(),
63028
63175
  downloadProgress: number().min(0).max(1).optional(),
63176
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
63177
+ download: LlmDownloadProgressSchema.optional(),
63029
63178
  lastError: string().optional(),
63030
63179
  crashesInWindow: number(),
63031
63180
  /** Child RSS (sampled best-effort). */
@@ -63036,7 +63185,14 @@ var LlmNodeModelSchema = object({
63036
63185
  file: string(),
63037
63186
  sizeBytes: number(),
63038
63187
  catalogId: string().optional(),
63039
- installedAt: number().optional()
63188
+ installedAt: number().optional(),
63189
+ /**
63190
+ * Absolute path on the node. Present so a file that is on disk but matches
63191
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
63192
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
63193
+ * it the picker could list such a file and do nothing with it.
63194
+ */
63195
+ path: string().optional()
63040
63196
  });
63041
63197
  var LlmRuntimeDiskUsageSchema = object({
63042
63198
  nodeId: string(),
@@ -63092,10 +63248,47 @@ var LlmProfileSchema = object({
63092
63248
  baseUrl: string().optional(),
63093
63249
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
63094
63250
  apiKey: string().optional(),
63251
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
63252
+ * degraded to text — that shipped once and produced a confident answer to a
63253
+ * question about a picture nobody sent. */
63095
63254
  supportsVision: boolean(),
63096
63255
  temperature: number().min(0).max(2).optional(),
63256
+ /** Nucleus sampling. Every wire we speak has it. */
63257
+ topP: number().min(0).max(1).optional(),
63258
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
63259
+ * wire does, and the client drops it there (measured: the request body gets
63260
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
63261
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
63262
+ topK: number().int().positive().optional(),
63097
63263
  maxTokens: number().int().positive().optional(),
63264
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
63265
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
63266
+ * the model with, so it is the one field that changes a PROCESS. */
63267
+ contextLength: number().int().positive().optional(),
63268
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
63269
+ * two system prompts fighting is worse than either alone). */
63270
+ systemPrompt: string().optional(),
63271
+ /** Total generation bound — the only one a unary call has. */
63098
63272
  timeoutMs: number().int().positive().default(6e4),
63273
+ /** The TCP handshake only — "is the port even open". NOT the wait for
63274
+ * response headers: on the LM Studio / llama-server wire those are written
63275
+ * once the model has finished loading, so they belong to the bound below. */
63276
+ connectTimeoutMs: number().int().positive().default(1e4),
63277
+ /** Accepted, but no output yet — response headers included, because a cold
63278
+ * GPU load is exactly what happens before them. */
63279
+ firstTokenTimeoutMs: number().int().positive().default(12e4),
63280
+ /** Output started then stopped. */
63281
+ idleTimeoutMs: number().int().positive().default(6e4),
63282
+ /** Profile-level default. The per-consumer table and a per-call override
63283
+ * both beat it — see `resolveRetryPolicy`. */
63284
+ retry: LlmRetryPolicySchema.default({
63285
+ enabled: false,
63286
+ maxAttempts: 1
63287
+ }),
63288
+ /** Whether this profile may use tools. The tool-call plumbing rides the
63289
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
63290
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
63291
+ toolsEnabled: boolean().default(false),
63099
63292
  extraHeaders: record(string(), string()).optional(),
63100
63293
  /** kind === 'managed-local' only (spec §4). */
63101
63294
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -63145,6 +63338,36 @@ var ManagedModelCatalogEntrySchema = object({
63145
63338
  /** Vision models: companion projector file. */
63146
63339
  mmprojUrl: string().optional()
63147
63340
  });
63341
+ /**
63342
+ * The outcome of turning one operator-typed Hugging Face reference into a
63343
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
63344
+ * I will not pick for you" is a normal answer the UI has to render, not an
63345
+ * exception.
63346
+ *
63347
+ * `candidates` is the whole reason the refusal is usable — every string in it
63348
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
63349
+ */
63350
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
63351
+ ok: literal(true),
63352
+ /** Ready to hand to `installModel` unchanged. */
63353
+ model: ManagedModelRefSchema,
63354
+ label: string(),
63355
+ repo: string(),
63356
+ quantization: string(),
63357
+ purpose: _enum(["text", "vision"]),
63358
+ totalBytes: number(),
63359
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
63360
+ * see that 0.9 GB of it is a projector they did not name. */
63361
+ extraFilenames: array(string())
63362
+ }), object({
63363
+ ok: literal(false),
63364
+ code: string(),
63365
+ message: string(),
63366
+ candidates: array(string()).optional(),
63367
+ /** Set when the refusal was only the ceiling: re-calling with
63368
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
63369
+ requiredBytes: number().optional()
63370
+ })]);
63148
63371
  var LlmRuntimeNodeSchema = object({
63149
63372
  nodeId: string(),
63150
63373
  reachable: boolean(),
@@ -63157,7 +63380,10 @@ var ProfileRefInputSchema = object({
63157
63380
  addonId: string(),
63158
63381
  profileId: string()
63159
63382
  });
63160
- method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
63383
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
63384
+ addonId: string().optional(),
63385
+ requestId: string()
63386
+ }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
63161
63387
  kind: "mutation",
63162
63388
  auth: "admin"
63163
63389
  }), method(ProfileRefInputSchema, _void(), {
@@ -63178,6 +63404,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
63178
63404
  consumer: string().optional(),
63179
63405
  profileId: string().optional()
63180
63406
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
63407
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
63408
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
63409
+ ref: string(),
63410
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
63411
+ maxBytes: number().positive().optional()
63412
+ }), HfModelResolutionSchema, {
63413
+ kind: "mutation",
63414
+ auth: "admin"
63415
+ }), method(object({
63181
63416
  nodeId: string(),
63182
63417
  model: ManagedModelRefSchema
63183
63418
  }), _void(), {
@@ -64825,6 +65060,8 @@ var NcSystemEventKindSchema = _enum([
64825
65060
  "stream-offline",
64826
65061
  "node-online",
64827
65062
  "node-offline",
65063
+ "node-inference-unavailable",
65064
+ "detection-blind",
64828
65065
  "addon-update-available",
64829
65066
  "server-update-available",
64830
65067
  "alarm-triggered",
@@ -64886,7 +65123,16 @@ var NcScheduleSchema = object({
64886
65123
  });
64887
65124
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
64888
65125
  var NcPlateMatcherSchema = object({
64889
- values: array(string().min(1)).min(1),
65126
+ /**
65127
+ * Plate texts (or gallery vehicle names) to match. EMPTY = **any plate the
65128
+ * pipeline could read** — the plate half of "no selection = no narrowing",
65129
+ * and the switch that says this rule is about vehicles that were IDENTIFIED
65130
+ * rather than merely seen. A subject carrying no plate still fails.
65131
+ *
65132
+ * The `.min(1)` this used to carry made that state unauthorable; nothing has
65133
+ * ever persisted an empty list, so widening it cannot change an existing rule.
65134
+ */
65135
+ values: array(string().min(1)),
64890
65136
  /** Max Levenshtein distance after normalization (uppercase alphanumeric). */
64891
65137
  maxDistance: number().int().min(0).max(3).default(1)
64892
65138
  });
@@ -64920,28 +65166,36 @@ var NcOccupancyConditionSchema = object({
64920
65166
  /**
64921
65167
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
64922
65168
  *
64923
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
64924
- * reference notifier uses, so an operator moving between them re-uses what
64925
- * they already know): a rule matches when, over a sampling window of
64926
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
64927
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
65169
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
65170
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
65171
+ * there is no second switch that can disagree with the first and every rule
65172
+ * authored before the decision migrates for free (`audioModeOf`):
64928
65173
  *
64929
- * - `dbThreshold`its level is at or above this many dBFS (see
64930
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
64931
- * - `labels` the classifier put at least one of these labels on it.
65174
+ * - **LABEL mode `labels` present.** The rule fires on the FIRST frame the
65175
+ * classifier labels with one of them. No window, no percentage:
65176
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
65177
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
65178
+ * the analyzer's (`classificationMinScore`, per device) — a label only
65179
+ * reaches this condition if the classifier was already confident enough.
65180
+ * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
65181
+ * the condition: at least `hitPercent`% of the samples over
65182
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
65183
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
65184
+ * must be FULL before it can match — a window open for two of its ten
65185
+ * seconds is 100% of nothing.
64932
65186
  *
64933
- * Both are OPTIONAL and independent, which is the point of the shape: a
64934
- * loudness rule ("something loud at 3am") needs no model to be right, and a
64935
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
64936
- * is given** a window in which every sample is trivially a hit would fire on
64937
- * silence, so the engine refuses such a condition rather than notifying on
64938
- * nothing (the schema cannot express "at least one of" without becoming a
64939
- * ZodEffects the cap path would have to special-case).
65187
+ * **Why label mode has no window.** It had one, and it never fired: the
65188
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
65189
+ * of them per episode, even through continuous crying. The measured maximum
65190
+ * `hitPercent` over the whole live history was 40 under the shipped default
65191
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
65192
+ * the wrong question to ask of a sparse classifier.
64940
65193
  *
64941
- * `hitPercent` is over the samples the window actually HOLDS, and the window
64942
- * must be FULL before it can match a window that has been open for two
64943
- * seconds of its ten is 100% of nothing, and firing on it would make
64944
- * `samplingSeconds` decorative.
65194
+ * **Fail-closed when NEITHER is given** every sample would be a trivial hit
65195
+ * and the rule would fire on silence. The schema cannot express "exactly one
65196
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
65197
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
65198
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
64945
65199
  *
64946
65200
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
64947
65201
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -64949,13 +65203,13 @@ var NcOccupancyConditionSchema = object({
64949
65203
  * an operator who typed `dog` mean the same thing.
64950
65204
  */
64951
65205
  var NcAudioConditionSchema = object({
64952
- /** Audio macro labels; absent = any sound (level-only rule). */
65206
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
64953
65207
  labels: array(string().min(1)).min(1).optional(),
64954
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
65208
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
64955
65209
  dbThreshold: number().min(-96).max(0).optional(),
64956
- /** Percentage of the window's samples that must be hits (1–100). */
65210
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
64957
65211
  hitPercent: number().int().min(1).max(100).default(60),
64958
- /** Length of the sampling window in seconds. */
65212
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
64959
65213
  samplingSeconds: number().int().min(1).max(300).default(10)
64960
65214
  });
64961
65215
  /**
@@ -65093,13 +65347,81 @@ var NcRuleActionsSchema = object({
65093
65347
  */
65094
65348
  buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
65095
65349
  });
65350
+ /**
65351
+ * "This rule applies only while `deviceId` is in one of `states`."
65352
+ *
65353
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
65354
+ * `on`/`off` for a switch — not a normalised set, because normalising would
65355
+ * make the condition lie about devices whose states have no equivalent.
65356
+ *
65357
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
65358
+ * condition that fired on "I could not read it" would be worse than no gate.
65359
+ */
65360
+ var NcDeviceStateConditionSchema = object({
65361
+ deviceId: number().int(),
65362
+ /** Any of these matches. */
65363
+ states: array(string().min(1)).min(1)
65364
+ });
65365
+ /**
65366
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
65367
+ *
65368
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
65369
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
65370
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
65371
+ * already has a trigger ("tell me about a person at the front door, but only
65372
+ * while the bin is still out"). That is why it composes with every delivery
65373
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
65374
+ * kind exist for it — see D159.
65375
+ *
65376
+ * ── Identity ───────────────────────────────────────────────────────────────
65377
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
65378
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
65379
+ * carried as a HINT for the editor and for the log line, never as part of the
65380
+ * lookup key: a rule whose hint drifted must still gate correctly.
65381
+ *
65382
+ * ── Which boolean ──────────────────────────────────────────────────────────
65383
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
65384
+ * already declares which boolean drives notification rules, and a second knob
65385
+ * that could disagree with it is exactly the D62 failure. Set it only to
65386
+ * override one rule against the scene's own default.
65387
+ *
65388
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
65389
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
65390
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
65391
+ * evidence, in either direction.
65392
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
65393
+ * The latch is a durable fact about the past ("it has diverged since I armed
65394
+ * it"), so a camera that has gone dark does not clear it — that is the whole
65395
+ * reason the operator asked for a latch.
65396
+ *
65397
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
65398
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
65399
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
65400
+ * said out loud in the log rather than dropped in silence.
65401
+ */
65402
+ var NcSceneConditionSchema = object({
65403
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
65404
+ sceneId: string().min(1),
65405
+ /** The camera the scene lives on. A hint for the editor and the log line. */
65406
+ deviceId: number().int().optional(),
65407
+ /** The state the scene must be in for the rule to fire. */
65408
+ requiredState: _enum(["matched", "diverged"]),
65409
+ /**
65410
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
65411
+ * scene's own `emit` field, which is the only place that decision belongs.
65412
+ */
65413
+ latched: boolean().optional()
65414
+ });
65096
65415
  var NcConditionsSchema = object({
65097
65416
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
65098
- deviceState: object({
65099
- deviceId: number().int(),
65100
- /** Any of these matches. */
65101
- states: array(string().min(1)).min(1)
65102
- }).optional(),
65417
+ deviceState: NcDeviceStateConditionSchema.optional(),
65418
+ /**
65419
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
65420
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
65421
+ * unlike `occupancy`/`audio` it discriminates nothing. See
65422
+ * {@link NcSceneCondition} and D159.
65423
+ */
65424
+ scene: NcSceneConditionSchema.optional(),
65103
65425
  /** Device scope — absent = all devices. */
65104
65426
  devices: array(number()).optional(),
65105
65427
  /** Detector class names (any overlap with the record's class set). */
@@ -65125,18 +65447,47 @@ var NcConditionsSchema = object({
65125
65447
  */
65126
65448
  labelEquals: array(string().min(1)).optional(),
65127
65449
  /**
65128
- * Identity matcher. P1 boundary: matched against the record's collapsed
65129
- * `label` (the identity display name propagated by the face pipeline) —
65130
- * identity-ID matching rides in P2 when identity ids reach the record.
65450
+ * KNOWN FACES the rule's identity scope, and the switch that says the rule
65451
+ * is about recognised people at all.
65452
+ *
65453
+ * Three states, and the empty one is the point:
65454
+ *
65455
+ * | value | meaning |
65456
+ * | --- | --- |
65457
+ * | absent | the rule does not care who it is; an unrecognised person matches |
65458
+ * | `[]` | **only known faces** — any identity in the gallery, nobody in particular |
65459
+ * | a list | only these identities |
65460
+ *
65461
+ * `[]` is the repo-wide "no selection = no narrowing" reading (an absent
65462
+ * `devices` list is every device), applied one level down: the operator has
65463
+ * turned the face scope ON and narrowed it to nothing, which is every known
65464
+ * face. No second field states the same thing — a switch that can disagree
65465
+ * with the list under it is worse than no switch (D62).
65466
+ *
65467
+ * MEMBERS ARE FACE-GALLERY `Identity.id`s (uuid), not display names. A name is
65468
+ * renameable, and a rule authored on "Gianluca" went silently dark the moment
65469
+ * the operator fixed the spelling. The id reaches the record on
65470
+ * `LabelAttribution.identityId`; the name is what the editor shows and what
65471
+ * `{{label}}` renders.
65472
+ *
65473
+ * Rules written before this carry NAMES, and are resolved to ids lazily at
65474
+ * load (`NcRuleStore.load`) against the live gallery — a name nothing answers
65475
+ * for is left as it stands and reported, never dropped. The engine also
65476
+ * accepts a display-name hit as a compatibility leg, so a rule whose
65477
+ * migration could not resolve keeps matching exactly what it matched before.
65131
65478
  */
65132
65479
  identities: array(string().min(1)).optional(),
65133
- /** Fuzzy plate matcher against the record's `label` (plate text). */
65480
+ /**
65481
+ * KNOWN PLATES / VEHICLES — the plate mirror of {@link identities}, including
65482
+ * the empty-list reading: `values: []` is "any plate the OCR could read",
65483
+ * a non-empty list is those plates (fuzzily). See {@link NcPlateMatcherSchema}.
65484
+ */
65134
65485
  plates: NcPlateMatcherSchema.optional(),
65135
65486
  /**
65136
- * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics.
65137
- * Same P1 boundary: matched against the record's collapsed `label` (the
65138
- * identity display name). A record with NO label passes (nothing to
65139
- * exclude), unlike the include variant which fails on an absent label.
65487
+ * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics, and
65488
+ * the same id members and the same lazy name→id migration. A record with NO
65489
+ * identity passes (nothing to exclude), unlike the include variant which
65490
+ * fails on an unrecognised subject. An EMPTY list excludes nobody.
65140
65491
  */
65141
65492
  identitiesExclude: array(string().min(1)).optional(),
65142
65493
  /**
@@ -65528,7 +65879,80 @@ var NcRuleInputSchema = object({
65528
65879
  * a rule that predates the gate must keep delivering byte-for-byte as it
65529
65880
  * did, and absent is the only way to say that without a migration.
65530
65881
  */
65531
- confirm: NcConfirmSchema.optional()
65882
+ confirm: NcConfirmSchema.optional(),
65883
+ /**
65884
+ * WAIT for face/plate recognition before saying anything.
65885
+ *
65886
+ * A notification's TEXT is frozen at enqueue and its media is re-resolved at
65887
+ * send; the identity is neither. A face is confirmed after `confirmFrames`
65888
+ * agreeing observations — p50 **11.4 s** after the track was first seen,
65889
+ * measured on this hub — and an `immediate` rule enqueues on the first object
65890
+ * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
65891
+ * immediate path, and no amount of media re-resolution fixes a sentence.
65892
+ *
65893
+ * Only two honest answers exist, and this flag picks between them. It has
65894
+ * effect ONLY on a rule that declares a recognition scope
65895
+ * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
65896
+ * other rule there is nothing to wait for and the flag is inert.
65897
+ *
65898
+ * | value | what happens |
65899
+ * | --- | --- |
65900
+ * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
65901
+ * | 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) |
65902
+ *
65903
+ * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
65904
+ * on the addon cap path, and absent has to keep meaning exactly what every
65905
+ * rule authored before this field meant.
65906
+ *
65907
+ * The cost of `true` is stated here because the editor states it too: a rule
65908
+ * that waits also inherits track-close SEMANTICS — its `zones` condition
65909
+ * tests every zone the track visited and a `crossing` condition can no longer
65910
+ * be satisfied, because a closed track carries no crossing.
65911
+ */
65912
+ waitForEnhancement: boolean().optional(),
65913
+ /**
65914
+ * GROUP a burst of subjects into ONE notification that grows.
65915
+ *
65916
+ * Seconds of quiet after the last matching subject before the burst is
65917
+ * considered over. While it is open, the first subject enqueues immediately —
65918
+ * **exactly as today, with no added latency** — and every real growth (a new
65919
+ * subject, or a name confirmed on one already in it) REPLACES that
65920
+ * notification with an updated one naming everybody. The push carries the
65921
+ * group's own coalescing tag, so the phone replaces rather than stacks.
65922
+ *
65923
+ * `0` / absent = off, and off is today's behaviour byte for byte.
65924
+ *
65925
+ * ### Why an idle cutoff and not a window
65926
+ *
65927
+ * The measured seven-person arrival on device 590 spans 110 s with every
65928
+ * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
65929
+ * idle cutoff holds it as one and ends it when the arrival actually ends.
65930
+ * 30 is Frigate's shipped value for the same decision.
65931
+ *
65932
+ * ### What it replaces
65933
+ *
65934
+ * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
65935
+ * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
65936
+ * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
65937
+ * track that did fire and 7 carrying a confirmed identity nobody heard about.
65938
+ * A group collapses the same volume by MERGING, so the cooldown becomes a
65939
+ * budget over GROUPS — which is what it always meant — and a growth is never
65940
+ * throttled by the window its own first member spent.
65941
+ *
65942
+ * ### Interaction with {@link waitForEnhancement}
65943
+ *
65944
+ * They compose, and the order matters. `waitForEnhancement` defers the rule to
65945
+ * TRACK CLOSE, so with both set the group is opened by the first member to
65946
+ * CLOSE — already carrying its name — and grows as later members close. That
65947
+ * is later, and complete. With grouping alone the group opens on the first
65948
+ * object event and picks up names as they are confirmed, through the growth
65949
+ * path. Neither combination fires twice for one subject.
65950
+ *
65951
+ * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
65952
+ * on the addon cap path, so absent must keep meaning what it meant before this
65953
+ * field existed.
65954
+ */
65955
+ groupIdleSec: number().int().min(0).max(600).optional()
65532
65956
  });
65533
65957
  /**
65534
65958
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -65635,6 +66059,7 @@ var NcConditionDescriptorSchema = object({
65635
66059
  "occupancy",
65636
66060
  "audio",
65637
66061
  "deviceState",
66062
+ "scene",
65638
66063
  "systemEvent"
65639
66064
  ]),
65640
66065
  operator: _enum([
@@ -66040,7 +66465,87 @@ var MethodAccessSchema = _enum([
66040
66465
  var AllowedProviderSchema = union([literal("*"), array(string())]);
66041
66466
  var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
66042
66467
  var CapScopeSchema = _enum(["device", "system"]);
66043
- var TokenScopeSchema = discriminatedUnion("type", [
66468
+ /**
66469
+ * DeviceSelector (scope model v3 — 2026-08-12).
66470
+ *
66471
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
66472
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
66473
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
66474
+ * AFTER the grant was minted — no re-grant, no re-login.
66475
+ *
66476
+ * - `all` — every device in the deployment. The broad viewer/operator
66477
+ * lever without a `category` grant (a `category` grant also covers device
66478
+ * caps that carry no deviceId; `all` is specifically the device set).
66479
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
66480
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
66481
+ * is NOT covered until the grant is edited.
66482
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
66483
+ * DYNAMIC. A device that changes type, or a new device of the type,
66484
+ * re-resolves on the next request.
66485
+ * - `locations` — every device whose operator-assigned `location` label is
66486
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
66487
+ * null/unset location matches NO `locations` selector.
66488
+ */
66489
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
66490
+ object({ kind: literal("all") }),
66491
+ object({
66492
+ kind: literal("ids"),
66493
+ ids: array(number().int()).min(1)
66494
+ }),
66495
+ object({
66496
+ kind: literal("types"),
66497
+ types: array(_enum(DeviceType)).min(1)
66498
+ }),
66499
+ object({
66500
+ kind: literal("locations"),
66501
+ locations: array(string().min(1)).min(1)
66502
+ })
66503
+ ]);
66504
+ var DeviceTokenScopeSchema = object({
66505
+ type: literal("device"),
66506
+ /** The device SET this grant covers — resolved against the live fleet. */
66507
+ selector: DeviceSelectorSchema,
66508
+ access: array(MethodAccessSchema).min(1),
66509
+ /**
66510
+ * Whether a grant on a PARENT device transparently covers its accessory
66511
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
66512
+ * Direction is parent → children ONLY.
66513
+ *
66514
+ * Absent → the matcher DERIVES it from the access flavour: `view`
66515
+ * inherits (a camera viewer sees the camera's accessories), `create` /
66516
+ * `delete` do NOT (actuating/removing a child is an explicit act the
66517
+ * operator must grant on the child, not inherit from the parent). Set it
66518
+ * explicitly to override that default per grant.
66519
+ */
66520
+ includeLinked: boolean().optional()
66521
+ });
66522
+ /**
66523
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
66524
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
66525
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
66526
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
66527
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
66528
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
66529
+ * migration cannot reach a JWT already in a client's hands; parse-time
66530
+ * migration covers both without a flag day. No cast — the raw object is read
66531
+ * through `Reflect.get` (its static type is `unknown`).
66532
+ */
66533
+ function migrateLegacyTokenScope(raw) {
66534
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
66535
+ if (Reflect.get(raw, "type") !== "device") return raw;
66536
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
66537
+ const targets = Reflect.get(raw, "targets");
66538
+ if (!Array.isArray(targets)) return raw;
66539
+ return {
66540
+ type: "device",
66541
+ selector: {
66542
+ kind: "ids",
66543
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
66544
+ },
66545
+ access: Reflect.get(raw, "access")
66546
+ };
66547
+ }
66548
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
66044
66549
  object({
66045
66550
  type: literal("category"),
66046
66551
  target: CapScopeSchema,
@@ -66056,18 +66561,8 @@ var TokenScopeSchema = discriminatedUnion("type", [
66056
66561
  target: string(),
66057
66562
  access: array(MethodAccessSchema).min(1)
66058
66563
  }),
66059
- object({
66060
- type: literal("device"),
66061
- /**
66062
- * One or more deviceIds (serialised as strings for wire-format
66063
- * consistency with the rest of the union). Matcher accepts if
66064
- * `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
66065
- * of one scope-per-device when granting access to a set of cameras.
66066
- */
66067
- targets: array(string()).min(1),
66068
- access: array(MethodAccessSchema).min(1)
66069
- })
66070
- ]);
66564
+ DeviceTokenScopeSchema
66565
+ ]));
66071
66566
  object({
66072
66567
  id: string(),
66073
66568
  username: string(),
@@ -66384,7 +66879,7 @@ var TrackEnvelopeSchema = object({
66384
66879
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
66385
66880
  * keeps every scalar the list surfaces actually render (ids, class(es),
66386
66881
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
66387
- * zonesVisited, bestEventId, envelope, hasFace) and returns `positions` /
66882
+ * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
66388
66883
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
66389
66884
  * `getTrack`. Mirrors the event-store `projection` convention
66390
66885
  * (`getObjectEvents` et al.).
@@ -66520,7 +67015,21 @@ union([literal(1), literal(2)]);
66520
67015
  var LabelAttributionSchema = object({
66521
67016
  stepId: string(),
66522
67017
  modelId: string().optional(),
66523
- decidedAt: number()
67018
+ decidedAt: number(),
67019
+ /**
67020
+ * The GALLERY id behind a recognised tier-2 label — a face-gallery
67021
+ * `Identity.id` or a plate-gallery `Vehicle.id` (both `randomUUID`).
67022
+ *
67023
+ * The text alone is a DISPLAY NAME, and a display name is renameable: a
67024
+ * notification rule authored on "Gianluca" stopped matching the moment the
67025
+ * operator fixed the spelling in the gallery, and nothing said so. The id is
67026
+ * the thing that does not move, so it is what a rule matches on
67027
+ * (`NcConditions.identities`) and the text is what a human is shown.
67028
+ *
67029
+ * Absent when the label names no gallery row — a plate the OCR read but no
67030
+ * vehicle claims, a sub-class, a species, any tier-1 value.
67031
+ */
67032
+ identityId: string().optional()
66524
67033
  });
66525
67034
  /**
66526
67035
  * The TIERED label model (roadmap 4g), spread into `TrackSchema` and
@@ -66657,6 +67166,28 @@ var TrackSchema = object({
66657
67166
  * `=== true` and render nothing otherwise, never infer "no face".
66658
67167
  */
66659
67168
  hasFace: boolean().optional(),
67169
+ /**
67170
+ * This subject CONTAINS a folded rider — a person the rider-pairing step
67171
+ * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
67172
+ * so the passage is tracked once and as a VEHICLE.
67173
+ *
67174
+ * It exists because the fold's record was dishonest. D34 and the code both
67175
+ * said "the person is not lost — it is reported so both entities stay on the
67176
+ * record"; in fact the pair went into a per-processor RAM field behind an
67177
+ * accessor nobody called, and every durable surface said `vehicle`, full
67178
+ * stop. This is the composition note that makes the row true.
67179
+ *
67180
+ * A COMPOSITION, never a class and never a label. "This vehicle contains a
67181
+ * person" is not an answer to "what is this" — both label tiers would refuse
67182
+ * a macro token anyway (D89), and correctly. Nothing here changes what the
67183
+ * subject IS: a cyclist stays one vehicle track, occupancy still counts one,
67184
+ * and a `person` rule still does not fire for someone cycling past.
67185
+ *
67186
+ * **Absent ≠ false**, exactly like {@link hasFace}: every row written before
67187
+ * the column, and every hub that predates the field, omits it. Test
67188
+ * `=== true` and render nothing otherwise — never infer "no rider".
67189
+ */
67190
+ hasRider: boolean().optional(),
66660
67191
  ...TrackFlagFields,
66661
67192
  ...TrackRetrainFields
66662
67193
  });
@@ -67006,7 +67537,10 @@ var RecentTracksQueryInput = object({
67006
67537
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
67007
67538
  cursor: string().optional(),
67008
67539
  /** See {@link TrackProjectionSchema}. Default `full`. */
67009
- projection: TrackProjectionSchema.optional()
67540
+ projection: TrackProjectionSchema.optional(),
67541
+ /** Include stationary-promoted rows (parked objects). Default false: the
67542
+ * feed lists passages; parking records live on the stationary registry. */
67543
+ includeStationary: boolean().optional()
67010
67544
  });
67011
67545
  var RecentTracksPageSchema = object({
67012
67546
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -67224,7 +67758,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
67224
67758
  zone: TrackZoneFilterSchema.optional(),
67225
67759
  /** See {@link TrackProjectionSchema}. Default `full` (backward
67226
67760
  * compatible — omitting the field keeps today's exact behaviour). */
67227
- projection: TrackProjectionSchema.optional()
67761
+ projection: TrackProjectionSchema.optional(),
67762
+ /** Include stationary-promoted rows (parked objects handed to the
67763
+ * stationary registry). Default false: the timeline lists passages,
67764
+ * not parking records (operator decision, 2026-08-15). */
67765
+ includeStationary: boolean().optional()
67228
67766
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
67229
67767
  kind: "mutation",
67230
67768
  auth: "admin"
@@ -67388,11 +67926,16 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
67388
67926
  auth: "admin"
67389
67927
  }), method(object({
67390
67928
  eventId: string(),
67391
- kind: MediaFileKindEnum.optional()
67929
+ kind: MediaFileKindEnum.optional(),
67930
+ deviceId: number()
67931
+ }), array(MediaFileSchema).readonly()), method(object({
67932
+ trackId: string(),
67933
+ kinds: array(MediaFileKindEnum).optional(),
67934
+ deviceId: number()
67392
67935
  }), array(MediaFileSchema).readonly()), method(object({
67393
67936
  trackId: string(),
67394
- kinds: array(MediaFileKindEnum).optional()
67395
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
67937
+ deviceId: number()
67938
+ }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
67396
67939
  kind: "mutation",
67397
67940
  auth: "admin"
67398
67941
  }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
@@ -68092,6 +68635,17 @@ var maxSessionHoldMsField = {
68092
68635
  default: 12e4,
68093
68636
  step: 5e3
68094
68637
  };
68638
+ /**
68639
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
68640
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
68641
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
68642
+ */
68643
+ var audioMotionWindowMsField = {
68644
+ min: 5e3,
68645
+ max: 6e5,
68646
+ default: 9e4,
68647
+ step: 5e3
68648
+ };
68095
68649
  var motionFpsField = {
68096
68650
  min: 1,
68097
68651
  max: 30,
@@ -68123,7 +68677,7 @@ var detectionFpsField = {
68123
68677
  var occupancyRecheckSecField = {
68124
68678
  min: 0,
68125
68679
  max: 300,
68126
- default: 30,
68680
+ default: 300,
68127
68681
  step: 5
68128
68682
  };
68129
68683
  var occupancyRecheckFramesField = {
@@ -68268,6 +68822,27 @@ var RunnerCameraConfigSchema = object({
68268
68822
  * resolved `CameraDetectionConfig`.
68269
68823
  */
68270
68824
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
68825
+ /**
68826
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
68827
+ * 'on-motion'` audio window, measured from the LAST motion event.
68828
+ *
68829
+ * This exists because the falling edge cannot be relied on. Camera-native
68830
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
68831
+ * its email-push SMTP path both emit `detected: true` and never the
68832
+ * counterpart); only the frame-diff analyzer emits falls. So on an
68833
+ * onboard-only camera a window that closed only on `detected: false` never
68834
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
68835
+ * battery camera, the one failure mode the mode exists to prevent.
68836
+ *
68837
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
68838
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
68839
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
68840
+ *
68841
+ * Not consumed by the runner: carried here so it shares the per-camera
68842
+ * device-settings surface with `motionCooldownMs`, exactly like
68843
+ * `maxSessionHoldMs`.
68844
+ */
68845
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
68271
68846
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
68272
68847
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
68273
68848
  motionStreamId: string(),
@@ -68363,7 +68938,7 @@ var RunnerCameraConfigSchema = object({
68363
68938
  */
68364
68939
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
68365
68940
  });
68366
- 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;
68941
+ 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;
68367
68942
  /**
68368
68943
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
68369
68944
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -69379,7 +69954,16 @@ targets: array(object({
69379
69954
  /** A sleeping battery camera: the frame is deliberately stale and will
69380
69955
  * NOT refresh in the background. A surface should say so rather than
69381
69956
  * present it as current. */
69382
- sleeping: boolean()
69957
+ sleeping: boolean(),
69958
+ /** Current device state rendered over the cached frame. State images
69959
+ * remain authoritative even when their photographic background is
69960
+ * old; null means the link must carry a current camera frame. */
69961
+ stateReason: _enum([
69962
+ "disabled",
69963
+ "sleeping",
69964
+ "unreachable",
69965
+ "waking"
69966
+ ]).nullable()
69383
69967
  })));
69384
69968
  /**
69385
69969
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -71033,6 +71617,25 @@ var BatteryStatusSchema = object({
71033
71617
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
71034
71618
  lastUpdated: number(),
71035
71619
  /**
71620
+ * Ms epoch of the last time the device PROVED it was reachable — a
71621
+ * completed firmware round-trip, an observed wake, or an inbound push
71622
+ * (firmware event, email). `0`/absent = never since this slice was born.
71623
+ *
71624
+ * This is the ONLY input that separates "asleep" from "gone", and it is
71625
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
71626
+ * for the radio, because a poll that confirms reachability is the same
71627
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
71628
+ * single derivation every consumer must use; no surface computes its own.
71629
+ *
71630
+ * It is deliberately NOT a clock in the
71631
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
71632
+ * observation itself, and it is the only thing a 30-hour silence is
71633
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
71634
+ * Reolink provider) so a value that means "recently" cannot cost a
71635
+ * SQLite commit per round-trip.
71636
+ */
71637
+ lastContactAt: number().optional(),
71638
+ /**
71036
71639
  * True when the source is a BINARY low-battery indicator (HA
71037
71640
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
71038
71641
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -73322,54 +73925,139 @@ var TalkAudioCodecSchema = _enum([
73322
73925
  "g711ulaw",
73323
73926
  "g711alaw"
73324
73927
  ]);
73325
- DeviceType.Camera, method(object({ deviceId: number() }), object({
73326
- sessionId: string(),
73327
- sdpOffer: string()
73328
- }), {
73329
- kind: "mutation",
73330
- auth: "admin"
73331
- }), method(object({
73332
- deviceId: number(),
73333
- sessionId: string(),
73334
- sdpAnswer: string()
73335
- }), _void(), {
73336
- kind: "mutation",
73337
- auth: "admin"
73338
- }), method(object({
73339
- deviceId: number(),
73340
- sessionId: string()
73341
- }), _void(), {
73342
- kind: "mutation",
73343
- auth: "admin"
73344
- }), method(object({ deviceId: number() }), object({ sessionId: string() }), {
73345
- kind: "mutation",
73346
- auth: "admin"
73347
- }), method(object({
73348
- deviceId: number(),
73349
- /** Audio bytes for ONE frame, base64-encoded so the payload
73350
- * survives tRPC JSON serialization. */
73351
- audioBase64: string(),
73352
- /** Wire codec of the payload. Omit to let the provider default
73353
- * to its native expected format (s16le @ provider-native rate,
73354
- * mono). See {@link TalkAudioCodecSchema} for the supported set. */
73355
- codec: TalkAudioCodecSchema.optional(),
73356
- /** Sample rate (Hz). REQUIRED for `s16le`; advisory for
73357
- * `opus` (encoder clock); ignored for `g711*` (implied 8000). */
73358
- sampleRate: number().int().positive().optional(),
73359
- /** Channel count. Default 1. */
73360
- channels: number().int().positive().optional(),
73361
- /** Sequence number for ordering / dropping out-of-order frames. */
73362
- sequenceNumber: number().int()
73363
- }), object({ accepted: boolean() }), {
73364
- kind: "mutation",
73365
- auth: "admin"
73366
- }), method(object({ deviceId: number() }), _void(), {
73367
- kind: "mutation",
73368
- auth: "admin"
73369
- }), object({
73370
- deviceId: number(),
73371
- status: IntercomStatusSchema
73372
- });
73928
+ var intercomCapability = {
73929
+ name: "intercom",
73930
+ scope: "device",
73931
+ deviceNative: true,
73932
+ mode: "singleton",
73933
+ deviceTypes: [DeviceType.Camera],
73934
+ methods: {
73935
+ /**
73936
+ * Open a server-side WebRTC audio-only session. Returns an SDP
73937
+ * offer with a single sendonly audio m-line the client answers
73938
+ * (client → server direction). The server wakes battery cams
73939
+ * transparently before opening the upstream talk channel.
73940
+ */
73941
+ startSession: method(object({ deviceId: number() }), object({
73942
+ sessionId: string(),
73943
+ sdpOffer: string()
73944
+ }), {
73945
+ kind: "mutation",
73946
+ auth: "admin"
73947
+ }),
73948
+ handleAnswer: method(object({
73949
+ deviceId: number(),
73950
+ sessionId: string(),
73951
+ sdpAnswer: string()
73952
+ }), _void(), {
73953
+ kind: "mutation",
73954
+ auth: "admin"
73955
+ }),
73956
+ /** Close explicitly. Server also auto-closes on 30s idle. */
73957
+ stopSession: method(object({
73958
+ deviceId: number(),
73959
+ sessionId: string()
73960
+ }), _void(), {
73961
+ kind: "mutation",
73962
+ auth: "admin"
73963
+ }),
73964
+ /**
73965
+ * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
73966
+ * non-WebRTC consumers (HomeKit export, Alexa raw audio, test
73967
+ * harnesses) that already have decoded PCM frames and just need a
73968
+ * direct path onto the camera's talk channel. Mutually exclusive
73969
+ * with `startSession` (an active WebRTC session must be stopped
73970
+ * before a raw-PCM session can be opened on the same device, and
73971
+ * vice versa).
73972
+ */
73973
+ startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
73974
+ kind: "mutation",
73975
+ auth: "admin"
73976
+ }),
73977
+ /**
73978
+ * Push one chunk of talk-back audio onto the active talk session.
73979
+ * The cap is codec-agnostic: the caller declares (or omits) the
73980
+ * wire format via `codec`; the provider decides between passthrough
73981
+ * (when the wire codec matches the camera's native talk channel),
73982
+ * transcoding via the `audio-codec` cap, or rejecting the call.
73983
+ *
73984
+ * Callers do NOT need to know the camera's wire format or sample
73985
+ * rate — that information lives entirely inside the provider.
73986
+ *
73987
+ * Sequence numbers MUST be monotonic per talk session; older frames
73988
+ * arriving after newer ones are dropped to avoid smearing the
73989
+ * downstream encoder state (G.711 is stateless but IMA ADPCM's
73990
+ * predictor would corrupt with re-ordering).
73991
+ */
73992
+ pushTalkAudio: method(object({
73993
+ deviceId: number(),
73994
+ /** Audio bytes for ONE frame, base64-encoded so the payload
73995
+ * survives tRPC JSON serialization. */
73996
+ audioBase64: string(),
73997
+ /** Wire codec of the payload. Omit to let the provider default
73998
+ * to its native expected format (s16le @ provider-native rate,
73999
+ * mono). See {@link TalkAudioCodecSchema} for the supported set. */
74000
+ codec: TalkAudioCodecSchema.optional(),
74001
+ /** Sample rate (Hz). REQUIRED for `s16le`; advisory for
74002
+ * `opus` (encoder clock); ignored for `g711*` (implied 8000). */
74003
+ sampleRate: number().int().positive().optional(),
74004
+ /** Channel count. Default 1. */
74005
+ channels: number().int().positive().optional(),
74006
+ /** Sequence number for ordering / dropping out-of-order frames. */
74007
+ sequenceNumber: number().int()
74008
+ }), object({ accepted: boolean() }), {
74009
+ kind: "mutation",
74010
+ auth: "admin"
74011
+ }),
74012
+ /** Close the raw-PCM talk session. Idempotent. */
74013
+ endTalkSession: method(object({ deviceId: number() }), _void(), {
74014
+ kind: "mutation",
74015
+ auth: "admin"
74016
+ })
74017
+ },
74018
+ events: { onStatusChanged: { data: object({
74019
+ deviceId: number(),
74020
+ status: IntercomStatusSchema
74021
+ }) } },
74022
+ status: {
74023
+ schema: IntercomStatusSchema,
74024
+ kind: "command-driven"
74025
+ },
74026
+ /**
74027
+ * Runtime-state slice — mirrored by the kernel.
74028
+ *
74029
+ * The cap declared `status` and nothing else, so the only two sources an
74030
+ * exporter has for a value — the `device.state-changed` slice event and the
74031
+ * `deviceState.getAllSnapshots` snapshot, both built from runtime state —
74032
+ * carried nothing for `intercom`. A talk-back entity in Home Assistant would
74033
+ * have been published and never received a value, which is the defect the
74034
+ * export's two classification tables exist to prevent (177 of them, once), so
74035
+ * `intercom` was excluded rather than exported.
74036
+ *
74037
+ * The shape is the status shape: there is exactly one truth about talk-back
74038
+ * and duplicating it into a second schema is how two halves of one capability
74039
+ * come to disagree. Providers write it through
74040
+ * `this.runtimeState.setCapState('intercom', …)` at the four points that open
74041
+ * and close a session, and seed it at registration so the slice exists before
74042
+ * the first session rather than after it.
74043
+ *
74044
+ * **Bound, named rather than hidden:** `talking` mirrors the provider's own
74045
+ * session handle, so a session torn down by a transport death that never
74046
+ * reaches `stopSession` / `endTalkSession` leaves it latched until the next
74047
+ * session or the next restart. That is why the slice is `session` and not
74048
+ * `restored` — a restart must never restore "talking".
74049
+ */
74050
+ runtimeState: IntercomStatusSchema,
74051
+ /**
74052
+ * Runtime-state durability: **session** — `talking` describes a live audio
74053
+ * session, which by definition does not survive the process that held it.
74054
+ * Restoring it would publish a camera as talking to nobody.
74055
+ *
74056
+ * See `RuntimeStateDurability`. Enforced by
74057
+ * `scripts/check-runtime-state-durability.ts`.
74058
+ */
74059
+ durability: "session"
74060
+ };
73373
74061
  /**
73374
74062
  * Robotic lawn-mower cap. Models HA `lawn_mower.*` entities — anything
73375
74063
  * with a mowing lifecycle plus a dock action.
@@ -76066,7 +76754,7 @@ method(object({
76066
76754
  toMs: number()
76067
76755
  }), RecordingAvailabilitySchema, {
76068
76756
  kind: "query",
76069
- auth: "admin"
76757
+ auth: "protected"
76070
76758
  }), method(object({
76071
76759
  deviceId: number(),
76072
76760
  fromMs: number(),
@@ -76074,14 +76762,14 @@ method(object({
76074
76762
  tzOffsetMinutes: number()
76075
76763
  }), RecordingDaysSchema, {
76076
76764
  kind: "query",
76077
- auth: "admin"
76765
+ auth: "protected"
76078
76766
  }), method(object({
76079
76767
  deviceId: number(),
76080
76768
  fromMs: number(),
76081
76769
  toMs: number()
76082
76770
  }), RecordingManifestSchema, {
76083
76771
  kind: "query",
76084
- auth: "admin"
76772
+ auth: "protected"
76085
76773
  }), method(object({}), RecordingStorageUsageSchema, {
76086
76774
  kind: "query",
76087
76775
  auth: "admin"
@@ -76371,14 +77059,77 @@ method(object({
76371
77059
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
76372
77060
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
76373
77061
  *
76374
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
76375
- * derives the device-detail contribution; the provider carries NO hand-written
76376
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
76377
- * every hysteresis flip / availability change; consumers never poll.
77062
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
77063
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
77064
+ * for the thing: a scene is a standing question about the property ("is the bin
77065
+ * still out"), and the operator's question is "which of my scenes have tripped",
77066
+ * across every camera at once — not "what does camera 617 think". Buried one
77067
+ * camera deep it also could not be found. The surface is now a top-level admin
77068
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
77069
+ * picks the camera inside the create flow, the same shape Events and Faces have.
77070
+ *
77071
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
77072
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
77073
+ * directions, so a registration nobody declares fails exactly as loudly as a
77074
+ * declaration nobody registers. The editor is imported directly by the page.
77075
+ *
77076
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
77077
+ * availability change; consumers never poll.
76378
77078
  */
76379
- /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
76380
- * be added without a wire break (matching falls back to any-condition refs). */
77079
+ /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
77080
+ * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
77081
+ * Open by design so more can be added without a wire break.
77082
+ *
77083
+ * Matching does NOT fall back across conditions: cross-condition cosines are
77084
+ * not comparable, so "I have never seen this scene in this light" is reported
77085
+ * as `unknown`, never guessed. A day reference scored against an IR frame
77086
+ * collapses the cosine and would latch a false alarm every single night. */
76381
77087
  var SceneConditionSchema = string();
77088
+ /**
77089
+ * What a scene does when the CURRENT light has no reference of its own.
77090
+ *
77091
+ * The lighting variants are not equally likely to exist. Almost every operator
77092
+ * captures daylight and then never stands outside at 22:00 to capture IR, and a
77093
+ * scene that is only ever going to be asked about a daytime question ("is the
77094
+ * bin still on the kerb at 08:00") does not need a night reference at all. The
77095
+ * night half must therefore be OPTIONAL, and optional means the scene keeps
77096
+ * working without it rather than degrading into a permanent complaint.
77097
+ *
77098
+ * - `skip` (default) — the check in that light is not made. Not a verdict, not
77099
+ * an alarm, not even an `unknown`: the live state simply stays whatever the
77100
+ * last covered light left it at, the latch is untouched, and the hysteresis
77101
+ * run is neither spent nor cleared. The scene resumes by itself at first
77102
+ * light. This is the only behaviour under which "I never captured IR" is a
77103
+ * configuration choice instead of a nightly fault.
77104
+ * - `judge-anyway` — score against the OTHER conditions' references. Available
77105
+ * for cameras whose IR frame is close enough to daylight (a floodlit
77106
+ * driveway, an always-white-light doorbell), and wrong for everything else:
77107
+ * cross-condition cosines are not comparable, so a day reference against a
77108
+ * true IR frame collapses and the scene reports a theft at 21:40.
77109
+ *
77110
+ * Never applies when the scene has NO comparable reference at all — that is
77111
+ * "not armed yet", it is reported as `no-reference-for-condition`, and silence
77112
+ * there would hide a scene the operator never finished setting up.
77113
+ */
77114
+ var SceneUncoveredPolicySchema = _enum(["skip", "judge-anyway"]);
77115
+ /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
77116
+ * `unknown` = we cannot judge (no reference for this condition, encoder model
77117
+ * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
77118
+ * and never counts toward hysteresis in either direction. */
77119
+ var SceneVerdictSchema = _enum([
77120
+ "matched",
77121
+ "diverged",
77122
+ "unknown"
77123
+ ]);
77124
+ /** Why a scene cannot judge. Named, because this feature's failure mode is
77125
+ * silence that reads as "nothing has happened". */
77126
+ var SceneUnavailableSchema = _enum([
77127
+ "no-reference-for-condition",
77128
+ "view-shifted",
77129
+ "no-vision-profile",
77130
+ "encoder-model-changed",
77131
+ "no-snapshot"
77132
+ ]);
76382
77133
  /** One captured reference — condition-tagged, model-version-gated. `embedding`
76383
77134
  * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
76384
77135
  var SceneReferenceSchema = object({
@@ -76386,7 +77137,14 @@ var SceneReferenceSchema = object({
76386
77137
  modelId: string(),
76387
77138
  condition: SceneConditionSchema,
76388
77139
  capturedAt: number(),
76389
- thumbnailMediaId: string().optional()
77140
+ thumbnailMediaId: string().optional(),
77141
+ /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
77142
+ * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
77143
+ * normalized rect frame a different piece of world, and the scene would
77144
+ * diverge forever with a perfectly plausible cosine. Checked LAZILY, only
77145
+ * when hysteresis is about to flip — one extra encode per candidate
77146
+ * transition, not per poll. */
77147
+ anchorEmbedding: array(number()).optional()
76390
77148
  });
76391
77149
  var SceneMonitorStateSchema = object({
76392
77150
  id: string(),
@@ -76408,6 +77166,28 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
76408
77166
  profileId: string().optional(),
76409
77167
  hysteresisCount: number().int().positive()
76410
77168
  })]);
77169
+ var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
77170
+ /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
77171
+ * out in silence rather than reporting a fault every night. */
77172
+ var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
77173
+ /**
77174
+ * Vision-model adjudication of a candidate flip. Field names deliberately
77175
+ * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
77176
+ *
77177
+ * `onTimeout` defaults to **'hold'**, the OPPOSITE of `NcConfirmGate`'s
77178
+ * fail-open: a notification suppressed is the worse error there, but a vision
77179
+ * model that timed out has not told us the bin is gone, and a latch is a
77180
+ * stateful claim that costs the operator a trip to reset.
77181
+ */
77182
+ var SceneConfirmSchema = object({
77183
+ enabled: boolean().default(false),
77184
+ prompt: string().min(1).max(1e3),
77185
+ profileId: string().optional(),
77186
+ timeoutMs: number().int().min(1e3).max(2e4).default(8e3),
77187
+ maxImagePx: number().int().min(64).max(2048).default(448),
77188
+ /** What a timeout / unavailable model means for the PENDING flip. */
77189
+ onTimeout: _enum(["flip", "hold"]).default("hold")
77190
+ });
76411
77191
  var SceneMonitorSchema = object({
76412
77192
  id: string(),
76413
77193
  label: string(),
@@ -76426,7 +77206,56 @@ var SceneMonitorSchema = object({
76426
77206
  lastConfidence: number().nullable(),
76427
77207
  currentCondition: SceneConditionSchema.nullable(),
76428
77208
  availability: _enum(["ok", "unavailable"]),
76429
- unavailableReason: string().nullable()
77209
+ unavailableReason: string().nullable(),
77210
+ /** Which state is "the initial screen". `null` until the first capture. */
77211
+ baselineStateId: string().nullable(),
77212
+ /** Which boolean drives notification rules and any export. */
77213
+ emit: _enum(["latched", "live"]).default("latched"),
77214
+ /** Live: does the region match the baseline RIGHT NOW. */
77215
+ verdict: SceneVerdictSchema,
77216
+ /** Has it been `diverged` at least once since `armedAt` — the operator's boolean. */
77217
+ latched: boolean(),
77218
+ /** Last reset (or creation). */
77219
+ armedAt: number(),
77220
+ divergedAt: number().nullable(),
77221
+ restoredAt: number().nullable(),
77222
+ /** A check is only COUNTED when the device has been quiet this long. Motion
77223
+ * during the window DISCARDS the observation — a car pulling up in front of
77224
+ * the bin must not be able to spend hysteresis credit. */
77225
+ quietSeconds: number().int().min(0).max(3600).default(60),
77226
+ /** An observation only advances the pending count when it is at least this
77227
+ * far from the previously counted one, so N agreeing checks span real time
77228
+ * rather than N adjacent polls inside one occlusion. */
77229
+ minObservationSpacingSec: number().int().min(0).max(3600).default(120),
77230
+ /** Vision-model adjudication of a candidate flip. Similarity primary only. */
77231
+ confirm: SceneConfirmSchema.optional(),
77232
+ /** Whole-frame anchor cosine below which a flip is REFUSED as `view-shifted`. */
77233
+ anchorThreshold: number().min(0).max(1).default(SCENE_DEFAULT_ANCHOR_THRESHOLD),
77234
+ /** Clear the latch on its own when the scene matches again? Default false —
77235
+ * `restoredAt` and the `scene-restored` edge are recorded regardless, so an
77236
+ * automation can react to the bin coming back without the operator's own
77237
+ * alarm silently clearing itself. */
77238
+ autoRestore: boolean().default(false),
77239
+ /** What to do when the current light has no reference of its own. See
77240
+ * {@link SceneUncoveredPolicySchema} — the default makes night OPTIONAL. */
77241
+ onUncoveredCondition: SceneUncoveredPolicySchema.default(SCENE_DEFAULT_UNCOVERED_POLICY),
77242
+ /**
77243
+ * The light whose checks are currently being SAT OUT under
77244
+ * `onUncoveredCondition: 'skip'` — `null` when the scene is checking normally.
77245
+ *
77246
+ * Engine-reported and advisory only: it moves no verdict, no latch and no
77247
+ * hysteresis. It exists so the card can say *"night (IR) — checks paused,
77248
+ * nothing captured in this light"* in the same calm voice as the coverage
77249
+ * line, because the alternative is a scene that silently stops answering
77250
+ * after sunset with nothing anywhere saying why. A skipped check must never
77251
+ * read as a broken one.
77252
+ */
77253
+ suspendedCondition: SceneConditionSchema.nullable().default(null),
77254
+ /** Named cause when `verdict === 'unknown'`. */
77255
+ unavailable: SceneUnavailableSchema.nullable(),
77256
+ /** Conditions that have at least one comparable reference — the coverage line
77257
+ * ("day ✓ · ir ✓ · dusk ✗") that turns a silent fallback into a visible fact. */
77258
+ coveredConditions: array(SceneConditionSchema)
76430
77259
  });
76431
77260
  var SceneMonitorStatusSchema = object({
76432
77261
  monitors: array(SceneMonitorSchema),
@@ -76439,12 +77268,6 @@ var sceneMonitorCapability = {
76439
77268
  kind: "wrapper",
76440
77269
  defaultActive: true,
76441
77270
  deviceTypes: [DeviceType.Camera],
76442
- deviceConfig: { ui: {
76443
- kind: "widget",
76444
- widgetId: "host/scene-monitor-editor",
76445
- tab: "scenes",
76446
- label: "Scenes"
76447
- } },
76448
77271
  methods: {
76449
77272
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
76450
77273
  createScene: method(object({
@@ -76475,7 +77298,15 @@ var sceneMonitorCapability = {
76475
77298
  "both"
76476
77299
  ]).optional(),
76477
77300
  checkIntervalSec: number().optional(),
76478
- check: SceneCheckSchema.optional()
77301
+ check: SceneCheckSchema.optional(),
77302
+ emit: _enum(["latched", "live"]).optional(),
77303
+ quietSeconds: number().int().min(0).max(3600).optional(),
77304
+ minObservationSpacingSec: number().int().min(0).max(3600).optional(),
77305
+ anchorThreshold: number().min(0).max(1).optional(),
77306
+ autoRestore: boolean().optional(),
77307
+ onUncoveredCondition: SceneUncoveredPolicySchema.optional(),
77308
+ /** `null` clears the vision-model adjudicator. */
77309
+ confirm: SceneConfirmSchema.nullable().optional()
76479
77310
  })
76480
77311
  }), _void(), {
76481
77312
  kind: "mutation",
@@ -76516,6 +77347,26 @@ var sceneMonitorCapability = {
76516
77347
  }), _void(), {
76517
77348
  kind: "mutation",
76518
77349
  auth: "admin"
77350
+ }),
77351
+ /**
77352
+ * Clear the latch, re-arm, and — by default — RE-CAPTURE the baseline for
77353
+ * the CURRENT condition. The bin never goes back in exactly the same spot;
77354
+ * "reset" in the operator's head means *this is the new normal*, and
77355
+ * re-capture is what makes the feature self-healing against slow drift
77356
+ * instead of failing silently weeks later.
77357
+ *
77358
+ * Reachable from three surfaces on this one mutation: the scene card, a
77359
+ * notification button (an `onTrigger` sequence with a `kind:'cap'` step —
77360
+ * no new Notification-Center code at all), and tRPC for scripts.
77361
+ */
77362
+ resetScene: method(object({
77363
+ deviceId: number(),
77364
+ monitorId: string(),
77365
+ /** Defaults to TRUE at the provider seam — see `SCENE_RESET_RECAPTURES`. */
77366
+ recapture: boolean().optional()
77367
+ }), _void(), {
77368
+ kind: "mutation",
77369
+ auth: "admin"
76519
77370
  })
76520
77371
  },
76521
77372
  status: {
@@ -76752,7 +77603,70 @@ var CamStreamDescriptorSchema = object({
76752
77603
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
76753
77604
  metadata: record(string(), unknown()).optional()
76754
77605
  });
76755
- DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
77606
+ /**
77607
+ * `stream-catalog` — device-scoped, provider-implemented. The pull counterpart
77608
+ * of the removed `publishCameraStream` push: a camera provider returns the full
77609
+ * set of stream descriptors it can offer for the device, synchronously, so the
77610
+ * broker can reconcile its registry against the authoritative provider state.
77611
+ */
77612
+ /**
77613
+ * The catalog as a DURABLE fact rather than a live answer.
77614
+ *
77615
+ * A battery camera's descriptors are profile-stable — they change when the
77616
+ * operator rewrites an encoder profile, not minute to minute — but building
77617
+ * them costs a Baichuan login, which on a sleeping Argus IS a wake. So the
77618
+ * provider is allowed to build them exactly once per profile and must serve
77619
+ * every later pull from a cache.
77620
+ *
77621
+ * Holding that cache only in RAM is what turned a restart into an outage. The
77622
+ * runner comes back with the camera asleep, `buildStreamCatalogUncached`
77623
+ * correctly refuses to wake it, the pull answers `[]`, the broker has no
77624
+ * cam-stream entry to build a broker from, and `webrtcSession.handleOffer`
77625
+ * fails with a flat "No broker for stream" — for as long as the camera sleeps,
77626
+ * which on a battery cam is most of the day. The camera was fine. The stream
77627
+ * was unreachable because the process had forgotten what the camera offers.
77628
+ *
77629
+ * Declaring it here puts it in `device-runtime-state`, the kernel's canonical
77630
+ * declared collection, with the same `restored` durability `battery` uses for
77631
+ * the same reason: the last known value is the only value there is while the
77632
+ * device is asleep. The broker's brokers are therefore always DEFINABLE — it
77633
+ * is the DIAL that wakes a camera, never the catalog (D173).
77634
+ */
77635
+ var StreamCatalogStateSchema = object({
77636
+ /** The descriptors as last built from a real camera response. Never a guess:
77637
+ * a failed or refused build writes NOTHING, so a restored catalog is always
77638
+ * one the camera itself once produced. */
77639
+ descriptors: array(CamStreamDescriptorSchema),
77640
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
77641
+ * path decide whether the camera's own awake window is worth spending on a
77642
+ * re-read. */
77643
+ lastFetchedAt: number()
77644
+ });
77645
+ var streamCatalogCapability = {
77646
+ name: "stream-catalog",
77647
+ scope: "device",
77648
+ deviceNative: true,
77649
+ mode: "singleton",
77650
+ deviceTypes: [DeviceType.Camera],
77651
+ methods: { getCatalog: method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly()) },
77652
+ runtimeState: StreamCatalogStateSchema,
77653
+ /**
77654
+ * Runtime-state durability: **restored** — see the schema doc. A cold
77655
+ * catalog on a sleeping battery camera is not a slow first frame, it is a
77656
+ * camera that cannot be watched at all until it happens to wake.
77657
+ *
77658
+ * Churn is nil by construction: the slice is written only by a SUCCESSFUL
77659
+ * build, and a build only runs when there is no cached copy (or the copy is
77660
+ * a day old and the camera is awake anyway).
77661
+ *
77662
+ * See `RuntimeStateDurability`. Enforced by
77663
+ * `scripts/check-runtime-state-durability.ts`.
77664
+ */
77665
+ durability: "restored",
77666
+ /** Clock field: written, but excluded from the compare that decides whether
77667
+ * persisting is worth a SQLite commit — the descriptors are the value. */
77668
+ volatileStateFields: ["lastFetchedAt"]
77669
+ };
76756
77670
  /** One of the camera's stream profiles. */
76757
77671
  var StreamProfileSchema = _enum([
76758
77672
  "main",
@@ -77006,12 +77920,64 @@ var NetworkAddressSchema = object({
77006
77920
  family: string(),
77007
77921
  internal: boolean()
77008
77922
  });
77923
+ /**
77924
+ * Provenance of the site coordinates, and the whole reason this is not just two
77925
+ * numbers.
77926
+ *
77927
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
77928
+ * nothing overwrites it.
77929
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
77930
+ * default that is right to a few kilometres beats the coarse UTC clock split
77931
+ * the sun-times consumers otherwise fall back to.
77932
+ *
77933
+ * The UI shows which one it is. An operator who cannot tell a guess from their
77934
+ * own input will eventually trust the guess.
77935
+ */
77936
+ var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
77937
+ /**
77938
+ * The read shape: the location plus the honest state of the one-shot derivation.
77939
+ *
77940
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
77941
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
77942
+ * failed; the hub will NOT try again on its own — the fallback is declared
77943
+ * (consumers degrade to their own last resort) and the operator either types the
77944
+ * coordinates or presses detect.
77945
+ */
77946
+ var SiteLocationStatusSchema = object({
77947
+ location: object({
77948
+ /** WGS84 decimal degrees. */
77949
+ latitude: number().min(-90).max(90),
77950
+ longitude: number().min(-180).max(180),
77951
+ source: SiteLocationSourceSchema,
77952
+ /** Epoch ms the value was last written. */
77953
+ updatedAt: number(),
77954
+ /**
77955
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
77956
+ * only — never parsed, never matched on. Absent for an operator-typed value.
77957
+ */
77958
+ label: string().optional()
77959
+ }).nullable(),
77960
+ derivationAttemptedAt: number().nullable(),
77961
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
77962
+ derivationError: string().nullable()
77963
+ });
77964
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
77965
+ var SetSiteLocationInputSchema = object({
77966
+ latitude: number().min(-90).max(90),
77967
+ longitude: number().min(-180).max(180)
77968
+ }).nullable();
77009
77969
  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(), {
77010
77970
  kind: "mutation",
77011
77971
  auth: "admin"
77012
77972
  }), method(_void(), _void(), {
77013
77973
  kind: "mutation",
77014
77974
  auth: "admin"
77975
+ }), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
77976
+ kind: "mutation",
77977
+ auth: "admin"
77978
+ }), method(_void(), SiteLocationStatusSchema, {
77979
+ kind: "mutation",
77980
+ auth: "admin"
77015
77981
  });
77016
77982
  /**
77017
77983
  * Tamper / case-open detection sensor. Drives Home Assistant
@@ -78331,6 +79297,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
78331
79297
  humiditySensor: humiditySensorCapability,
78332
79298
  image: imageCapability,
78333
79299
  imageSettings: imageSettingsCapability,
79300
+ intercom: intercomCapability,
78334
79301
  lawnMowerControl: lawnMowerControlCapability,
78335
79302
  lockControl: lockControlCapability,
78336
79303
  mediaPlayer: mediaPlayerCapability,
@@ -78349,6 +79316,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
78349
79316
  sceneMonitor: sceneMonitorCapability,
78350
79317
  scriptRunner: scriptRunnerCapability,
78351
79318
  smoke: smokeCapability,
79319
+ streamCatalog: streamCatalogCapability,
78352
79320
  streamParams: streamParamsCapability,
78353
79321
  switch: switchCapability,
78354
79322
  tamper: tamperCapability,
@@ -79002,6 +79970,15 @@ var BaseDeviceProvider = class extends BaseAddon {
79002
79970
  labels: ["probe not implemented"]
79003
79971
  };
79004
79972
  }
79973
+ /**
79974
+ * Top-level devices restored at once in {@link onRestoreDevices}.
79975
+ *
79976
+ * Four covers the fleets this ships to without turning a boot into a burst a
79977
+ * camera NVR answers with a refusal. A provider whose upstream is a single
79978
+ * session with a serial command channel (a Baichuan hub, an NVR that
79979
+ * serialises ISAPI) should lower it; nothing needs to raise it.
79980
+ */
79981
+ restoreConcurrency = 4;
79005
79982
  async restoreDevices(savedDevices) {
79006
79983
  await this.onRestoreDevices(savedDevices);
79007
79984
  if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
@@ -79033,15 +80010,15 @@ var BaseDeviceProvider = class extends BaseAddon {
79033
80010
  */
79034
80011
  async onRestoreDevices(savedDevices) {
79035
80012
  const restored = /* @__PURE__ */ new Set();
79036
- for (const saved of savedDevices) {
79037
- if (saved.parentDeviceId !== null) continue;
80013
+ const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
80014
+ const restoreOne = async (saved) => {
79038
80015
  const Class = this.deviceClasses[saved.type];
79039
80016
  if (!Class) {
79040
80017
  this.ctx.logger.warn("No device class registered for restored type — skipping", {
79041
80018
  tags: { stableId: saved.stableId },
79042
80019
  meta: { type: saved.type }
79043
80020
  });
79044
- continue;
80021
+ return;
79045
80022
  }
79046
80023
  try {
79047
80024
  await this.ctx.kernel.devices.create(saved.stableId, Class, {});
@@ -79055,7 +80032,15 @@ var BaseDeviceProvider = class extends BaseAddon {
79055
80032
  }
79056
80033
  });
79057
80034
  }
79058
- }
80035
+ };
80036
+ let nextTopLevel = 0;
80037
+ await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
80038
+ for (;;) {
80039
+ const saved = topLevel[nextTopLevel++];
80040
+ if (saved === void 0) return;
80041
+ await restoreOne(saved);
80042
+ }
80043
+ }));
79059
80044
  const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
79060
80045
  for (const saved of childRows) {
79061
80046
  const Class = this.deviceClasses[saved.type];
@@ -81146,6 +82131,12 @@ Object.freeze({
81146
82131
  addonId: null,
81147
82132
  access: "create"
81148
82133
  },
82134
+ "llm.cancel": {
82135
+ capName: "llm",
82136
+ capScope: "system",
82137
+ addonId: null,
82138
+ access: "create"
82139
+ },
81149
82140
  "llm.deleteModel": {
81150
82141
  capName: "llm",
81151
82142
  capScope: "system",
@@ -81230,6 +82221,12 @@ Object.freeze({
81230
82221
  addonId: null,
81231
82222
  access: "view"
81232
82223
  },
82224
+ "llm.resolveModelRef": {
82225
+ capName: "llm",
82226
+ capScope: "system",
82227
+ addonId: null,
82228
+ access: "create"
82229
+ },
81233
82230
  "llm.setDefault": {
81234
82231
  capName: "llm",
81235
82232
  capScope: "system",
@@ -83396,6 +84393,12 @@ Object.freeze({
83396
84393
  addonId: null,
83397
84394
  access: "create"
83398
84395
  },
84396
+ "sceneMonitor.resetScene": {
84397
+ capName: "scene-monitor",
84398
+ capScope: "device",
84399
+ addonId: null,
84400
+ access: "delete"
84401
+ },
83399
84402
  "sceneMonitor.updateScene": {
83400
84403
  capName: "scene-monitor",
83401
84404
  capScope: "device",
@@ -84074,6 +85077,12 @@ Object.freeze({
84074
85077
  addonId: null,
84075
85078
  access: "create"
84076
85079
  },
85080
+ "system.detectSiteLocation": {
85081
+ capName: "system",
85082
+ capScope: "system",
85083
+ addonId: null,
85084
+ access: "create"
85085
+ },
84077
85086
  "system.featureFlags": {
84078
85087
  capName: "system",
84079
85088
  capScope: "system",
@@ -84092,6 +85101,12 @@ Object.freeze({
84092
85101
  addonId: null,
84093
85102
  access: "view"
84094
85103
  },
85104
+ "system.getSiteLocation": {
85105
+ capName: "system",
85106
+ capScope: "system",
85107
+ addonId: null,
85108
+ access: "view"
85109
+ },
84095
85110
  "system.health": {
84096
85111
  capName: "system",
84097
85112
  capScope: "system",
@@ -84116,6 +85131,12 @@ Object.freeze({
84116
85131
  addonId: null,
84117
85132
  access: "create"
84118
85133
  },
85134
+ "system.setSiteLocation": {
85135
+ capName: "system",
85136
+ capScope: "system",
85137
+ addonId: null,
85138
+ access: "create"
85139
+ },
84119
85140
  "terminalSession.adoptLegacyMonitor": {
84120
85141
  capName: "terminal-session",
84121
85142
  capScope: "system",
@@ -84687,6 +85708,1704 @@ Object.freeze({
84687
85708
  access: "create"
84688
85709
  }
84689
85710
  });
85711
+ Object.freeze({
85712
+ "accessories.setChildHidden": [{
85713
+ name: "childDeviceId",
85714
+ form: "single",
85715
+ optional: false
85716
+ }, {
85717
+ name: "deviceId",
85718
+ form: "single",
85719
+ optional: false
85720
+ }],
85721
+ "addonSettings.getDeviceSettings": [{
85722
+ name: "deviceId",
85723
+ form: "single",
85724
+ optional: false
85725
+ }],
85726
+ "addonSettings.updateDeviceSettings": [{
85727
+ name: "deviceId",
85728
+ form: "single",
85729
+ optional: false
85730
+ }],
85731
+ "alarmPanel.arm": [{
85732
+ name: "deviceId",
85733
+ form: "single",
85734
+ optional: false
85735
+ }],
85736
+ "alarmPanel.disarm": [{
85737
+ name: "deviceId",
85738
+ form: "single",
85739
+ optional: false
85740
+ }],
85741
+ "alarmPanel.trigger": [{
85742
+ name: "deviceId",
85743
+ form: "single",
85744
+ optional: false
85745
+ }],
85746
+ "audioAnalysis.resolveDeviceSettings": [{
85747
+ name: "deviceId",
85748
+ form: "single",
85749
+ optional: false
85750
+ }],
85751
+ "audioAnalyzer.classify": [{
85752
+ name: "deviceId",
85753
+ form: "single",
85754
+ optional: true
85755
+ }],
85756
+ "audioMetrics.getCurrentSnapshot": [{
85757
+ name: "deviceId",
85758
+ form: "single",
85759
+ optional: false
85760
+ }],
85761
+ "audioMetrics.getHistory": [{
85762
+ name: "deviceId",
85763
+ form: "single",
85764
+ optional: false
85765
+ }],
85766
+ "automationControl.disable": [{
85767
+ name: "deviceId",
85768
+ form: "single",
85769
+ optional: false
85770
+ }],
85771
+ "automationControl.enable": [{
85772
+ name: "deviceId",
85773
+ form: "single",
85774
+ optional: false
85775
+ }],
85776
+ "automationControl.trigger": [{
85777
+ name: "deviceId",
85778
+ form: "single",
85779
+ optional: false
85780
+ }],
85781
+ "battery.wakeForStream": [{
85782
+ name: "deviceId",
85783
+ form: "single",
85784
+ optional: false
85785
+ }],
85786
+ "brightness.setBrightness": [{
85787
+ name: "deviceId",
85788
+ form: "single",
85789
+ optional: false
85790
+ }],
85791
+ "button.press": [{
85792
+ name: "deviceId",
85793
+ form: "single",
85794
+ optional: false
85795
+ }],
85796
+ "cameraCredentials.getCredentials": [{
85797
+ name: "deviceId",
85798
+ form: "single",
85799
+ optional: false
85800
+ }],
85801
+ "cameraStreams.getBrokerStreams": [{
85802
+ name: "deviceId",
85803
+ form: "single",
85804
+ optional: false
85805
+ }],
85806
+ "cameraStreams.getCameraStreams": [{
85807
+ name: "deviceId",
85808
+ form: "single",
85809
+ optional: false
85810
+ }],
85811
+ "cameraStreams.getProfileRtspEntries": [{
85812
+ name: "deviceId",
85813
+ form: "single",
85814
+ optional: false
85815
+ }],
85816
+ "cameraStreams.getRtspEntries": [{
85817
+ name: "deviceId",
85818
+ form: "single",
85819
+ optional: false
85820
+ }],
85821
+ "cameraStreams.pickStream": [{
85822
+ name: "deviceId",
85823
+ form: "single",
85824
+ optional: false
85825
+ }],
85826
+ "climateControl.setFanMode": [{
85827
+ name: "deviceId",
85828
+ form: "single",
85829
+ optional: false
85830
+ }],
85831
+ "climateControl.setMode": [{
85832
+ name: "deviceId",
85833
+ form: "single",
85834
+ optional: false
85835
+ }],
85836
+ "climateControl.setPreset": [{
85837
+ name: "deviceId",
85838
+ form: "single",
85839
+ optional: false
85840
+ }],
85841
+ "climateControl.setSwingHorizontal": [{
85842
+ name: "deviceId",
85843
+ form: "single",
85844
+ optional: false
85845
+ }],
85846
+ "climateControl.setSwingVertical": [{
85847
+ name: "deviceId",
85848
+ form: "single",
85849
+ optional: false
85850
+ }],
85851
+ "climateControl.setTarget": [{
85852
+ name: "deviceId",
85853
+ form: "single",
85854
+ optional: false
85855
+ }],
85856
+ "climateControl.setTargetHumidity": [{
85857
+ name: "deviceId",
85858
+ form: "single",
85859
+ optional: false
85860
+ }],
85861
+ "climateControl.setTargetRange": [{
85862
+ name: "deviceId",
85863
+ form: "single",
85864
+ optional: false
85865
+ }],
85866
+ "color.setColor": [{
85867
+ name: "deviceId",
85868
+ form: "single",
85869
+ optional: false
85870
+ }],
85871
+ "consumables.reset": [{
85872
+ name: "deviceId",
85873
+ form: "single",
85874
+ optional: false
85875
+ }],
85876
+ "control.setValue": [{
85877
+ name: "deviceId",
85878
+ form: "single",
85879
+ optional: false
85880
+ }],
85881
+ "cover.close": [{
85882
+ name: "deviceId",
85883
+ form: "single",
85884
+ optional: false
85885
+ }],
85886
+ "cover.open": [{
85887
+ name: "deviceId",
85888
+ form: "single",
85889
+ optional: false
85890
+ }],
85891
+ "cover.setPosition": [{
85892
+ name: "deviceId",
85893
+ form: "single",
85894
+ optional: false
85895
+ }],
85896
+ "cover.setTiltPosition": [{
85897
+ name: "deviceId",
85898
+ form: "single",
85899
+ optional: false
85900
+ }],
85901
+ "cover.stop": [{
85902
+ name: "deviceId",
85903
+ form: "single",
85904
+ optional: false
85905
+ }],
85906
+ "dayNight.getOptions": [{
85907
+ name: "deviceId",
85908
+ form: "single",
85909
+ optional: false
85910
+ }],
85911
+ "dayNight.setSettings": [{
85912
+ name: "deviceId",
85913
+ form: "single",
85914
+ optional: false
85915
+ }],
85916
+ "decoder.createSession": [{
85917
+ name: "deviceId",
85918
+ form: "single",
85919
+ optional: true
85920
+ }],
85921
+ "deviceAdoption.release": [{
85922
+ name: "camDeviceId",
85923
+ form: "single",
85924
+ optional: false
85925
+ }],
85926
+ "deviceAdoption.resync": [{
85927
+ name: "camDeviceId",
85928
+ form: "single",
85929
+ optional: false
85930
+ }],
85931
+ "deviceDiscovery.adoptDevice": [{
85932
+ name: "deviceId",
85933
+ form: "single",
85934
+ optional: false
85935
+ }],
85936
+ "deviceDiscovery.listDiscovered": [{
85937
+ name: "deviceId",
85938
+ form: "single",
85939
+ optional: false
85940
+ }],
85941
+ "deviceDiscovery.refreshDiscovery": [{
85942
+ name: "deviceId",
85943
+ form: "single",
85944
+ optional: false
85945
+ }],
85946
+ "deviceDiscovery.releaseDevice": [{
85947
+ name: "childDeviceId",
85948
+ form: "single",
85949
+ optional: false
85950
+ }, {
85951
+ name: "deviceId",
85952
+ form: "single",
85953
+ optional: false
85954
+ }],
85955
+ "deviceManager.adoptionRelease": [{
85956
+ name: "camDeviceId",
85957
+ form: "single",
85958
+ optional: false
85959
+ }],
85960
+ "deviceManager.adoptionResync": [{
85961
+ name: "camDeviceId",
85962
+ form: "single",
85963
+ optional: false
85964
+ }],
85965
+ "deviceManager.applyInitialMeta": [{
85966
+ name: "deviceId",
85967
+ form: "single",
85968
+ optional: false
85969
+ }, {
85970
+ name: "linkDeviceId",
85971
+ form: "single",
85972
+ optional: true
85973
+ }],
85974
+ "deviceManager.disable": [{
85975
+ name: "deviceId",
85976
+ form: "single",
85977
+ optional: false
85978
+ }],
85979
+ "deviceManager.enable": [{
85980
+ name: "deviceId",
85981
+ form: "single",
85982
+ optional: false
85983
+ }],
85984
+ "deviceManager.getBindings": [{
85985
+ name: "deviceId",
85986
+ form: "single",
85987
+ optional: false
85988
+ }],
85989
+ "deviceManager.getChildren": [{
85990
+ name: "parentDeviceId",
85991
+ form: "single",
85992
+ optional: false
85993
+ }],
85994
+ "deviceManager.getConfigSchema": [{
85995
+ name: "deviceId",
85996
+ form: "single",
85997
+ optional: false
85998
+ }],
85999
+ "deviceManager.getDevice": [{
86000
+ name: "deviceId",
86001
+ form: "single",
86002
+ optional: false
86003
+ }],
86004
+ "deviceManager.getDeviceAggregate": [{
86005
+ name: "deviceId",
86006
+ form: "single",
86007
+ optional: false
86008
+ }],
86009
+ "deviceManager.getDeviceLiveInfoAggregate": [{
86010
+ name: "deviceId",
86011
+ form: "single",
86012
+ optional: false
86013
+ }],
86014
+ "deviceManager.getDeviceSettingsAggregate": [{
86015
+ name: "deviceId",
86016
+ form: "single",
86017
+ optional: false
86018
+ }],
86019
+ "deviceManager.getDeviceStatusAggregate": [{
86020
+ name: "deviceId",
86021
+ form: "single",
86022
+ optional: false
86023
+ }],
86024
+ "deviceManager.getDeviceStatusAggregateBatch": [{
86025
+ name: "deviceIds",
86026
+ form: "array",
86027
+ optional: false
86028
+ }],
86029
+ "deviceManager.getLinkedDevices": [{
86030
+ name: "deviceId",
86031
+ form: "single",
86032
+ optional: false
86033
+ }],
86034
+ "deviceManager.getSettingsSchema": [{
86035
+ name: "deviceId",
86036
+ form: "single",
86037
+ optional: false
86038
+ }],
86039
+ "deviceManager.getStreamProfileMap": [{
86040
+ name: "deviceId",
86041
+ form: "single",
86042
+ optional: false
86043
+ }],
86044
+ "deviceManager.getStreamSources": [{
86045
+ name: "deviceId",
86046
+ form: "single",
86047
+ optional: false
86048
+ }],
86049
+ "deviceManager.getWireableFields": [{
86050
+ name: "deviceId",
86051
+ form: "single",
86052
+ optional: false
86053
+ }],
86054
+ "deviceManager.loadConfig": [{
86055
+ name: "deviceId",
86056
+ form: "single",
86057
+ optional: false
86058
+ }],
86059
+ "deviceManager.loadMeta": [{
86060
+ name: "deviceId",
86061
+ form: "single",
86062
+ optional: false
86063
+ }],
86064
+ "deviceManager.loadRuntimeState": [{
86065
+ name: "deviceId",
86066
+ form: "single",
86067
+ optional: false
86068
+ }],
86069
+ "deviceManager.persistConfig": [{
86070
+ name: "deviceId",
86071
+ form: "single",
86072
+ optional: false
86073
+ }],
86074
+ "deviceManager.probeStreams": [{
86075
+ name: "deviceId",
86076
+ form: "single",
86077
+ optional: false
86078
+ }],
86079
+ "deviceManager.registerDevice": [{
86080
+ name: "parentDeviceId",
86081
+ form: "single",
86082
+ optional: true
86083
+ }],
86084
+ "deviceManager.remove": [{
86085
+ name: "deviceId",
86086
+ form: "single",
86087
+ optional: false
86088
+ }],
86089
+ "deviceManager.removeDevice": [{
86090
+ name: "deviceId",
86091
+ form: "single",
86092
+ optional: false
86093
+ }],
86094
+ "deviceManager.runDeviceAction": [{
86095
+ name: "deviceId",
86096
+ form: "single",
86097
+ optional: false
86098
+ }],
86099
+ "deviceManager.setChildLayout": [{
86100
+ name: "deviceId",
86101
+ form: "single",
86102
+ optional: false
86103
+ }],
86104
+ "deviceManager.setDisabled": [{
86105
+ name: "deviceId",
86106
+ form: "single",
86107
+ optional: false
86108
+ }],
86109
+ "deviceManager.setDisplay": [{
86110
+ name: "deviceId",
86111
+ form: "single",
86112
+ optional: false
86113
+ }],
86114
+ "deviceManager.setIntegrationId": [{
86115
+ name: "deviceId",
86116
+ form: "single",
86117
+ optional: false
86118
+ }],
86119
+ "deviceManager.setLinkDeviceId": [{
86120
+ name: "deviceId",
86121
+ form: "single",
86122
+ optional: false
86123
+ }, {
86124
+ name: "linkDeviceId",
86125
+ form: "single",
86126
+ optional: true
86127
+ }],
86128
+ "deviceManager.setLocation": [{
86129
+ name: "deviceId",
86130
+ form: "single",
86131
+ optional: false
86132
+ }],
86133
+ "deviceManager.setMetadata": [{
86134
+ name: "deviceId",
86135
+ form: "single",
86136
+ optional: false
86137
+ }],
86138
+ "deviceManager.setName": [{
86139
+ name: "deviceId",
86140
+ form: "single",
86141
+ optional: false
86142
+ }],
86143
+ "deviceManager.setPrimaryChildEntityId": [{
86144
+ name: "deviceId",
86145
+ form: "single",
86146
+ optional: false
86147
+ }],
86148
+ "deviceManager.setRole": [{
86149
+ name: "deviceId",
86150
+ form: "single",
86151
+ optional: false
86152
+ }],
86153
+ "deviceManager.setStreamProfileMap": [{
86154
+ name: "deviceId",
86155
+ form: "single",
86156
+ optional: false
86157
+ }],
86158
+ "deviceManager.setType": [{
86159
+ name: "deviceId",
86160
+ form: "single",
86161
+ optional: false
86162
+ }],
86163
+ "deviceManager.setWrapperActive": [{
86164
+ name: "deviceId",
86165
+ form: "single",
86166
+ optional: false
86167
+ }],
86168
+ "deviceManager.testField": [{
86169
+ name: "deviceId",
86170
+ form: "single",
86171
+ optional: false
86172
+ }],
86173
+ "deviceManager.updateConfig": [{
86174
+ name: "deviceId",
86175
+ form: "single",
86176
+ optional: false
86177
+ }],
86178
+ "deviceManager.updateDeviceField": [{
86179
+ name: "deviceId",
86180
+ form: "single",
86181
+ optional: false
86182
+ }],
86183
+ "deviceManager.updateDeviceFieldsBatch": [{
86184
+ name: "deviceId",
86185
+ form: "single",
86186
+ optional: false
86187
+ }],
86188
+ "deviceOps.getConfigEntries": [{
86189
+ name: "deviceId",
86190
+ form: "single",
86191
+ optional: false
86192
+ }],
86193
+ "deviceOps.getRawState": [{
86194
+ name: "deviceId",
86195
+ form: "single",
86196
+ optional: false
86197
+ }],
86198
+ "deviceOps.getSettingsSchema": [{
86199
+ name: "deviceId",
86200
+ form: "single",
86201
+ optional: false
86202
+ }],
86203
+ "deviceOps.getStreamSources": [{
86204
+ name: "deviceId",
86205
+ form: "single",
86206
+ optional: false
86207
+ }],
86208
+ "deviceOps.removeDevice": [{
86209
+ name: "deviceId",
86210
+ form: "single",
86211
+ optional: false
86212
+ }],
86213
+ "deviceOps.runAction": [{
86214
+ name: "deviceId",
86215
+ form: "single",
86216
+ optional: false
86217
+ }],
86218
+ "deviceOps.setConfig": [{
86219
+ name: "deviceId",
86220
+ form: "single",
86221
+ optional: false
86222
+ }],
86223
+ "deviceState.getCapSlice": [{
86224
+ name: "deviceId",
86225
+ form: "single",
86226
+ optional: false
86227
+ }],
86228
+ "deviceState.getSnapshot": [{
86229
+ name: "deviceId",
86230
+ form: "single",
86231
+ optional: false
86232
+ }],
86233
+ "deviceState.setCapSlice": [{
86234
+ name: "deviceId",
86235
+ form: "single",
86236
+ optional: false
86237
+ }],
86238
+ "events.getEventClipUrl": [{
86239
+ name: "deviceId",
86240
+ form: "single",
86241
+ optional: false
86242
+ }],
86243
+ "events.getEvents": [{
86244
+ name: "deviceId",
86245
+ form: "single",
86246
+ optional: false
86247
+ }],
86248
+ "events.getEventThumbnail": [{
86249
+ name: "deviceId",
86250
+ form: "single",
86251
+ optional: false
86252
+ }],
86253
+ "faceGallery.getFaceByTrack": [{
86254
+ name: "deviceId",
86255
+ form: "single",
86256
+ optional: false
86257
+ }],
86258
+ "faceGallery.listRecentFaces": [{
86259
+ name: "deviceId",
86260
+ form: "single",
86261
+ optional: true
86262
+ }],
86263
+ "fanControl.setDirection": [{
86264
+ name: "deviceId",
86265
+ form: "single",
86266
+ optional: false
86267
+ }],
86268
+ "fanControl.setOscillating": [{
86269
+ name: "deviceId",
86270
+ form: "single",
86271
+ optional: false
86272
+ }],
86273
+ "fanControl.setPercentage": [{
86274
+ name: "deviceId",
86275
+ form: "single",
86276
+ optional: false
86277
+ }],
86278
+ "fanControl.setPreset": [{
86279
+ name: "deviceId",
86280
+ form: "single",
86281
+ optional: false
86282
+ }],
86283
+ "humidifier.setMode": [{
86284
+ name: "deviceId",
86285
+ form: "single",
86286
+ optional: false
86287
+ }],
86288
+ "humidifier.setOn": [{
86289
+ name: "deviceId",
86290
+ form: "single",
86291
+ optional: false
86292
+ }],
86293
+ "humidifier.setTargetHumidity": [{
86294
+ name: "deviceId",
86295
+ form: "single",
86296
+ optional: false
86297
+ }],
86298
+ "imageSettings.getOptions": [{
86299
+ name: "deviceId",
86300
+ form: "single",
86301
+ optional: false
86302
+ }],
86303
+ "imageSettings.setSettings": [{
86304
+ name: "deviceId",
86305
+ form: "single",
86306
+ optional: false
86307
+ }],
86308
+ "intercom.endTalkSession": [{
86309
+ name: "deviceId",
86310
+ form: "single",
86311
+ optional: false
86312
+ }],
86313
+ "intercom.handleAnswer": [{
86314
+ name: "deviceId",
86315
+ form: "single",
86316
+ optional: false
86317
+ }],
86318
+ "intercom.pushTalkAudio": [{
86319
+ name: "deviceId",
86320
+ form: "single",
86321
+ optional: false
86322
+ }],
86323
+ "intercom.startSession": [{
86324
+ name: "deviceId",
86325
+ form: "single",
86326
+ optional: false
86327
+ }],
86328
+ "intercom.startTalkSession": [{
86329
+ name: "deviceId",
86330
+ form: "single",
86331
+ optional: false
86332
+ }],
86333
+ "intercom.stopSession": [{
86334
+ name: "deviceId",
86335
+ form: "single",
86336
+ optional: false
86337
+ }],
86338
+ "lawnMowerControl.dock": [{
86339
+ name: "deviceId",
86340
+ form: "single",
86341
+ optional: false
86342
+ }],
86343
+ "lawnMowerControl.pause": [{
86344
+ name: "deviceId",
86345
+ form: "single",
86346
+ optional: false
86347
+ }],
86348
+ "lawnMowerControl.startMowing": [{
86349
+ name: "deviceId",
86350
+ form: "single",
86351
+ optional: false
86352
+ }],
86353
+ "lockControl.lock": [{
86354
+ name: "deviceId",
86355
+ form: "single",
86356
+ optional: false
86357
+ }],
86358
+ "lockControl.open": [{
86359
+ name: "deviceId",
86360
+ form: "single",
86361
+ optional: false
86362
+ }],
86363
+ "lockControl.unlock": [{
86364
+ name: "deviceId",
86365
+ form: "single",
86366
+ optional: false
86367
+ }],
86368
+ "mediaPlayer.next": [{
86369
+ name: "deviceId",
86370
+ form: "single",
86371
+ optional: false
86372
+ }],
86373
+ "mediaPlayer.pause": [{
86374
+ name: "deviceId",
86375
+ form: "single",
86376
+ optional: false
86377
+ }],
86378
+ "mediaPlayer.play": [{
86379
+ name: "deviceId",
86380
+ form: "single",
86381
+ optional: false
86382
+ }],
86383
+ "mediaPlayer.playMedia": [{
86384
+ name: "deviceId",
86385
+ form: "single",
86386
+ optional: false
86387
+ }],
86388
+ "mediaPlayer.previous": [{
86389
+ name: "deviceId",
86390
+ form: "single",
86391
+ optional: false
86392
+ }],
86393
+ "mediaPlayer.seek": [{
86394
+ name: "deviceId",
86395
+ form: "single",
86396
+ optional: false
86397
+ }],
86398
+ "mediaPlayer.selectSource": [{
86399
+ name: "deviceId",
86400
+ form: "single",
86401
+ optional: false
86402
+ }],
86403
+ "mediaPlayer.setMute": [{
86404
+ name: "deviceId",
86405
+ form: "single",
86406
+ optional: false
86407
+ }],
86408
+ "mediaPlayer.setRepeat": [{
86409
+ name: "deviceId",
86410
+ form: "single",
86411
+ optional: false
86412
+ }],
86413
+ "mediaPlayer.setShuffle": [{
86414
+ name: "deviceId",
86415
+ form: "single",
86416
+ optional: false
86417
+ }],
86418
+ "mediaPlayer.setVolume": [{
86419
+ name: "deviceId",
86420
+ form: "single",
86421
+ optional: false
86422
+ }],
86423
+ "mediaPlayer.stop": [{
86424
+ name: "deviceId",
86425
+ form: "single",
86426
+ optional: false
86427
+ }],
86428
+ "motion.isDetected": [{
86429
+ name: "deviceId",
86430
+ form: "single",
86431
+ optional: false
86432
+ }],
86433
+ "motionDetection.analyze": [{
86434
+ name: "deviceId",
86435
+ form: "single",
86436
+ optional: false
86437
+ }],
86438
+ "motionDetection.removeCamera": [{
86439
+ name: "deviceId",
86440
+ form: "single",
86441
+ optional: false
86442
+ }],
86443
+ "motionTrigger.setMotionTrigger": [{
86444
+ name: "deviceId",
86445
+ form: "single",
86446
+ optional: false
86447
+ }],
86448
+ "motionZones.getOptions": [{
86449
+ name: "deviceId",
86450
+ form: "single",
86451
+ optional: false
86452
+ }],
86453
+ "motionZones.setZone": [{
86454
+ name: "deviceId",
86455
+ form: "single",
86456
+ optional: false
86457
+ }],
86458
+ "nativeObjectDetection.setEnabled": [{
86459
+ name: "deviceId",
86460
+ form: "single",
86461
+ optional: false
86462
+ }],
86463
+ "networkQuality.getDeviceStats": [{
86464
+ name: "deviceId",
86465
+ form: "single",
86466
+ optional: false
86467
+ }],
86468
+ "networkQuality.reportClientStats": [{
86469
+ name: "deviceId",
86470
+ form: "single",
86471
+ optional: false
86472
+ }],
86473
+ "notificationRules.setDeviceMuted": [{
86474
+ name: "deviceId",
86475
+ form: "single",
86476
+ optional: false
86477
+ }],
86478
+ "notifier.cancel": [{
86479
+ name: "deviceId",
86480
+ form: "single",
86481
+ optional: false
86482
+ }],
86483
+ "notifier.send": [{
86484
+ name: "deviceId",
86485
+ form: "single",
86486
+ optional: false
86487
+ }],
86488
+ "osd.setOverlay": [{
86489
+ name: "deviceId",
86490
+ form: "single",
86491
+ optional: false
86492
+ }],
86493
+ "osdManager.clearSlotBinding": [{
86494
+ name: "deviceId",
86495
+ form: "single",
86496
+ optional: false
86497
+ }],
86498
+ "osdManager.copyDeviceConfiguration": [{
86499
+ name: "sourceDeviceId",
86500
+ form: "single",
86501
+ optional: false
86502
+ }, {
86503
+ name: "targetDeviceId",
86504
+ form: "single",
86505
+ optional: false
86506
+ }],
86507
+ "osdManager.getDeviceOsd": [{
86508
+ name: "deviceId",
86509
+ form: "single",
86510
+ optional: false
86511
+ }],
86512
+ "osdManager.getSourceCatalog": [{
86513
+ name: "deviceId",
86514
+ form: "single",
86515
+ optional: false
86516
+ }],
86517
+ "osdManager.previewSlot": [{
86518
+ name: "deviceId",
86519
+ form: "single",
86520
+ optional: false
86521
+ }],
86522
+ "osdManager.renderDevice": [{
86523
+ name: "deviceId",
86524
+ form: "single",
86525
+ optional: false
86526
+ }],
86527
+ "osdManager.setSlotBinding": [{
86528
+ name: "deviceId",
86529
+ form: "single",
86530
+ optional: false
86531
+ }],
86532
+ "petFeeder.callPet": [{
86533
+ name: "deviceId",
86534
+ form: "single",
86535
+ optional: false
86536
+ }],
86537
+ "petFeeder.cancelFeed": [{
86538
+ name: "deviceId",
86539
+ form: "single",
86540
+ optional: false
86541
+ }],
86542
+ "petFeeder.feed": [{
86543
+ name: "deviceId",
86544
+ form: "single",
86545
+ optional: false
86546
+ }],
86547
+ "petFeeder.markFoodReplenished": [{
86548
+ name: "deviceId",
86549
+ form: "single",
86550
+ optional: false
86551
+ }],
86552
+ "petFeeder.playSound": [{
86553
+ name: "deviceId",
86554
+ form: "single",
86555
+ optional: false
86556
+ }],
86557
+ "petFeeder.resetDesiccant": [{
86558
+ name: "deviceId",
86559
+ form: "single",
86560
+ optional: false
86561
+ }],
86562
+ "petFeeder.setChildLock": [{
86563
+ name: "deviceId",
86564
+ form: "single",
86565
+ optional: false
86566
+ }],
86567
+ "petFeeder.setFeedSound": [{
86568
+ name: "deviceId",
86569
+ form: "single",
86570
+ optional: false
86571
+ }],
86572
+ "petFeeder.setIndicatorLight": [{
86573
+ name: "deviceId",
86574
+ form: "single",
86575
+ optional: false
86576
+ }],
86577
+ "petFeeder.setVolume": [{
86578
+ name: "deviceId",
86579
+ form: "single",
86580
+ optional: false
86581
+ }],
86582
+ "pipelineAnalytics.clearTracks": [{
86583
+ name: "deviceId",
86584
+ form: "single",
86585
+ optional: false
86586
+ }],
86587
+ "pipelineAnalytics.completeRetrainTrack": [{
86588
+ name: "deviceId",
86589
+ form: "single",
86590
+ optional: false
86591
+ }],
86592
+ "pipelineAnalytics.deleteDeviceEvents": [{
86593
+ name: "deviceId",
86594
+ form: "single",
86595
+ optional: false
86596
+ }],
86597
+ "pipelineAnalytics.deleteTracks": [{
86598
+ name: "deviceId",
86599
+ form: "single",
86600
+ optional: false
86601
+ }],
86602
+ "pipelineAnalytics.deselectRetrainFrame": [{
86603
+ name: "deviceId",
86604
+ form: "single",
86605
+ optional: false
86606
+ }],
86607
+ "pipelineAnalytics.getActiveTracks": [{
86608
+ name: "deviceId",
86609
+ form: "single",
86610
+ optional: false
86611
+ }],
86612
+ "pipelineAnalytics.getAudioEvents": [{
86613
+ name: "deviceId",
86614
+ form: "single",
86615
+ optional: false
86616
+ }],
86617
+ "pipelineAnalytics.getEventDensity": [{
86618
+ name: "deviceId",
86619
+ form: "single",
86620
+ optional: false
86621
+ }],
86622
+ "pipelineAnalytics.getEventMedia": [{
86623
+ name: "deviceId",
86624
+ form: "single",
86625
+ optional: false
86626
+ }],
86627
+ "pipelineAnalytics.getKeyEvents": [{
86628
+ name: "deviceId",
86629
+ form: "single",
86630
+ optional: false
86631
+ }],
86632
+ "pipelineAnalytics.getMotionEvents": [{
86633
+ name: "deviceId",
86634
+ form: "single",
86635
+ optional: false
86636
+ }],
86637
+ "pipelineAnalytics.getObjectEvents": [{
86638
+ name: "deviceId",
86639
+ form: "single",
86640
+ optional: false
86641
+ }],
86642
+ "pipelineAnalytics.getRetrainExportUrl": [{
86643
+ name: "deviceIds",
86644
+ form: "array",
86645
+ optional: true
86646
+ }],
86647
+ "pipelineAnalytics.getSensorEvents": [{
86648
+ name: "deviceId",
86649
+ form: "single",
86650
+ optional: false
86651
+ }],
86652
+ "pipelineAnalytics.getTrack": [{
86653
+ name: "deviceId",
86654
+ form: "single",
86655
+ optional: false
86656
+ }],
86657
+ "pipelineAnalytics.getTrackMedia": [{
86658
+ name: "deviceId",
86659
+ form: "single",
86660
+ optional: false
86661
+ }],
86662
+ "pipelineAnalytics.getTrainingExportSummary": [{
86663
+ name: "deviceIds",
86664
+ form: "array",
86665
+ optional: true
86666
+ }],
86667
+ "pipelineAnalytics.getTrainingExportUrl": [{
86668
+ name: "deviceIds",
86669
+ form: "array",
86670
+ optional: true
86671
+ }],
86672
+ "pipelineAnalytics.listEventKinds": [{
86673
+ name: "deviceId",
86674
+ form: "single",
86675
+ optional: false
86676
+ }],
86677
+ "pipelineAnalytics.listEventKindsBatch": [{
86678
+ name: "deviceIds",
86679
+ form: "array",
86680
+ optional: false
86681
+ }],
86682
+ "pipelineAnalytics.listOpsLog": [{
86683
+ name: "deviceId",
86684
+ form: "single",
86685
+ optional: true
86686
+ }],
86687
+ "pipelineAnalytics.listRecentTracks": [{
86688
+ name: "deviceIds",
86689
+ form: "array",
86690
+ optional: false
86691
+ }],
86692
+ "pipelineAnalytics.listRetrainStaging": [{
86693
+ name: "deviceIds",
86694
+ form: "array",
86695
+ optional: true
86696
+ }],
86697
+ "pipelineAnalytics.listTrackMedia": [{
86698
+ name: "deviceId",
86699
+ form: "single",
86700
+ optional: false
86701
+ }],
86702
+ "pipelineAnalytics.listTracks": [{
86703
+ name: "deviceId",
86704
+ form: "single",
86705
+ optional: false
86706
+ }],
86707
+ "pipelineAnalytics.proposeRetrainAnnotations": [{
86708
+ name: "deviceId",
86709
+ form: "single",
86710
+ optional: false
86711
+ }],
86712
+ "pipelineAnalytics.pruneEventsBefore": [{
86713
+ name: "deviceId",
86714
+ form: "single",
86715
+ optional: false
86716
+ }],
86717
+ "pipelineAnalytics.pruneTracksBefore": [{
86718
+ name: "deviceId",
86719
+ form: "single",
86720
+ optional: false
86721
+ }],
86722
+ "pipelineAnalytics.rebuildObjectEmbeddings": [{
86723
+ name: "deviceId",
86724
+ form: "single",
86725
+ optional: true
86726
+ }],
86727
+ "pipelineAnalytics.restageRetrainTrack": [{
86728
+ name: "deviceId",
86729
+ form: "single",
86730
+ optional: false
86731
+ }],
86732
+ "pipelineAnalytics.saveRetrainAnnotations": [{
86733
+ name: "deviceId",
86734
+ form: "single",
86735
+ optional: false
86736
+ }],
86737
+ "pipelineAnalytics.searchObjectEvents": [{
86738
+ name: "deviceId",
86739
+ form: "single",
86740
+ optional: true
86741
+ }],
86742
+ "pipelineAnalytics.selectRetrainFrames": [{
86743
+ name: "deviceId",
86744
+ form: "single",
86745
+ optional: false
86746
+ }],
86747
+ "pipelineAnalytics.setTrackFlags": [{
86748
+ name: "deviceId",
86749
+ form: "single",
86750
+ optional: false
86751
+ }],
86752
+ "pipelineAnalytics.wipeAllAnalytics": [{
86753
+ name: "deviceId",
86754
+ form: "single",
86755
+ optional: false
86756
+ }],
86757
+ "pipelineExecutor.runPipeline": [{
86758
+ name: "deviceId",
86759
+ form: "single",
86760
+ optional: true
86761
+ }],
86762
+ "pipelineExecutor.runPipelineBatch": [{
86763
+ name: "deviceId",
86764
+ form: "single",
86765
+ optional: true
86766
+ }],
86767
+ "pipelineOrchestrator.assignAudio": [{
86768
+ name: "deviceId",
86769
+ form: "single",
86770
+ optional: false
86771
+ }],
86772
+ "pipelineOrchestrator.assignPipeline": [{
86773
+ name: "deviceId",
86774
+ form: "single",
86775
+ optional: false
86776
+ }],
86777
+ "pipelineOrchestrator.getAudioAssignment": [{
86778
+ name: "deviceId",
86779
+ form: "single",
86780
+ optional: false
86781
+ }],
86782
+ "pipelineOrchestrator.getCameraMetrics": [{
86783
+ name: "deviceId",
86784
+ form: "single",
86785
+ optional: false
86786
+ }],
86787
+ "pipelineOrchestrator.getCameraSettings": [{
86788
+ name: "deviceId",
86789
+ form: "single",
86790
+ optional: false
86791
+ }],
86792
+ "pipelineOrchestrator.getCameraStatus": [{
86793
+ name: "deviceId",
86794
+ form: "single",
86795
+ optional: false
86796
+ }],
86797
+ "pipelineOrchestrator.getCameraStatuses": [{
86798
+ name: "deviceIds",
86799
+ form: "array",
86800
+ optional: true
86801
+ }],
86802
+ "pipelineOrchestrator.getCameraStepOverrides": [{
86803
+ name: "deviceId",
86804
+ form: "single",
86805
+ optional: false
86806
+ }],
86807
+ "pipelineOrchestrator.getCameraSwitches": [{
86808
+ name: "deviceId",
86809
+ form: "single",
86810
+ optional: false
86811
+ }],
86812
+ "pipelineOrchestrator.getPipelineAssignment": [{
86813
+ name: "deviceId",
86814
+ form: "single",
86815
+ optional: false
86816
+ }],
86817
+ "pipelineOrchestrator.getPipelineDevicePin": [{
86818
+ name: "deviceId",
86819
+ form: "single",
86820
+ optional: false
86821
+ }],
86822
+ "pipelineOrchestrator.resolvePipeline": [{
86823
+ name: "deviceId",
86824
+ form: "single",
86825
+ optional: false
86826
+ }],
86827
+ "pipelineOrchestrator.setCameraPipelineForAgent": [{
86828
+ name: "deviceId",
86829
+ form: "single",
86830
+ optional: false
86831
+ }],
86832
+ "pipelineOrchestrator.setCameraStepOverride": [{
86833
+ name: "deviceId",
86834
+ form: "single",
86835
+ optional: false
86836
+ }],
86837
+ "pipelineOrchestrator.setCameraStepToggle": [{
86838
+ name: "deviceId",
86839
+ form: "single",
86840
+ optional: false
86841
+ }],
86842
+ "pipelineOrchestrator.setCameraSwitch": [{
86843
+ name: "deviceId",
86844
+ form: "single",
86845
+ optional: false
86846
+ }],
86847
+ "pipelineOrchestrator.setPipelineDevicePin": [{
86848
+ name: "deviceId",
86849
+ form: "single",
86850
+ optional: false
86851
+ }],
86852
+ "pipelineOrchestrator.unassignAudio": [{
86853
+ name: "deviceId",
86854
+ form: "single",
86855
+ optional: false
86856
+ }],
86857
+ "pipelineOrchestrator.unassignPipeline": [{
86858
+ name: "deviceId",
86859
+ form: "single",
86860
+ optional: false
86861
+ }],
86862
+ "pipelineRunner.attachCamera": [{
86863
+ name: "deviceId",
86864
+ form: "single",
86865
+ optional: false
86866
+ }],
86867
+ "pipelineRunner.detachCamera": [{
86868
+ name: "deviceId",
86869
+ form: "single",
86870
+ optional: false
86871
+ }],
86872
+ "pipelineRunner.getCameraMetrics": [{
86873
+ name: "deviceId",
86874
+ form: "single",
86875
+ optional: false
86876
+ }],
86877
+ "pipelineRunner.reportMotion": [{
86878
+ name: "deviceId",
86879
+ form: "single",
86880
+ optional: false
86881
+ }],
86882
+ "pipelineRunner.runDetailSubtree": [{
86883
+ name: "deviceId",
86884
+ form: "single",
86885
+ optional: false
86886
+ }],
86887
+ "pipelineRunner.runStatelessStep": [{
86888
+ name: "sourceDeviceId",
86889
+ form: "single",
86890
+ optional: false
86891
+ }],
86892
+ "plateGallery.getPlateByTrack": [{
86893
+ name: "deviceId",
86894
+ form: "single",
86895
+ optional: false
86896
+ }],
86897
+ "plateGallery.listPlates": [{
86898
+ name: "deviceId",
86899
+ form: "single",
86900
+ optional: true
86901
+ }],
86902
+ "privacyMask.getOptions": [{
86903
+ name: "deviceId",
86904
+ form: "single",
86905
+ optional: false
86906
+ }],
86907
+ "privacyMask.setAudioEnabled": [{
86908
+ name: "deviceId",
86909
+ form: "single",
86910
+ optional: false
86911
+ }],
86912
+ "privacyMask.setMask": [{
86913
+ name: "deviceId",
86914
+ form: "single",
86915
+ optional: false
86916
+ }],
86917
+ "ptz.continuousMove": [{
86918
+ name: "deviceId",
86919
+ form: "single",
86920
+ optional: false
86921
+ }],
86922
+ "ptz.deletePreset": [{
86923
+ name: "deviceId",
86924
+ form: "single",
86925
+ optional: false
86926
+ }],
86927
+ "ptz.getOptions": [{
86928
+ name: "deviceId",
86929
+ form: "single",
86930
+ optional: false
86931
+ }],
86932
+ "ptz.getPosition": [{
86933
+ name: "deviceId",
86934
+ form: "single",
86935
+ optional: false
86936
+ }],
86937
+ "ptz.getPresets": [{
86938
+ name: "deviceId",
86939
+ form: "single",
86940
+ optional: false
86941
+ }],
86942
+ "ptz.goHome": [{
86943
+ name: "deviceId",
86944
+ form: "single",
86945
+ optional: false
86946
+ }],
86947
+ "ptz.goToPreset": [{
86948
+ name: "deviceId",
86949
+ form: "single",
86950
+ optional: false
86951
+ }],
86952
+ "ptz.move": [{
86953
+ name: "deviceId",
86954
+ form: "single",
86955
+ optional: false
86956
+ }],
86957
+ "ptz.savePreset": [{
86958
+ name: "deviceId",
86959
+ form: "single",
86960
+ optional: false
86961
+ }],
86962
+ "ptz.setAutofocus": [{
86963
+ name: "deviceId",
86964
+ form: "single",
86965
+ optional: false
86966
+ }],
86967
+ "ptz.stop": [{
86968
+ name: "deviceId",
86969
+ form: "single",
86970
+ optional: false
86971
+ }],
86972
+ "ptzAutotrack.getSettings": [{
86973
+ name: "deviceId",
86974
+ form: "single",
86975
+ optional: false
86976
+ }],
86977
+ "ptzAutotrack.getStatus": [{
86978
+ name: "deviceId",
86979
+ form: "single",
86980
+ optional: false
86981
+ }],
86982
+ "ptzAutotrack.setEnabled": [{
86983
+ name: "deviceId",
86984
+ form: "single",
86985
+ optional: false
86986
+ }],
86987
+ "ptzAutotrack.setSettings": [{
86988
+ name: "deviceId",
86989
+ form: "single",
86990
+ optional: false
86991
+ }],
86992
+ "reboot.reboot": [{
86993
+ name: "deviceId",
86994
+ form: "single",
86995
+ optional: false
86996
+ }],
86997
+ "recording.deleteFootprint": [{
86998
+ name: "deviceId",
86999
+ form: "single",
87000
+ optional: false
87001
+ }],
87002
+ "recording.getAvailability": [{
87003
+ name: "deviceId",
87004
+ form: "single",
87005
+ optional: false
87006
+ }],
87007
+ "recording.getDaysWithRecordings": [{
87008
+ name: "deviceId",
87009
+ form: "single",
87010
+ optional: false
87011
+ }],
87012
+ "recording.getDeviceConfig": [{
87013
+ name: "deviceId",
87014
+ form: "single",
87015
+ optional: false
87016
+ }],
87017
+ "recording.getPlaybackManifest": [{
87018
+ name: "deviceId",
87019
+ form: "single",
87020
+ optional: false
87021
+ }],
87022
+ "recording.listOpsLog": [{
87023
+ name: "deviceId",
87024
+ form: "single",
87025
+ optional: true
87026
+ }],
87027
+ "recording.locateSegment": [{
87028
+ name: "deviceId",
87029
+ form: "single",
87030
+ optional: false
87031
+ }],
87032
+ "recording.pruneFootage": [{
87033
+ name: "deviceId",
87034
+ form: "single",
87035
+ optional: false
87036
+ }],
87037
+ "recording.readGopBytes": [{
87038
+ name: "deviceId",
87039
+ form: "single",
87040
+ optional: false
87041
+ }],
87042
+ "recording.readSegmentBytes": [{
87043
+ name: "deviceId",
87044
+ form: "single",
87045
+ optional: false
87046
+ }],
87047
+ "recording.relocateFootage": [{
87048
+ name: "deviceId",
87049
+ form: "single",
87050
+ optional: true
87051
+ }],
87052
+ "recording.renderClip": [{
87053
+ name: "deviceId",
87054
+ form: "single",
87055
+ optional: false
87056
+ }],
87057
+ "recording.renderGif": [{
87058
+ name: "deviceId",
87059
+ form: "single",
87060
+ optional: false
87061
+ }],
87062
+ "recording.rescanStorage": [{
87063
+ name: "deviceId",
87064
+ form: "single",
87065
+ optional: false
87066
+ }],
87067
+ "recording.setDeviceConfig": [{
87068
+ name: "deviceId",
87069
+ form: "single",
87070
+ optional: false
87071
+ }],
87072
+ "recording.startStorageMigrationMove": [{
87073
+ name: "deviceId",
87074
+ form: "single",
87075
+ optional: true
87076
+ }],
87077
+ "recordingExport.createExport": [{
87078
+ name: "deviceId",
87079
+ form: "single",
87080
+ optional: false
87081
+ }],
87082
+ "recordingExport.listExports": [{
87083
+ name: "deviceId",
87084
+ form: "single",
87085
+ optional: true
87086
+ }],
87087
+ "sceneMonitor.captureReference": [{
87088
+ name: "deviceId",
87089
+ form: "single",
87090
+ optional: false
87091
+ }],
87092
+ "sceneMonitor.createScene": [{
87093
+ name: "deviceId",
87094
+ form: "single",
87095
+ optional: false
87096
+ }],
87097
+ "sceneMonitor.deleteReference": [{
87098
+ name: "deviceId",
87099
+ form: "single",
87100
+ optional: false
87101
+ }],
87102
+ "sceneMonitor.deleteScene": [{
87103
+ name: "deviceId",
87104
+ form: "single",
87105
+ optional: false
87106
+ }],
87107
+ "sceneMonitor.listScenes": [{
87108
+ name: "deviceId",
87109
+ form: "single",
87110
+ optional: false
87111
+ }],
87112
+ "sceneMonitor.recheckNow": [{
87113
+ name: "deviceId",
87114
+ form: "single",
87115
+ optional: false
87116
+ }],
87117
+ "sceneMonitor.resetScene": [{
87118
+ name: "deviceId",
87119
+ form: "single",
87120
+ optional: false
87121
+ }],
87122
+ "sceneMonitor.updateScene": [{
87123
+ name: "deviceId",
87124
+ form: "single",
87125
+ optional: false
87126
+ }],
87127
+ "scriptRunner.run": [{
87128
+ name: "deviceId",
87129
+ form: "single",
87130
+ optional: false
87131
+ }],
87132
+ "scriptRunner.stop": [{
87133
+ name: "deviceId",
87134
+ form: "single",
87135
+ optional: false
87136
+ }],
87137
+ "snapshot.getSnapshot": [{
87138
+ name: "deviceId",
87139
+ form: "single",
87140
+ optional: false
87141
+ }],
87142
+ "snapshot.getSnapshotLinks": [{
87143
+ name: "targets",
87144
+ form: "object-array",
87145
+ optional: false,
87146
+ itemField: "deviceId"
87147
+ }],
87148
+ "snapshot.getSnapshotOverview": [{
87149
+ name: "deviceIds",
87150
+ form: "array",
87151
+ optional: false
87152
+ }],
87153
+ "snapshot.invalidateCache": [{
87154
+ name: "deviceId",
87155
+ form: "single",
87156
+ optional: false
87157
+ }],
87158
+ "streamBroker.acquireEgressTranscode": [{
87159
+ name: "deviceId",
87160
+ form: "single",
87161
+ optional: false
87162
+ }],
87163
+ "streamBroker.assignProfile": [{
87164
+ name: "deviceId",
87165
+ form: "single",
87166
+ optional: false
87167
+ }],
87168
+ "streamBroker.getDeviceAudioMute": [{
87169
+ name: "deviceId",
87170
+ form: "single",
87171
+ optional: false
87172
+ }],
87173
+ "streamBroker.getStreamWithCodec": [{
87174
+ name: "deviceId",
87175
+ form: "single",
87176
+ optional: false
87177
+ }],
87178
+ "streamBroker.produceEventMedia": [{
87179
+ name: "deviceId",
87180
+ form: "single",
87181
+ optional: false
87182
+ }],
87183
+ "streamBroker.publishCameraStream": [{
87184
+ name: "deviceId",
87185
+ form: "single",
87186
+ optional: false
87187
+ }],
87188
+ "streamBroker.renderPreBufferClip": [{
87189
+ name: "deviceId",
87190
+ form: "single",
87191
+ optional: false
87192
+ }],
87193
+ "streamBroker.restartProfile": [{
87194
+ name: "deviceId",
87195
+ form: "single",
87196
+ optional: false
87197
+ }],
87198
+ "streamBroker.retractCameraStream": [{
87199
+ name: "deviceId",
87200
+ form: "single",
87201
+ optional: false
87202
+ }],
87203
+ "streamBroker.setDeviceAudioMute": [{
87204
+ name: "deviceId",
87205
+ form: "single",
87206
+ optional: false
87207
+ }],
87208
+ "streamBroker.unassignProfile": [{
87209
+ name: "deviceId",
87210
+ form: "single",
87211
+ optional: false
87212
+ }],
87213
+ "streamCatalog.getCatalog": [{
87214
+ name: "deviceId",
87215
+ form: "single",
87216
+ optional: false
87217
+ }],
87218
+ "streamParams.getConfigSchema": [{
87219
+ name: "deviceId",
87220
+ form: "single",
87221
+ optional: false
87222
+ }],
87223
+ "streamParams.getOptions": [{
87224
+ name: "deviceId",
87225
+ form: "single",
87226
+ optional: false
87227
+ }],
87228
+ "streamParams.setProfile": [{
87229
+ name: "deviceId",
87230
+ form: "single",
87231
+ optional: false
87232
+ }],
87233
+ "switch.setState": [{
87234
+ name: "deviceId",
87235
+ form: "single",
87236
+ optional: false
87237
+ }],
87238
+ "vacuumControl.locate": [{
87239
+ name: "deviceId",
87240
+ form: "single",
87241
+ optional: false
87242
+ }],
87243
+ "vacuumControl.pause": [{
87244
+ name: "deviceId",
87245
+ form: "single",
87246
+ optional: false
87247
+ }],
87248
+ "vacuumControl.returnToBase": [{
87249
+ name: "deviceId",
87250
+ form: "single",
87251
+ optional: false
87252
+ }],
87253
+ "vacuumControl.setFanSpeed": [{
87254
+ name: "deviceId",
87255
+ form: "single",
87256
+ optional: false
87257
+ }],
87258
+ "vacuumControl.start": [{
87259
+ name: "deviceId",
87260
+ form: "single",
87261
+ optional: false
87262
+ }],
87263
+ "vacuumControl.stop": [{
87264
+ name: "deviceId",
87265
+ form: "single",
87266
+ optional: false
87267
+ }],
87268
+ "valve.close": [{
87269
+ name: "deviceId",
87270
+ form: "single",
87271
+ optional: false
87272
+ }],
87273
+ "valve.open": [{
87274
+ name: "deviceId",
87275
+ form: "single",
87276
+ optional: false
87277
+ }],
87278
+ "valve.setPosition": [{
87279
+ name: "deviceId",
87280
+ form: "single",
87281
+ optional: false
87282
+ }],
87283
+ "valve.stop": [{
87284
+ name: "deviceId",
87285
+ form: "single",
87286
+ optional: false
87287
+ }],
87288
+ "videoclips.getClipPlayback": [{
87289
+ name: "deviceId",
87290
+ form: "single",
87291
+ optional: false
87292
+ }],
87293
+ "videoclips.listClips": [{
87294
+ name: "deviceId",
87295
+ form: "single",
87296
+ optional: false
87297
+ }],
87298
+ "waterHeater.setAway": [{
87299
+ name: "deviceId",
87300
+ form: "single",
87301
+ optional: false
87302
+ }],
87303
+ "waterHeater.setOperationMode": [{
87304
+ name: "deviceId",
87305
+ form: "single",
87306
+ optional: false
87307
+ }],
87308
+ "waterHeater.setTargetTemp": [{
87309
+ name: "deviceId",
87310
+ form: "single",
87311
+ optional: false
87312
+ }],
87313
+ "webrtcSession.addIceCandidate": [{
87314
+ name: "deviceId",
87315
+ form: "single",
87316
+ optional: false
87317
+ }],
87318
+ "webrtcSession.closeSession": [{
87319
+ name: "deviceId",
87320
+ form: "single",
87321
+ optional: false
87322
+ }],
87323
+ "webrtcSession.createSession": [{
87324
+ name: "deviceId",
87325
+ form: "single",
87326
+ optional: false
87327
+ }],
87328
+ "webrtcSession.getIceCandidates": [{
87329
+ name: "deviceId",
87330
+ form: "single",
87331
+ optional: false
87332
+ }],
87333
+ "webrtcSession.getSessionState": [{
87334
+ name: "deviceId",
87335
+ form: "single",
87336
+ optional: false
87337
+ }],
87338
+ "webrtcSession.handleAnswer": [{
87339
+ name: "deviceId",
87340
+ form: "single",
87341
+ optional: false
87342
+ }],
87343
+ "webrtcSession.handleOffer": [{
87344
+ name: "deviceId",
87345
+ form: "single",
87346
+ optional: false
87347
+ }],
87348
+ "webrtcSession.hasAdaptiveBitrate": [{
87349
+ name: "deviceId",
87350
+ form: "single",
87351
+ optional: false
87352
+ }],
87353
+ "webrtcSession.listStreams": [{
87354
+ name: "deviceId",
87355
+ form: "single",
87356
+ optional: false
87357
+ }],
87358
+ "zoneAnalytics.getCameraHistory": [{
87359
+ name: "deviceId",
87360
+ form: "single",
87361
+ optional: false
87362
+ }],
87363
+ "zoneAnalytics.getCurrentSnapshot": [{
87364
+ name: "deviceId",
87365
+ form: "single",
87366
+ optional: false
87367
+ }],
87368
+ "zoneAnalytics.getUnzonedHistory": [{
87369
+ name: "deviceId",
87370
+ form: "single",
87371
+ optional: false
87372
+ }],
87373
+ "zoneAnalytics.getZoneHistory": [{
87374
+ name: "deviceId",
87375
+ form: "single",
87376
+ optional: false
87377
+ }],
87378
+ "zoneRules.listRules": [{
87379
+ name: "deviceId",
87380
+ form: "single",
87381
+ optional: false
87382
+ }],
87383
+ "zoneRules.setRules": [{
87384
+ name: "deviceId",
87385
+ form: "single",
87386
+ optional: false
87387
+ }],
87388
+ "zones.addZone": [{
87389
+ name: "deviceId",
87390
+ form: "single",
87391
+ optional: false
87392
+ }],
87393
+ "zones.listZones": [{
87394
+ name: "deviceId",
87395
+ form: "single",
87396
+ optional: false
87397
+ }],
87398
+ "zones.removeZone": [{
87399
+ name: "deviceId",
87400
+ form: "single",
87401
+ optional: false
87402
+ }],
87403
+ "zones.updateZone": [{
87404
+ name: "deviceId",
87405
+ form: "single",
87406
+ optional: false
87407
+ }]
87408
+ });
84690
87409
  Object.freeze({
84691
87410
  "broker": "broker",
84692
87411
  "device-export": "device-export",