@camstack/addon-remote-storage 1.2.115 → 1.2.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  let node_crypto = require("node:crypto");
24
24
  let node_path = require("node:path");
25
25
  node_path = __toESM(node_path);
26
- //#region ../types/dist/event-category-C5xZWqz6.mjs
26
+ //#region ../types/dist/event-category-BGmz_oxW.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -556,6 +556,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
556
556
  */
557
557
  EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
558
558
  EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
559
+ /**
560
+ * A camera provider's RAW onboard motion push (D673): the `MotionOnMotionChanged`
561
+ * data shape without `triggeredBy`, built by `providerMotionEvent`. Its one
562
+ * consumer is the orchestrator's camera-sensors bridge, which re-emits the
563
+ * camera's EFFECTIVE onboard motion as `MotionOnMotionChanged` (a pass-through
564
+ * for a camera with no attached sensor). Providers must not emit
565
+ * `MotionOnMotionChanged` themselves; `scripts/check-onboard-motion-emitters.ts`
566
+ * keeps it so (camera-grid is a named, deferred exception).
567
+ */
559
568
  EventCategory["ProviderMotion"] = "provider.motion";
560
569
  EventCategory["ProviderDetection"] = "provider.detection";
561
570
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
@@ -5394,7 +5403,7 @@ var ZodIssueCode = {
5394
5403
  var ZodFirstPartyTypeKind;
5395
5404
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5396
5405
  //#endregion
5397
- //#region ../types/dist/sleep-gU6hqv4_.mjs
5406
+ //#region ../types/dist/sleep-CDCONfkt.mjs
5398
5407
  /**
5399
5408
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5400
5409
  * window to float samples (D455).
@@ -7423,7 +7432,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7423
7432
  return DeviceRole;
7424
7433
  }({});
7425
7434
  //#endregion
7426
- //#region ../types/dist/composition-Dq42XWp0.mjs
7435
+ //#region ../types/dist/composition-Bzr7GwgP.mjs
7427
7436
  /**
7428
7437
  * The ffmpeg build every CamStack node downloads, per platform and arch.
7429
7438
  *
@@ -7477,21 +7486,20 @@ function portable(target) {
7477
7486
  }
7478
7487
  portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7479
7488
  /**
7480
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7481
- * valutatore puro del suo FRONTE.
7489
+ * THE table "which boolean of this device type counts as HIGH", and the
7490
+ * pure evaluator of its EDGE.
7482
7491
  *
7483
- * Viveva dentro il builtin virtual-doorbell
7484
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
7485
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
7486
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
7487
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
7488
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
7489
- * SPOSTA qui e il doorbell la ri-esporta.
7492
+ * ONE table for everyone who asks "is this device HIGH?": the
7493
+ * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
7494
+ * sensor markers, the orchestrator's camera-sensors bridge and
7495
+ * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
7496
+ * A second copy would diverge at the first cap added, and the symptom — "the
7497
+ * sensor rings the doorbell but does not record" — is exactly D62.
7490
7498
  *
7491
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
7492
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
7493
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
7494
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
7499
+ * ⚠ NOT `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), and the
7500
+ * two must not be unified: that one answers "what is the state WORD for a
7501
+ * rule" (and includes `presence`, `cover`, `alarm-panel`), this one "which
7502
+ * boolean's EDGE counts". Deliberately different vocabularies.
7495
7503
  */
7496
7504
  /**
7497
7505
  * Known binary / switch source caps → the boolean slice field whose
@@ -7512,6 +7520,8 @@ var SOURCE_CAP_ACTIVE_FIELD = {
7512
7520
  vibration: "detected",
7513
7521
  tamper: "tampered"
7514
7522
  };
7523
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
7524
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
7515
7525
  /**
7516
7526
  * Error types for the safe expression engine. Two distinct classes so callers
7517
7527
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -9098,22 +9108,22 @@ var CLASS_MAP_MACRO_TARGETS = [
9098
9108
  "package"
9099
9109
  ];
9100
9110
  /**
9101
- * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
9102
- * un operatore può selezionare.
9111
+ * The FIRST-LEVEL macro classes: those an object detector emits and that
9112
+ * an operator can select.
9103
9113
  *
9104
- * Sono le tre offerte dallo step `object-detection`
9114
+ * They are the three offered by the `object-detection` step
9105
9115
  * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
9106
- * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
9107
- * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
9108
- * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
9109
- * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
9116
+ * `enabledMacroClasses`). `package` is in {@link CLASS_MAP_MACRO_TARGETS} and in
9117
+ * `MACRO_LABELS` — it is a true macro — but NOT here: it belongs to the step
9118
+ * `package-detection`, whose enablement is driven by zones, and offering the
9119
+ * same word twice has already made an operator turn on the COCO proxy
9110
9120
  * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
9111
9121
  *
9112
- * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
9113
- * dello step e una seconda volta come union `FirstLevelMacro`
9114
- * (`types/detection.ts`); una terza copia per il trigger di registrazione
9115
- * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
9116
- * successiva.
9122
+ * ONE list. Before today the same three were hand-written in the step's offer
9123
+ * and a second time as the union `FirstLevelMacro`
9124
+ * (`types/detection.ts`); a third copy for the recording trigger
9125
+ * (`RecordingTriggers.objectClasses`) would have made the later divergence
9126
+ * invisible.
9117
9127
  */
9118
9128
  var FIRST_LEVEL_MACRO_CLASSES = [
9119
9129
  "person",
@@ -9178,17 +9188,17 @@ var RecordingStorageModeSchema = _enum([
9178
9188
  "on-device-decision"
9179
9189
  ]);
9180
9190
  /**
9181
- * Le macro classi che possono aprire una finestra di registrazione — le stesse
9182
- * tre offerte dallo step `object-detection`, da UNA lista
9191
+ * The macro classes that can open a recording window — the same
9192
+ * three offered by the `object-detection` step, from ONE list
9183
9193
  * ({@link FIRST_LEVEL_MACRO_CLASSES}).
9184
9194
  */
9185
9195
  var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
9186
9196
  /**
9187
- * True quando `values` non ripete un elemento.
9197
+ * True when `values` does not repeat an element.
9188
9198
  *
9189
- * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
9190
- * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
9191
- * conterebbe due volte le sue finestre in `segmentMissedByMs`.
9199
+ * A duplicate is not harmless: every entry of `objectClasses` / `sensorDeviceIds`
9200
+ * becomes a SOURCE in `bandTriggerSources`, and the same source twice
9201
+ * would count its windows twice in `segmentMissedByMs`.
9192
9202
  */
9193
9203
  var noDuplicates = (values) => new Set(values).size === values.length;
9194
9204
  /** Which detectors trigger an `events`-mode band. */
@@ -9196,34 +9206,34 @@ var RecordingTriggersSchema = object({
9196
9206
  motion: boolean().optional(),
9197
9207
  audioThresholdDbfs: number().optional(),
9198
9208
  /**
9199
- * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
9200
- * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
9201
- * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
9202
- * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
9209
+ * The macro classes whose detection opens a window. ABSENT = the source
9210
+ * is not listened to; an EMPTY array is rejected, because "events band, object
9211
+ * trigger on, no class" is the same shape "enabled and records
9212
+ * nothing, forever" that `eventsBandCanEverDemand` is written against.
9203
9213
  *
9204
- * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
9205
- * quelle che hanno attraversato `enabledMacroClasses`, i
9206
- * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
9207
- * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
9208
- * finestre — vedi `recorder/object-trigger.ts`.
9214
+ * The signal read is ALREADY FILTERED: only `source: 'pipeline'` detections, i.e.
9215
+ * those that went through `enabledMacroClasses`, the
9216
+ * `minConfidence<Macro>` and the full-frame guard. The camera's onboard AI
9217
+ * (`source: 'onboard'`) goes through none of those gates and does NOT open
9218
+ * windows — see `recorder/object-trigger.ts`.
9209
9219
  */
9210
9220
  objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
9211
9221
  /**
9212
- * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
9213
- * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
9222
+ * The LINKED devices whose RISING EDGE opens a window. Absent = the source
9223
+ * is not listened to; an empty array is rejected for the same reason as
9214
9224
  * `objectClasses`.
9215
9225
  *
9216
- * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
9217
- * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
9218
- * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
9219
- * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9220
- * device (D12) — mai un elenco globale di cap.
9226
+ * These are SOURCE device ids, not cameras: the band names them, so the
9227
+ * hot path (`DeviceStateChanged`, at bus rate across the whole fleet)
9228
+ * does no RPC. The OFFER from which the operator picks them is another question, and
9229
+ * is resolved with `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9230
+ * device (D12) — never a global list of caps.
9221
9231
  *
9222
- * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
9223
- * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
9224
- * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
9225
- * il livello: un contatto trovato già aperto al riavvio del runner non fa
9226
- * registrare.
9232
+ * What "high" means depends on the device TYPE and is not decided here:
9233
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) is THE table,
9234
+ * the same one as the composite doorbell (D671). And it is the EDGE, not
9235
+ * the level: a contact found already open at runner restart does not
9236
+ * record.
9227
9237
  */
9228
9238
  sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
9229
9239
  });
@@ -10300,7 +10310,7 @@ var DecoderSessionConfigSchema = object({
10300
10310
  * - **VALUE** — at which level, for which scope, until when. That stays ONE
10301
10311
  * thing: the logging settings document on the `system` cap. Two authorities
10302
10312
  * over the values is the exact defect
10303
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
10313
+ * `docs/design/plans/2026-08-26-per-component-logging.md` was written to
10304
10314
  * remove; re-introducing it from the cure side would be grotesque.
10305
10315
  *
10306
10316
  * Nothing in this file reads a clock, an env var or a store. The registry is
@@ -12011,7 +12021,7 @@ var BrokerStatusEnum = _enum([
12011
12021
  "unreachable",
12012
12022
  "error"
12013
12023
  ]);
12014
- var BrokerInfoSchema$1 = object({
12024
+ var BrokerInfoSchema = object({
12015
12025
  /** Stable broker id. Persisted; survives addon restarts. */
12016
12026
  id: string(),
12017
12027
  /** Addon id of the provider that OWNS this broker.
@@ -12144,7 +12154,7 @@ var GetStateInputSchema = object({
12144
12154
  * HA: entity_id (returns the cached entity state). */
12145
12155
  key: string()
12146
12156
  });
12147
- method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, BrokerInfoSchema$1.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12157
+ method(ListInputSchema, array(BrokerInfoSchema)), method(GetInputSchema, BrokerInfoSchema.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12148
12158
  kind: "mutation",
12149
12159
  auth: "admin"
12150
12160
  }), method(RemoveInputSchema, _void(), {
@@ -13753,7 +13763,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13753
13763
  *
13754
13764
  * ⚠ An earlier version of this comment blamed Zod stripping, and that
13755
13765
  * was wrong — corrected 2026-08-26 after the hop map
13756
- * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
13766
+ * (`docs/design/2026-08-26-topic-hop-map.md`) traced the path.
13757
13767
  * There is **no Zod parse at all** between the door and the engine: the
13758
13768
  * dispatcher forwards the payload verbatim and UDS carries it whole. A
13759
13769
  * field declared here reaches `SqliteSettingsBackend` either way.
@@ -14509,7 +14519,17 @@ var SiteZoneSchema = object({
14509
14519
  var ProviderStatusSchema = object({
14510
14520
  connected: boolean(),
14511
14521
  deviceCount: number(),
14512
- error: string().optional()
14522
+ error: string().optional(),
14523
+ /**
14524
+ * `'orchestrator'`: this provider writes NO camera's `motion` runtime slice
14525
+ * — the orchestrator is its only writer (D696). Set by `BaseDeviceProvider`,
14526
+ * which every provider bundles, so only a provider BUILT from D696-amended
14527
+ * code says it. ABSENT means an older provider that may still write it (raw
14528
+ * VMD writes, a boot seed), and the orchestrator's provider gate keeps that
14529
+ * provider's cameras unwritten (`camera-sensors/motion-slice-provider-gate.ts`)
14530
+ * — the provider twin of the runner's `getLocalLoad().motionSliceWriter`.
14531
+ */
14532
+ motionSliceWriter: literal("orchestrator").optional()
14513
14533
  });
14514
14534
  object({
14515
14535
  externalId: string(),
@@ -15307,28 +15327,10 @@ method(object({
15307
15327
  deviceIds: array(number()).readonly(),
15308
15328
  caps: array(string()).readonly().optional()
15309
15329
  }), record(string(), record(string(), unknown().nullable())));
15310
- object({
15311
- /** Ms epoch of the last press. null = never observed since this provider started. */
15312
- lastPressedAt: number().nullable(),
15313
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
15314
- pressCountSinceStart: number()
15315
- });
15316
- object({
15317
- deviceId: number(),
15318
- timestamp: number()
15319
- });
15320
- /**
15321
- * The `doorbell-source` extension slot's editable value (D648): the camera's
15322
- * virtual-doorbell trigger devices, exactly what the wrapper's
15323
- * `doorbellSources` persists (as `{ deviceId }` records).
15324
- */
15325
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
15330
+ var ExtensionSlotIdSchema = _enum(["linked-devices"]);
15326
15331
  DeviceType.Camera;
15327
- DeviceType.Button, DeviceType.Camera;
15328
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
15329
- DeviceType.Camera, DeviceType.Camera;
15330
15332
  /** A slot's editable value, discriminated on `slot`. */
15331
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15333
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15332
15334
  DeviceType.Camera;
15333
15335
  var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
15334
15336
  state: literal("unreachable"),
@@ -15365,10 +15367,11 @@ var ExtensionSlotSourceSchema = object({
15365
15367
  });
15366
15368
  /**
15367
15369
  * A DIFFERENT authority that gates whether the slot's sources do anything —
15368
- * reported read-only, named, never written through this contract (D62). The
15369
- * virtual doorbell's sources ring only while the wrapper is bound to the
15370
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
15371
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
15370
+ * reported read-only, named, never written through this contract (D62) — e.g.
15371
+ * a slot whose sources act only while a wrapper is bound to the host, a
15372
+ * binding that belongs to `deviceManager.setWrapperActive`. No slot reports
15373
+ * one today (`linked-devices` answers `null`). `unknown` means the gate could
15374
+ * not be read; it is not "inactive" (D315).
15372
15375
  */
15373
15376
  var ExtensionSlotActivationSchema = object({
15374
15377
  state: _enum([
@@ -15384,7 +15387,7 @@ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
15384
15387
  /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
15385
15388
  slotRef: string(),
15386
15389
  slot: ExtensionSlotIdSchema,
15387
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
15390
+ /** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
15388
15391
  authority: string(),
15389
15392
  activation: ExtensionSlotActivationSchema.nullable(),
15390
15393
  value: ExtensionSlotValueSchema,
@@ -15662,7 +15665,7 @@ method(object({
15662
15665
  * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
15663
15666
  * ends in a log line, and a log line is the thing the operator asked to stop
15664
15667
  * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
15665
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
15668
+ * ricontrolliamo tutti questi punti"* [can we instrument these errors in the meantime? That way next round we recheck all these points]. Reading them meant grepping Loki and
15666
15669
  * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
15667
15670
  * media blackout were both diagnosed. The counters stay; this is where they can
15668
15671
  * be READ.
@@ -17038,7 +17041,7 @@ var BrokerStatusSchema$1 = _enum([
17038
17041
  "unreachable",
17039
17042
  "tls-error"
17040
17043
  ]);
17041
- var BrokerInfoSchema = object({
17044
+ var BrokerInfoSchema$1 = object({
17042
17045
  id: string(),
17043
17046
  name: string(),
17044
17047
  url: string(),
@@ -17101,7 +17104,7 @@ var StatusSchema = object({
17101
17104
  brokerCount: number(),
17102
17105
  embeddedRunning: boolean()
17103
17106
  });
17104
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17107
+ method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17105
17108
  kind: "mutation",
17106
17109
  auth: "admin"
17107
17110
  }), method(IdInputSchema, _void(), {
@@ -19277,78 +19280,16 @@ var NcRuleInputSchema = object({
19277
19280
  */
19278
19281
  confirm: NcConfirmSchema.optional(),
19279
19282
  /**
19280
- * WAIT for face/plate recognition before saying anything.
19281
- *
19282
- * A notification's TEXT is frozen at enqueue and its media is re-resolved at
19283
- * send; the identity is neither. A face is confirmed after `confirmFrames`
19284
- * agreeing observations — p50 **11.4 s** after the track was first seen,
19285
- * measured on this hub — and an `immediate` rule enqueues on the first object
19286
- * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
19287
- * immediate path, and no amount of media re-resolution fixes a sentence.
19288
- *
19289
- * Only two honest answers exist, and this flag picks between them. It has
19290
- * effect ONLY on a rule that declares a recognition scope
19291
- * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
19292
- * other rule there is nothing to wait for and the flag is inert.
19293
- *
19294
- * | value | what happens |
19295
- * | --- | --- |
19296
- * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
19297
- * | 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) |
19298
- *
19299
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19300
- * on the addon cap path, and absent has to keep meaning exactly what every
19301
- * rule authored before this field meant.
19302
- *
19303
- * The cost of `true` is stated here because the editor states it too: a rule
19304
- * that waits also inherits track-close SEMANTICS — its `zones` condition
19305
- * tests every zone the track visited and a `crossing` condition can no longer
19306
- * be satisfied, because a closed track carries no crossing.
19307
- */
19308
- waitForEnhancement: boolean().optional(),
19309
- /**
19310
- * GROUP a burst of subjects into ONE notification that grows.
19311
- *
19312
- * Seconds of quiet after the last matching subject before the burst is
19313
- * considered over. While it is open, the first subject enqueues immediately —
19314
- * **exactly as today, with no added latency** — and every real growth (a new
19315
- * subject, or a name confirmed on one already in it) REPLACES that
19316
- * notification with an updated one naming everybody. The push carries the
19317
- * group's own coalescing tag, so the phone replaces rather than stacks.
19318
- *
19319
- * `0` / absent = off, and off is today's behaviour byte for byte.
19320
- *
19321
- * ### Why an idle cutoff and not a window
19322
- *
19323
- * The measured seven-person arrival on device 590 spans 110 s with every
19324
- * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
19325
- * idle cutoff holds it as one and ends it when the arrival actually ends.
19326
- * 30 s is a widely shipped value for the same decision.
19327
- *
19328
- * ### What it replaces
19329
- *
19330
- * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
19331
- * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
19332
- * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
19333
- * track that did fire and 7 carrying a confirmed identity nobody heard about.
19334
- * A group collapses the same volume by MERGING, so the cooldown becomes a
19335
- * budget over GROUPS — which is what it always meant — and a growth is never
19336
- * throttled by the window its own first member spent.
19337
- *
19338
- * ### Interaction with {@link waitForEnhancement}
19339
- *
19340
- * They compose, and the order matters. `waitForEnhancement` defers the rule to
19341
- * TRACK CLOSE, so with both set the group is opened by the first member to
19342
- * CLOSE — already carrying its name — and grows as later members close. That
19343
- * is later, and complete. With grouping alone the group opens on the first
19344
- * object event and picks up names as they are confirmed, through the growth
19345
- * path. Neither combination fires twice for one subject.
19346
- *
19347
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19348
- * on the addon cap path, so absent must keep meaning what it meant before this
19349
- * field existed.
19283
+ * IMMEDIATE GROUPED (D692): the first matching subject opens a window of this many
19284
+ * seconds; the rule sends ONE message when the window closes — early, once every
19285
+ * subject in it is final, never before {@link NC_GROUPING_MIN_SEC} — naming everybody
19286
+ * the rule matched, with one mosaic tile per subject.
19287
+ * Absent = plain immediate (one message per track, at the first match).
19288
+ * Only an `immediate` DETECTION rule may carry it (`ruleGroupingApplies`, enforced on
19289
+ * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
19290
+ * path does not run Zod defaults.
19350
19291
  */
19351
- groupIdleSec: number().int().min(0).max(600).optional()
19292
+ groupingSec: number().int().min(5).max(60).optional()
19352
19293
  });
19353
19294
  /**
19354
19295
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -19396,9 +19337,10 @@ var NcRuleClearableKeySchema = _enum([
19396
19337
  "targetUsers",
19397
19338
  "snoozeOptions",
19398
19339
  "snoozeAllowGlobal",
19399
- "waitForEnhancement",
19400
19340
  "actions",
19401
19341
  "confirm",
19342
+ "groupingSec",
19343
+ "waitForEnhancement",
19402
19344
  "groupIdleSec"
19403
19345
  ]);
19404
19346
  var NcRuleUpdateInputSchema = object({
@@ -19420,9 +19362,25 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19420
19362
  */
19421
19363
  disabledTargetIds: array(string()).default([])
19422
19364
  });
19365
+ /**
19366
+ * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19367
+ * still-true state. `backups-exports` and `alarm` are not sections — a completed
19368
+ * job is not an open state, and the alarm stays instant.
19369
+ */
19370
+ var NcStateSummarySectionIdSchema = _enum([
19371
+ "device-state",
19372
+ "streams",
19373
+ "battery",
19374
+ "consumables",
19375
+ "tanks",
19376
+ "detection-blind",
19377
+ "nodes",
19378
+ "addons",
19379
+ "updates"
19380
+ ]);
19423
19381
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
19424
19382
  var NcStateSummaryPreviewSectionSchema = object({
19425
- id: string(),
19383
+ id: NcStateSummarySectionIdSchema,
19426
19384
  status: _enum(["read", "unavailable"]),
19427
19385
  items: number().int().nonnegative(),
19428
19386
  reason: string().optional()
@@ -19432,7 +19390,7 @@ var NcStateSummaryPreviewSectionSchema = object({
19432
19390
  * a `rule` split (no `section`), one section's on a `section` split.
19433
19391
  */
19434
19392
  var NcStateSummaryPreviewRowSchema = object({
19435
- section: string().optional(),
19393
+ section: NcStateSummarySectionIdSchema.optional(),
19436
19394
  /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
19437
19395
  title: string(),
19438
19396
  text: string(),
@@ -19869,6 +19827,29 @@ object({
19869
19827
  firstAt: number(),
19870
19828
  lastAt: number()
19871
19829
  });
19830
+ var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
19831
+ var NcAlarmDelaySecSchema = number().int().min(0).max(600);
19832
+ var NcAlarmTriggeredSecSchema = number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
19833
+ var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
19834
+ var NcAlarmModeDelaysListSchema = array(object({
19835
+ mode: AlarmArmModeSchema,
19836
+ exitDelaySec: NcAlarmDelaySecSchema.optional(),
19837
+ entryDelaySec: NcAlarmDelaySecSchema.optional(),
19838
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
19839
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
19840
+ var NcAlarmModeDelaysEditListSchema = array(object({
19841
+ mode: AlarmArmModeSchema,
19842
+ exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
19843
+ entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
19844
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
19845
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
19846
+ /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
19847
+ var NcAlarmNonBlockingEditSchema = object({
19848
+ mode: AlarmArmModeSchema,
19849
+ deviceId: number().int().positive(),
19850
+ /** true = on the list (may be open, does not block); false = off the list (blocks). */
19851
+ nonBlocking: boolean()
19852
+ });
19872
19853
  /**
19873
19854
  * The per-mode lists, at most one per mode. Two lists for one mode have no
19874
19855
  * meaning a reader could agree on (union? the last one?), so they are refused
@@ -19877,10 +19858,10 @@ object({
19877
19858
  var NcAlarmNonBlockingListSchema = array(object({
19878
19859
  mode: AlarmArmModeSchema,
19879
19860
  deviceIds: array(number().int()).max(200)
19880
- })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
19861
+ })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
19881
19862
  /**
19882
- * The three durations the panel's state machine runs on, who hears about its
19883
- * transitions, and which openings each mode tolerates.
19863
+ * The three durations — global, and per mode since D697 — the panel's state
19864
+ * machine runs on, who hears about its transitions, and which openings each mode tolerates.
19884
19865
  *
19885
19866
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
19886
19867
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -19890,15 +19871,15 @@ var NcAlarmNonBlockingListSchema = array(object({
19890
19871
  */
19891
19872
  var NcAlarmSettingsSchema = object({
19892
19873
  /** Grace period between arming and the mode taking effect. 0 = immediate. */
19893
- exitDelaySec: number().int().min(0).max(600),
19874
+ exitDelaySec: NcAlarmDelaySecSchema,
19894
19875
  /** Grace period between a trigger and the alarm firing. 0 = immediate. */
19895
- entryDelaySec: number().int().min(0).max(600),
19876
+ entryDelaySec: NcAlarmDelaySecSchema,
19896
19877
  /**
19897
19878
  * How long `triggered` lasts before the panel re-arms itself.
19898
19879
  * **0 = until an operator disarms it** — the behaviour before this field
19899
19880
  * existed, and therefore what an untouched install keeps doing.
19900
19881
  */
19901
- triggeredDurationSec: number().int().min(0).max(3600),
19882
+ triggeredDurationSec: NcAlarmTriggeredSecSchema,
19902
19883
  /**
19903
19884
  * Deprecated by D630 — every transition is announced. Parsed, never read;
19904
19885
  * remove after one release. Optional so an older admin's patch (which still
@@ -19921,17 +19902,30 @@ var NcAlarmSettingsSchema = object({
19921
19902
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
19922
19903
  * parses as "every opening blocks" — the behaviour it was written under.
19923
19904
  */
19924
- nonBlocking: NcAlarmNonBlockingListSchema.default([])
19905
+ nonBlocking: NcAlarmNonBlockingListSchema.default([]),
19906
+ /**
19907
+ * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
19908
+ * without a field, uses the global value above — so `[]`, the default, is exactly the
19909
+ * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
19910
+ */
19911
+ modeDelays: NcAlarmModeDelaysListSchema.default([])
19925
19912
  });
19926
19913
  /**
19927
19914
  * Every field optional — a tab edits one control at a time.
19928
19915
  *
19929
- * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
19930
- * remove an inner `.default()`, so a patch that never named the field would
19931
- * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
19932
- * (the same trap {@link NcRulePatchSchema} documents).
19916
+ * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
19917
+ * `.partial()` does not remove an inner `.default()`, so a patch that never
19918
+ * named the field would parse to `[]` and wipe every mode's list on an
19919
+ * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
19920
+ * `modeDelays` means EDITS, not the stored overrides.
19933
19921
  */
19934
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
19922
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
19923
+ nonBlocking: NcAlarmNonBlockingListSchema,
19924
+ /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
19925
+ modeDelays: NcAlarmModeDelaysEditListSchema,
19926
+ /** ONE membership change; the panel applies it to the list it holds at its turn. */
19927
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema
19928
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
19935
19929
  /**
19936
19930
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
19937
19931
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -20031,6 +20025,28 @@ var NcAlarmConfigSchema = object({
20031
20025
  */
20032
20026
  live: NcAlarmLiveSchema.optional()
20033
20027
  });
20028
+ var NcAlarmSensorReadingSchema = _enum([
20029
+ "open",
20030
+ "closed",
20031
+ "unknown",
20032
+ "not-opening"
20033
+ ]);
20034
+ /** One device's state as the alarm reads it — the classifier the arm check uses. */
20035
+ var NcAlarmSensorStateSchema = object({
20036
+ deviceId: number().int().positive(),
20037
+ reading: NcAlarmSensorReadingSchema,
20038
+ /** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
20039
+ state: string().nullable(),
20040
+ /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
20041
+ unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
20042
+ /** The device manager's liveness as the centre mirrors it; null = not known. */
20043
+ online: boolean().nullable()
20044
+ });
20045
+ var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
20046
+ var NcAlarmSensorsSchema = object({
20047
+ sensors: array(NcAlarmSensorStateSchema),
20048
+ readAt: number().int().nonnegative()
20049
+ });
20034
20050
  /**
20035
20051
  * ONE rule's demand on ONE camera's clip ring.
20036
20052
  *
@@ -20121,7 +20137,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
20121
20137
  }), method(object({}), NcAlarmConfigSchema, { auth: "admin" }), method(object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
20122
20138
  kind: "mutation",
20123
20139
  auth: "admin"
20124
- });
20140
+ }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
20125
20141
  /**
20126
20142
  * Zod schemas for persisted record types.
20127
20143
  *
@@ -20468,12 +20484,26 @@ var CameraSensorRefSchema = object({
20468
20484
  function cameraSensorRefKey(ref) {
20469
20485
  return `${ref.addonId}/${ref.stableId}`;
20470
20486
  }
20471
- /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20472
- var CameraSensorsModeSchema = _enum(["in-addition"]);
20473
- var CameraSensorsConfigSchema = object({
20474
- mode: CameraSensorsModeSchema,
20475
- sensors: array(CameraSensorRefSchema).min(1).max(8)
20476
- }).superRefine((value, ctx) => {
20487
+ /**
20488
+ * A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
20489
+ * `replace`: its motion is the camera's, and while one is READY the camera's
20490
+ * own motion no longer counts on its own. `confirm`: while one is READY the
20491
+ * camera's own motion counts only while a confirm sensor is active — with or
20492
+ * without a replace sensor (spec C2, the controller's ruling on Q1).
20493
+ */
20494
+ var CameraSensorModeSchema = _enum([
20495
+ "in-addition",
20496
+ "confirm",
20497
+ "replace"
20498
+ ]);
20499
+ /** A stored sensor with no mode — every CS-1 row — is in addition. */
20500
+ var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
20501
+ /**
20502
+ * A camera's attached sensors. A CS-1 row carried an envelope
20503
+ * `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
20504
+ * what the write stores (D672 rule 3), so it is never written again.
20505
+ */
20506
+ var CameraSensorsConfigSchema = object({ sensors: array(CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) })).min(1).max(8) }).superRefine((value, ctx) => {
20477
20507
  const seen = /* @__PURE__ */ new Set();
20478
20508
  value.sensors.forEach((ref, index) => {
20479
20509
  const key = cameraSensorRefKey(ref);
@@ -20485,39 +20515,41 @@ var CameraSensorsConfigSchema = object({
20485
20515
  seen.add(key);
20486
20516
  });
20487
20517
  });
20488
- var RefFields = {
20518
+ /** Every attachment names its mode; an older orchestrator that does not is in addition. */
20519
+ var AttachmentFields = {
20489
20520
  addonId: string(),
20490
- stableId: string()
20521
+ stableId: string(),
20522
+ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
20491
20523
  };
20492
20524
  var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20493
20525
  object({
20494
- ...RefFields,
20526
+ ...AttachmentFields,
20495
20527
  state: literal("ready"),
20496
20528
  deviceId: number(),
20497
20529
  name: string(),
20498
20530
  caps: array(string()).readonly()
20499
20531
  }),
20500
20532
  object({
20501
- ...RefFields,
20533
+ ...AttachmentFields,
20502
20534
  state: literal("offline"),
20503
20535
  deviceId: number(),
20504
20536
  name: string(),
20505
20537
  reason: _enum(["offline", "disabled"])
20506
20538
  }),
20507
20539
  object({
20508
- ...RefFields,
20540
+ ...AttachmentFields,
20509
20541
  state: literal("missing"),
20510
20542
  reason: string()
20511
20543
  }),
20512
20544
  object({
20513
- ...RefFields,
20545
+ ...AttachmentFields,
20514
20546
  state: literal("unsupported"),
20515
20547
  deviceId: number(),
20516
20548
  name: string(),
20517
20549
  reason: string()
20518
20550
  }),
20519
20551
  object({
20520
- ...RefFields,
20552
+ ...AttachmentFields,
20521
20553
  state: literal("unreadable"),
20522
20554
  reason: string()
20523
20555
  })
@@ -20530,17 +20562,49 @@ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20530
20562
  * say.
20531
20563
  */
20532
20564
  var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20565
+ /** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
20566
+ var CameraOwnMotionSchema = _enum([
20567
+ "always",
20568
+ "when-confirmed",
20569
+ "never"
20570
+ ]);
20571
+ /** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
20572
+ var CameraSensorsNotEnforcedSchema = _enum(["provider-not-split"]);
20573
+ /**
20574
+ * The camera's OWN onboard motion (D696): the raw input the bridge combines,
20575
+ * never the effective value. The camera's `motion` slice is the effective one,
20576
+ * so a view of the camera's own row reads this.
20577
+ * - `atMs`: when the bridge received the camera's last own edge.
20578
+ * - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
20579
+ * AND it is younger than the window the bridge counts the camera's own motion
20580
+ * for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
20581
+ * sends a clear (heartbeats only) therefore reads `false` once no push came
20582
+ * within the window; a status read is a snapshot, so a live view re-asks.
20583
+ * Absent = this orchestrator has not received the camera's own motion since it
20584
+ * started (unknown, never "quiet"), or an orchestrator older than D696.
20585
+ */
20586
+ var CameraRawLevelSchema = object({
20587
+ detected: boolean(),
20588
+ atMs: number()
20589
+ });
20533
20590
  var CameraSensorsStatusSchema = discriminatedUnion("state", [
20534
- object({ state: literal("not-configured") }),
20591
+ object({
20592
+ state: literal("not-configured"),
20593
+ cameraLevel: CameraRawLevelSchema.optional()
20594
+ }),
20535
20595
  object({
20536
20596
  state: literal("unreadable"),
20537
- reason: string()
20597
+ reason: string(),
20598
+ cameraLevel: CameraRawLevelSchema.optional()
20538
20599
  }),
20539
20600
  object({
20540
20601
  state: literal("configured"),
20541
- mode: CameraSensorsModeSchema,
20602
+ cameraLevel: CameraRawLevelSchema.optional(),
20542
20603
  sensors: array(CameraSensorAttachmentSchema).readonly(),
20543
- notForwarded: CameraSensorsNotForwardedSchema.optional()
20604
+ /** Absent = an orchestrator older than CS-2: it does not say (D315). */
20605
+ cameraMotion: CameraOwnMotionSchema.optional(),
20606
+ notForwarded: CameraSensorsNotForwardedSchema.optional(),
20607
+ notEnforced: CameraSensorsNotEnforcedSchema.optional()
20544
20608
  })
20545
20609
  ]);
20546
20610
  Object.freeze({
@@ -21225,14 +21289,14 @@ var TrackSchema = object({
21225
21289
  * `derive-returned-null tile=standin`,
21226
21290
  * `native-tile tile=native no-key-frame-at-close`, …) written at close and
21227
21291
  * CLEARED by the late-keyFrame upgrade when a native tile lands after all.
21228
- * The operator-facing answer to "perché manca l'immagine?" on a track whose
21292
+ * The operator-facing answer to "perché manca l'immagine?" [why is the image missing?] on a track whose
21229
21293
  * tile is a face/plate stand-in, a raster crop, or an icon.
21230
21294
  *
21231
21295
  * **`no-key-frame-at-close` (D400).** The line used to be written ONLY when
21232
21296
  * the TILE ranked below native, so a track whose thumbnail landed natively
21233
21297
  * while its key frame missed carried no reason at all — three of the four
21234
21298
  * `MISSING_KEYFRAME` tracks in the 2026-09-07 operator audit were exactly
21235
- * that shape, and their "niente ultimo" had no cause anywhere on the row.
21299
+ * that shape, and their "niente ultimo" [no last frame] had no cause anywhere on the row.
21236
21300
  * The key frame is the head of a cascade (`keyFrame ⇒ keyFrameSmall ⇒ the
21237
21301
  * lastFrame copy source`), so its absence is now on this line whatever the
21238
21302
  * tile ended up being.
@@ -21319,7 +21383,7 @@ var MotionEventSchema = object({
21319
21383
  /**
21320
21384
  * Ms offsets from `timestamp` (the episode's own first rising edge, so the
21321
21385
  * first entry is always `0`) of every genuine off→on transition the
21322
- * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
21386
+ * episode saw — "ogni evento on si deve salvare" [every ON event must be saved] (D475). NOT one entry per
21323
21387
  * push: a firmware source that keepalives at ~1 Hz for the whole burst
21324
21388
  * (Reolink, Hikvision) produces exactly one edge; a source that reports an
21325
21389
  * explicit `false` mid-episode and then resumes before the quiet window
@@ -21843,6 +21907,7 @@ var KeyEventSchema = object({
21843
21907
  bestEventId: string(),
21844
21908
  /** Track lifetime in ms (lastSeen - firstSeen). */
21845
21909
  windowMs: number().optional(),
21910
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
21846
21911
  ...TrackFlagFields,
21847
21912
  ...TrackRetrainFields
21848
21913
  });
@@ -23731,8 +23796,10 @@ var RunnerCameraConfigSchema = object({
23731
23796
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
23732
23797
  * analyzer providers) running on decoded frames in this runner.
23733
23798
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
23734
- * analytics). The provider emits ProviderMotion
23735
- * events; the orchestrator forwards to `reportMotion`.
23799
+ * analytics). The provider emits its raw push as `ProviderMotion`
23800
+ * (D673); the orchestrator's camera-sensors bridge re-emits the camera's
23801
+ * effective motion as `MotionOnMotionChanged`, which is what reaches
23802
+ * `reportMotion`.
23736
23803
  */
23737
23804
  motionSources: MotionSourcesSchema.default(["analyzer"]),
23738
23805
  /**
@@ -23909,7 +23976,15 @@ var RunnerLocalLoadSchema = object({
23909
23976
  hasGpu: boolean(),
23910
23977
  inferenceBackend: string().optional(),
23911
23978
  cpuCores: number().optional()
23912
- })
23979
+ }),
23980
+ /**
23981
+ * Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
23982
+ * runner never writes it. ABSENT = a runner older than D696, which still
23983
+ * writes the slice from its phase machine; the orchestrator does not write
23984
+ * while any runner node is absent-or-unanswered, so one slice never has two
23985
+ * writers.
23986
+ */
23987
+ motionSliceWriter: literal("orchestrator").optional()
23913
23988
  });
23914
23989
  /**
23915
23990
  * Aggregate runtime metrics for the runner's whole local pool. Mirrors the
@@ -26605,7 +26680,7 @@ var ClipPlaybackSchema = object({
26605
26680
  * **One authorised yes is one wake.** `next-natural` — take the clip the next
26606
26681
  * time the camera is awake for its own reasons — is deliberately not a member:
26607
26682
  * it was proposed, it is free on the battery, and the operator declined it on
26608
- * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
26683
+ * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."* [When an export is requested the camera wakes up],
26609
26684
  * D558 "Considered and not taken").
26610
26685
  */
26611
26686
  var ClipWakeSchema = _enum(["authorised"]);
@@ -26866,7 +26941,7 @@ DeviceType.Camera, method(object({
26866
26941
  * It replaced `sources?: string[]`, a VIEW filter over source ids that
26867
26942
  * assumed the answer was a fan-out over everything a camera has. The
26868
26943
  * operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
26869
- * solo"* — so the list is asked of one provider at a time and there is
26944
+ * solo"* [there is only one user] — so the list is asked of one provider at a time and there is
26870
26945
  * nothing to filter out of it.
26871
26946
  */
26872
26947
  provider: string().min(1)
@@ -28810,6 +28885,17 @@ object({
28810
28885
  deviceId: number(),
28811
28886
  status: DeviceStatusSchema
28812
28887
  });
28888
+ object({
28889
+ /** Ms epoch of the last press. null = never observed since this provider started. */
28890
+ lastPressedAt: number().nullable(),
28891
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
28892
+ pressCountSinceStart: number()
28893
+ });
28894
+ object({
28895
+ deviceId: number(),
28896
+ timestamp: number()
28897
+ });
28898
+ DeviceType.Button, DeviceType.Camera;
28813
28899
  /**
28814
28900
  * Enum-state sensor — a string value picked from a finite option set.
28815
28901
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -30454,9 +30540,9 @@ object({
30454
30540
  /** Ms epoch of the last detected-true observation. Null if never detected. */
30455
30541
  lastDetectedAt: number().nullable(),
30456
30542
  /**
30457
- * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
30458
- * `null` while false and on every `Sensor` device (D475) — see that
30459
- * constant's doc for the one-authority rule.
30543
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
30544
+ * orchestrator writes it, D696), `null` while false and on every `Sensor`
30545
+ * device (D475) — see that constant's doc for the one-authority rule.
30460
30546
  *
30461
30547
  * ## Reading this field still arms nothing
30462
30548
  *
@@ -30474,38 +30560,41 @@ object({
30474
30560
  * `addon-export-alexa/src/motion-clear-hold.ts` and
30475
30561
  * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
30476
30562
  *
30477
- * ## Who writes it
30478
- *
30479
- * - **Cameras** — the runner's phase machine, `active → watching` on
30480
- * `cooldown_expired`, which then writes this slice with
30481
- * `detected: false` (`handlePhaseChanged` in
30482
- * `pipeline-runner/index.ts`). It produces the FALLING edge, which
30483
- * matters most for the sources that only ever push a rising one:
30484
- * Reolink emits `MotionOnMotionChanged { detected: true }` and never
30485
- * a false.
30486
- * - **Sensors** (Home Assistant binary sensors, Homematic) — the
30487
- * provider pushes the false itself, from the upstream system's own
30488
- * state change. No phase machine is involved.
30489
- *
30490
- * ### The phase machine is CANONICAL, not sole — and that is a defect
30491
- *
30492
- * An earlier revision of this docblock (mine, 2026-09-12) claimed the
30493
- * phase machine is the sole writer for a camera. It is not.
30494
- * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
30495
- * `setCapSlice(motionCapability, …)` on their own rising edge, and
30496
- * Hikvision's comment says why: it read THIS docblock, agreed the
30497
- * runner is canonical, and wrote anyway to avoid per-tick churn. So
30498
- * two authorities can disagree about one slice, which this repo
30499
- * forbids, and the doc said otherwise — which is worse than saying
30500
- * nothing, because it reads as verification.
30501
- *
30502
- * This predates D475 and is not fixed there: the fix touches every
30503
- * camera provider. Recorded in D475's Consequences. Do not restore the
30504
- * "sole writer" wording without also removing the other writers.
30563
+ * ## Who writes it (D696)
30564
+ *
30565
+ * - **Cameras** — the orchestrator, and nothing else. It writes the camera's
30566
+ * EFFECTIVE motion (`addon-pipeline-orchestrator`
30567
+ * `camera-sensors/motion-slice.ts`), alongside the effective
30568
+ * `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
30569
+ * for a split camera, an unsplit emitter's direct event, the analyzer and
30570
+ * device activity, as the camera's `motionSources` counts them. A source
30571
+ * that never sends a clear falls one window after its last `true`.
30572
+ * `lastDetectedAt` moves only on an effective rise. No camera provider
30573
+ * writes this slice (not even a boot seed), and neither does the runner's
30574
+ * phase machine: its phase is "a session is attached" for every on-motion
30575
+ * session, not motion, and it now has its own `DetectionPhaseTransition`.
30576
+ * A provider's `isDetected` answers from the hub's slice.
30577
+ * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
30578
+ * imports) — the provider pushes both edges itself, from the upstream
30579
+ * system's own state change. That is the sensor's own fact.
30580
+ *
30581
+ * `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
30582
+ * `motion` slice; each sensor writer is a named exemption.
30583
+ *
30584
+ * This replaces the D475 §4 rule ("the phase machine is the sole writer").
30585
+ * That rule was never true: Hikvision and Amcrest wrote the slice raw beside
30586
+ * it, every provider seeded it at boot, and every session left it
30587
+ * `detected: true` after its detach.
30505
30588
  */
30506
30589
  autoClearAfterMs: number().nullable()
30507
30590
  });
30508
- object({
30591
+ /**
30592
+ * Payload of `motion.onMotionChanged` event + the corresponding bus
30593
+ * event `EventCategory.MotionOnMotionChanged`. Single source of truth
30594
+ * — both the cap event surface and the bus payload type alias to this
30595
+ * schema.
30596
+ */
30597
+ var MotionOnMotionChangedDataSchema = object({
30509
30598
  deviceId: number(),
30510
30599
  detected: boolean(),
30511
30600
  timestamp: number(),
@@ -32970,7 +33059,7 @@ var ReadGopBytesResultSchema = object({
32970
33059
  /**
32971
33060
  * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
32972
33061
  * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
32973
- * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
33062
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-clip-replay-on-pipeline.md`):
32974
33063
  * a replay needs several seconds of native pixels, not one frame.
32975
33064
  *
32976
33065
  * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
@@ -35586,6 +35675,7 @@ DeviceType.Camera, method(object({
35586
35675
  detection: array(ZoneRuleSchema).readonly(),
35587
35676
  package: array(ZoneRuleSchema).readonly()
35588
35677
  });
35678
+ SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
35589
35679
  /**
35590
35680
  * Accessory device helpers — shared across drivers.
35591
35681
  *
@@ -40564,6 +40654,12 @@ Object.freeze({
40564
40654
  addonId: null,
40565
40655
  access: "view"
40566
40656
  },
40657
+ "notificationRules.getAlarmSensors": {
40658
+ capName: "notification-rules",
40659
+ capScope: "system",
40660
+ addonId: null,
40661
+ access: "view"
40662
+ },
40567
40663
  "notificationRules.getClipRetentionAsks": {
40568
40664
  capName: "notification-rules",
40569
40665
  capScope: "system",
@@ -44142,6 +44238,11 @@ Object.freeze({
44142
44238
  }
44143
44239
  });
44144
44240
  Object.freeze({
44241
+ "accessories.getStatus": [{
44242
+ name: "deviceId",
44243
+ form: "single",
44244
+ optional: false
44245
+ }],
44145
44246
  "accessories.setChildHidden": [{
44146
44247
  name: "childDeviceId",
44147
44248
  form: "single",
@@ -44161,6 +44262,11 @@ Object.freeze({
44161
44262
  form: "single",
44162
44263
  optional: false
44163
44264
  }],
44265
+ "airQualitySensor.getStatus": [{
44266
+ name: "deviceId",
44267
+ form: "single",
44268
+ optional: false
44269
+ }],
44164
44270
  "alarmPanel.arm": [{
44165
44271
  name: "deviceId",
44166
44272
  form: "single",
@@ -44171,11 +44277,36 @@ Object.freeze({
44171
44277
  form: "single",
44172
44278
  optional: false
44173
44279
  }],
44280
+ "alarmPanel.getStatus": [{
44281
+ name: "deviceId",
44282
+ form: "single",
44283
+ optional: false
44284
+ }],
44174
44285
  "alarmPanel.trigger": [{
44175
44286
  name: "deviceId",
44176
44287
  form: "single",
44177
44288
  optional: false
44178
44289
  }],
44290
+ "ambientLightSensor.getStatus": [{
44291
+ name: "deviceId",
44292
+ form: "single",
44293
+ optional: false
44294
+ }],
44295
+ "audioAnalysis.applyDeviceSettingsPatch": [{
44296
+ name: "deviceId",
44297
+ form: "single",
44298
+ optional: false
44299
+ }],
44300
+ "audioAnalysis.getDeviceLiveContribution": [{
44301
+ name: "deviceId",
44302
+ form: "single",
44303
+ optional: false
44304
+ }],
44305
+ "audioAnalysis.getDeviceSettingsContribution": [{
44306
+ name: "deviceId",
44307
+ form: "single",
44308
+ optional: false
44309
+ }],
44179
44310
  "audioAnalysis.resolveDeviceSettings": [{
44180
44311
  name: "deviceId",
44181
44312
  form: "single",
@@ -44216,16 +44347,36 @@ Object.freeze({
44216
44347
  form: "single",
44217
44348
  optional: false
44218
44349
  }],
44350
+ "automationControl.getStatus": [{
44351
+ name: "deviceId",
44352
+ form: "single",
44353
+ optional: false
44354
+ }],
44219
44355
  "automationControl.trigger": [{
44220
44356
  name: "deviceId",
44221
44357
  form: "single",
44222
44358
  optional: false
44223
44359
  }],
44360
+ "battery.getStatus": [{
44361
+ name: "deviceId",
44362
+ form: "single",
44363
+ optional: false
44364
+ }],
44224
44365
  "battery.wakeForStream": [{
44225
44366
  name: "deviceId",
44226
44367
  form: "single",
44227
44368
  optional: false
44228
44369
  }],
44370
+ "binary.getStatus": [{
44371
+ name: "deviceId",
44372
+ form: "single",
44373
+ optional: false
44374
+ }],
44375
+ "brightness.getStatus": [{
44376
+ name: "deviceId",
44377
+ form: "single",
44378
+ optional: false
44379
+ }],
44229
44380
  "brightness.setBrightness": [{
44230
44381
  name: "deviceId",
44231
44382
  form: "single",
@@ -44241,6 +44392,26 @@ Object.freeze({
44241
44392
  form: "single",
44242
44393
  optional: false
44243
44394
  }],
44395
+ "cameraCredentials.getStatus": [{
44396
+ name: "deviceId",
44397
+ form: "single",
44398
+ optional: false
44399
+ }],
44400
+ "cameraGridLayout.applyDeviceSettingsPatch": [{
44401
+ name: "deviceId",
44402
+ form: "single",
44403
+ optional: false
44404
+ }],
44405
+ "cameraGridLayout.getDeviceLiveContribution": [{
44406
+ name: "deviceId",
44407
+ form: "single",
44408
+ optional: false
44409
+ }],
44410
+ "cameraGridLayout.getDeviceSettingsContribution": [{
44411
+ name: "deviceId",
44412
+ form: "single",
44413
+ optional: false
44414
+ }],
44244
44415
  "cameraGridLayout.getLayout": [{
44245
44416
  name: "deviceId",
44246
44417
  form: "single",
@@ -44256,6 +44427,21 @@ Object.freeze({
44256
44427
  form: "single",
44257
44428
  optional: false
44258
44429
  }],
44430
+ "cameraPipelineConfig.applyDeviceSettingsPatch": [{
44431
+ name: "deviceId",
44432
+ form: "single",
44433
+ optional: false
44434
+ }],
44435
+ "cameraPipelineConfig.getDeviceLiveContribution": [{
44436
+ name: "deviceId",
44437
+ form: "single",
44438
+ optional: false
44439
+ }],
44440
+ "cameraPipelineConfig.getDeviceSettingsContribution": [{
44441
+ name: "deviceId",
44442
+ form: "single",
44443
+ optional: false
44444
+ }],
44259
44445
  "cameraStreams.getBrokerStreams": [{
44260
44446
  name: "deviceId",
44261
44447
  form: "single",
@@ -44281,6 +44467,16 @@ Object.freeze({
44281
44467
  form: "single",
44282
44468
  optional: false
44283
44469
  }],
44470
+ "carbonMonoxide.getStatus": [{
44471
+ name: "deviceId",
44472
+ form: "single",
44473
+ optional: false
44474
+ }],
44475
+ "climateControl.getStatus": [{
44476
+ name: "deviceId",
44477
+ form: "single",
44478
+ optional: false
44479
+ }],
44284
44480
  "climateControl.setFanMode": [{
44285
44481
  name: "deviceId",
44286
44482
  form: "single",
@@ -44321,16 +44517,41 @@ Object.freeze({
44321
44517
  form: "single",
44322
44518
  optional: false
44323
44519
  }],
44520
+ "color.getStatus": [{
44521
+ name: "deviceId",
44522
+ form: "single",
44523
+ optional: false
44524
+ }],
44324
44525
  "color.setColor": [{
44325
44526
  name: "deviceId",
44326
44527
  form: "single",
44327
44528
  optional: false
44328
44529
  }],
44530
+ "connectivity.getStatus": [{
44531
+ name: "deviceId",
44532
+ form: "single",
44533
+ optional: false
44534
+ }],
44535
+ "consumables.getStatus": [{
44536
+ name: "deviceId",
44537
+ form: "single",
44538
+ optional: false
44539
+ }],
44329
44540
  "consumables.reset": [{
44330
44541
  name: "deviceId",
44331
44542
  form: "single",
44332
44543
  optional: false
44333
44544
  }],
44545
+ "contact.getStatus": [{
44546
+ name: "deviceId",
44547
+ form: "single",
44548
+ optional: false
44549
+ }],
44550
+ "control.getStatus": [{
44551
+ name: "deviceId",
44552
+ form: "single",
44553
+ optional: false
44554
+ }],
44334
44555
  "control.setValue": [{
44335
44556
  name: "deviceId",
44336
44557
  form: "single",
@@ -44341,6 +44562,11 @@ Object.freeze({
44341
44562
  form: "single",
44342
44563
  optional: false
44343
44564
  }],
44565
+ "cover.getStatus": [{
44566
+ name: "deviceId",
44567
+ form: "single",
44568
+ optional: false
44569
+ }],
44344
44570
  "cover.open": [{
44345
44571
  name: "deviceId",
44346
44572
  form: "single",
@@ -44366,6 +44592,11 @@ Object.freeze({
44366
44592
  form: "single",
44367
44593
  optional: false
44368
44594
  }],
44595
+ "dayNight.getStatus": [{
44596
+ name: "deviceId",
44597
+ form: "single",
44598
+ optional: false
44599
+ }],
44369
44600
  "dayNight.setSettings": [{
44370
44601
  name: "deviceId",
44371
44602
  form: "single",
@@ -44376,11 +44607,31 @@ Object.freeze({
44376
44607
  form: "single",
44377
44608
  optional: true
44378
44609
  }],
44610
+ "detectionPipeline.applyDeviceSettingsPatch": [{
44611
+ name: "deviceId",
44612
+ form: "single",
44613
+ optional: false
44614
+ }],
44615
+ "detectionPipeline.getDeviceLiveContribution": [{
44616
+ name: "deviceId",
44617
+ form: "single",
44618
+ optional: false
44619
+ }],
44620
+ "detectionPipeline.getDeviceSettingsContribution": [{
44621
+ name: "deviceId",
44622
+ form: "single",
44623
+ optional: false
44624
+ }],
44379
44625
  "deviceAdminLink.getAdminLink": [{
44380
44626
  name: "deviceId",
44381
44627
  form: "single",
44382
44628
  optional: false
44383
44629
  }],
44630
+ "deviceAdoption.getStatus": [{
44631
+ name: "deviceId",
44632
+ form: "single",
44633
+ optional: false
44634
+ }],
44384
44635
  "deviceAdoption.release": [{
44385
44636
  name: "camDeviceId",
44386
44637
  form: "single",
@@ -44396,6 +44647,11 @@ Object.freeze({
44396
44647
  form: "single",
44397
44648
  optional: false
44398
44649
  }],
44650
+ "deviceDiscovery.getStatus": [{
44651
+ name: "deviceId",
44652
+ form: "single",
44653
+ optional: false
44654
+ }],
44399
44655
  "deviceDiscovery.listDiscovered": [{
44400
44656
  name: "deviceId",
44401
44657
  form: "single",
@@ -44415,6 +44671,21 @@ Object.freeze({
44415
44671
  form: "single",
44416
44672
  optional: false
44417
44673
  }],
44674
+ "deviceExport.applyDeviceSettingsPatch": [{
44675
+ name: "deviceId",
44676
+ form: "single",
44677
+ optional: false
44678
+ }],
44679
+ "deviceExport.getDeviceLiveContribution": [{
44680
+ name: "deviceId",
44681
+ form: "single",
44682
+ optional: false
44683
+ }],
44684
+ "deviceExport.getDeviceSettingsContribution": [{
44685
+ name: "deviceId",
44686
+ form: "single",
44687
+ optional: false
44688
+ }],
44418
44689
  "deviceExtension.listCandidates": [{
44419
44690
  name: "deviceId",
44420
44691
  form: "single",
@@ -44763,6 +45034,26 @@ Object.freeze({
44763
45034
  form: "single",
44764
45035
  optional: false
44765
45036
  }],
45037
+ "deviceStatus.getStatus": [{
45038
+ name: "deviceId",
45039
+ form: "single",
45040
+ optional: false
45041
+ }],
45042
+ "doorbell.getStatus": [{
45043
+ name: "deviceId",
45044
+ form: "single",
45045
+ optional: false
45046
+ }],
45047
+ "enumSensor.getStatus": [{
45048
+ name: "deviceId",
45049
+ form: "single",
45050
+ optional: false
45051
+ }],
45052
+ "eventEmitter.getStatus": [{
45053
+ name: "deviceId",
45054
+ form: "single",
45055
+ optional: false
45056
+ }],
44766
45057
  "faceGallery.getFaceByTrack": [{
44767
45058
  name: "deviceId",
44768
45059
  form: "single",
@@ -44777,6 +45068,11 @@ Object.freeze({
44777
45068
  form: "array",
44778
45069
  optional: true
44779
45070
  }],
45071
+ "fanControl.getStatus": [{
45072
+ name: "deviceId",
45073
+ form: "single",
45074
+ optional: false
45075
+ }],
44780
45076
  "fanControl.setDirection": [{
44781
45077
  name: "deviceId",
44782
45078
  form: "single",
@@ -44797,6 +45093,26 @@ Object.freeze({
44797
45093
  form: "single",
44798
45094
  optional: false
44799
45095
  }],
45096
+ "featureProbe.getStatus": [{
45097
+ name: "deviceId",
45098
+ form: "single",
45099
+ optional: false
45100
+ }],
45101
+ "flood.getStatus": [{
45102
+ name: "deviceId",
45103
+ form: "single",
45104
+ optional: false
45105
+ }],
45106
+ "gas.getStatus": [{
45107
+ name: "deviceId",
45108
+ form: "single",
45109
+ optional: false
45110
+ }],
45111
+ "humidifier.getStatus": [{
45112
+ name: "deviceId",
45113
+ form: "single",
45114
+ optional: false
45115
+ }],
44800
45116
  "humidifier.setMode": [{
44801
45117
  name: "deviceId",
44802
45118
  form: "single",
@@ -44812,11 +45128,26 @@ Object.freeze({
44812
45128
  form: "single",
44813
45129
  optional: false
44814
45130
  }],
45131
+ "humiditySensor.getStatus": [{
45132
+ name: "deviceId",
45133
+ form: "single",
45134
+ optional: false
45135
+ }],
45136
+ "image.getStatus": [{
45137
+ name: "deviceId",
45138
+ form: "single",
45139
+ optional: false
45140
+ }],
44815
45141
  "imageSettings.getOptions": [{
44816
45142
  name: "deviceId",
44817
45143
  form: "single",
44818
45144
  optional: false
44819
45145
  }],
45146
+ "imageSettings.getStatus": [{
45147
+ name: "deviceId",
45148
+ form: "single",
45149
+ optional: false
45150
+ }],
44820
45151
  "imageSettings.setSettings": [{
44821
45152
  name: "deviceId",
44822
45153
  form: "single",
@@ -44827,6 +45158,11 @@ Object.freeze({
44827
45158
  form: "single",
44828
45159
  optional: false
44829
45160
  }],
45161
+ "intercom.getStatus": [{
45162
+ name: "deviceId",
45163
+ form: "single",
45164
+ optional: false
45165
+ }],
44830
45166
  "intercom.handleAnswer": [{
44831
45167
  name: "deviceId",
44832
45168
  form: "single",
@@ -44857,6 +45193,11 @@ Object.freeze({
44857
45193
  form: "single",
44858
45194
  optional: false
44859
45195
  }],
45196
+ "lawnMowerControl.getStatus": [{
45197
+ name: "deviceId",
45198
+ form: "single",
45199
+ optional: false
45200
+ }],
44860
45201
  "lawnMowerControl.pause": [{
44861
45202
  name: "deviceId",
44862
45203
  form: "single",
@@ -44867,6 +45208,11 @@ Object.freeze({
44867
45208
  form: "single",
44868
45209
  optional: false
44869
45210
  }],
45211
+ "lockControl.getStatus": [{
45212
+ name: "deviceId",
45213
+ form: "single",
45214
+ optional: false
45215
+ }],
44870
45216
  "lockControl.lock": [{
44871
45217
  name: "deviceId",
44872
45218
  form: "single",
@@ -44882,6 +45228,11 @@ Object.freeze({
44882
45228
  form: "single",
44883
45229
  optional: false
44884
45230
  }],
45231
+ "mediaPlayer.getStatus": [{
45232
+ name: "deviceId",
45233
+ form: "single",
45234
+ optional: false
45235
+ }],
44885
45236
  "mediaPlayer.next": [{
44886
45237
  name: "deviceId",
44887
45238
  form: "single",
@@ -44942,6 +45293,11 @@ Object.freeze({
44942
45293
  form: "single",
44943
45294
  optional: false
44944
45295
  }],
45296
+ "motion.getStatus": [{
45297
+ name: "deviceId",
45298
+ form: "single",
45299
+ optional: false
45300
+ }],
44945
45301
  "motion.isDetected": [{
44946
45302
  name: "deviceId",
44947
45303
  form: "single",
@@ -44952,11 +45308,31 @@ Object.freeze({
44952
45308
  form: "single",
44953
45309
  optional: false
44954
45310
  }],
45311
+ "motionDetection.applyDeviceSettingsPatch": [{
45312
+ name: "deviceId",
45313
+ form: "single",
45314
+ optional: false
45315
+ }],
45316
+ "motionDetection.getDeviceLiveContribution": [{
45317
+ name: "deviceId",
45318
+ form: "single",
45319
+ optional: false
45320
+ }],
45321
+ "motionDetection.getDeviceSettingsContribution": [{
45322
+ name: "deviceId",
45323
+ form: "single",
45324
+ optional: false
45325
+ }],
44955
45326
  "motionDetection.removeCamera": [{
44956
45327
  name: "deviceId",
44957
45328
  form: "single",
44958
45329
  optional: false
44959
45330
  }],
45331
+ "motionTrigger.getStatus": [{
45332
+ name: "deviceId",
45333
+ form: "single",
45334
+ optional: false
45335
+ }],
44960
45336
  "motionTrigger.setMotionTrigger": [{
44961
45337
  name: "deviceId",
44962
45338
  form: "single",
@@ -44967,6 +45343,11 @@ Object.freeze({
44967
45343
  form: "single",
44968
45344
  optional: false
44969
45345
  }],
45346
+ "motionZones.getStatus": [{
45347
+ name: "deviceId",
45348
+ form: "single",
45349
+ optional: false
45350
+ }],
44970
45351
  "motionZones.setZone": [{
44971
45352
  name: "deviceId",
44972
45353
  form: "single",
@@ -44977,6 +45358,11 @@ Object.freeze({
44977
45358
  form: "single",
44978
45359
  optional: false
44979
45360
  }],
45361
+ "nativeObjectDetection.getStatus": [{
45362
+ name: "deviceId",
45363
+ form: "single",
45364
+ optional: false
45365
+ }],
44980
45366
  "nativeObjectDetection.setEnabled": [{
44981
45367
  name: "deviceId",
44982
45368
  form: "single",
@@ -44992,6 +45378,11 @@ Object.freeze({
44992
45378
  form: "single",
44993
45379
  optional: false
44994
45380
  }],
45381
+ "navigation.getStatus": [{
45382
+ name: "deviceId",
45383
+ form: "single",
45384
+ optional: false
45385
+ }],
44995
45386
  "navigation.goToPoint": [{
44996
45387
  name: "deviceId",
44997
45388
  form: "single",
@@ -45037,6 +45428,11 @@ Object.freeze({
45037
45428
  form: "single",
45038
45429
  optional: false
45039
45430
  }],
45431
+ "networkLink.getStatus": [{
45432
+ name: "deviceId",
45433
+ form: "single",
45434
+ optional: false
45435
+ }],
45040
45436
  "networkQuality.getDeviceStats": [{
45041
45437
  name: "deviceId",
45042
45438
  form: "single",
@@ -45047,6 +45443,11 @@ Object.freeze({
45047
45443
  form: "single",
45048
45444
  optional: false
45049
45445
  }],
45446
+ "notificationRules.getAlarmSensors": [{
45447
+ name: "deviceIds",
45448
+ form: "array",
45449
+ optional: false
45450
+ }],
45050
45451
  "notificationRules.setDeviceMuted": [{
45051
45452
  name: "deviceId",
45052
45453
  form: "single",
@@ -45057,11 +45458,26 @@ Object.freeze({
45057
45458
  form: "single",
45058
45459
  optional: false
45059
45460
  }],
45461
+ "notifier.getStatus": [{
45462
+ name: "deviceId",
45463
+ form: "single",
45464
+ optional: false
45465
+ }],
45060
45466
  "notifier.send": [{
45061
45467
  name: "deviceId",
45062
45468
  form: "single",
45063
45469
  optional: false
45064
45470
  }],
45471
+ "numericSensor.getStatus": [{
45472
+ name: "deviceId",
45473
+ form: "single",
45474
+ optional: false
45475
+ }],
45476
+ "osd.getStatus": [{
45477
+ name: "deviceId",
45478
+ form: "single",
45479
+ optional: false
45480
+ }],
45065
45481
  "osd.setOverlay": [{
45066
45482
  name: "deviceId",
45067
45483
  form: "single",
@@ -45121,6 +45537,11 @@ Object.freeze({
45121
45537
  form: "single",
45122
45538
  optional: false
45123
45539
  }],
45540
+ "petFeeder.getStatus": [{
45541
+ name: "deviceId",
45542
+ form: "single",
45543
+ optional: false
45544
+ }],
45124
45545
  "petFeeder.markFoodReplenished": [{
45125
45546
  name: "deviceId",
45126
45547
  form: "single",
@@ -45156,6 +45577,11 @@ Object.freeze({
45156
45577
  form: "single",
45157
45578
  optional: false
45158
45579
  }],
45580
+ "pipelineAnalytics.applyDeviceSettingsPatch": [{
45581
+ name: "deviceId",
45582
+ form: "single",
45583
+ optional: false
45584
+ }],
45159
45585
  "pipelineAnalytics.clearTrackRelation": [{
45160
45586
  name: "deviceId",
45161
45587
  form: "single",
@@ -45196,6 +45622,16 @@ Object.freeze({
45196
45622
  form: "single",
45197
45623
  optional: false
45198
45624
  }],
45625
+ "pipelineAnalytics.getDeviceLiveContribution": [{
45626
+ name: "deviceId",
45627
+ form: "single",
45628
+ optional: false
45629
+ }],
45630
+ "pipelineAnalytics.getDeviceSettingsContribution": [{
45631
+ name: "deviceId",
45632
+ form: "single",
45633
+ optional: false
45634
+ }],
45199
45635
  "pipelineAnalytics.getEventDensity": [{
45200
45636
  name: "deviceId",
45201
45637
  form: "single",
@@ -45411,6 +45847,11 @@ Object.freeze({
45411
45847
  form: "single",
45412
45848
  optional: true
45413
45849
  }],
45850
+ "pipelineOrchestrator.applyDeviceSettingsPatch": [{
45851
+ name: "deviceId",
45852
+ form: "single",
45853
+ optional: false
45854
+ }],
45414
45855
  "pipelineOrchestrator.assignAudio": [{
45415
45856
  name: "deviceId",
45416
45857
  form: "single",
@@ -45456,6 +45897,16 @@ Object.freeze({
45456
45897
  form: "single",
45457
45898
  optional: false
45458
45899
  }],
45900
+ "pipelineOrchestrator.getDeviceLiveContribution": [{
45901
+ name: "deviceId",
45902
+ form: "single",
45903
+ optional: false
45904
+ }],
45905
+ "pipelineOrchestrator.getDeviceSettingsContribution": [{
45906
+ name: "deviceId",
45907
+ form: "single",
45908
+ optional: false
45909
+ }],
45459
45910
  "pipelineOrchestrator.getPipelineAssignment": [{
45460
45911
  name: "deviceId",
45461
45912
  form: "single",
@@ -45576,11 +46027,31 @@ Object.freeze({
45576
46027
  form: "single",
45577
46028
  optional: true
45578
46029
  }],
46030
+ "powerMeter.getStatus": [{
46031
+ name: "deviceId",
46032
+ form: "single",
46033
+ optional: false
46034
+ }],
46035
+ "presence.getStatus": [{
46036
+ name: "deviceId",
46037
+ form: "single",
46038
+ optional: false
46039
+ }],
46040
+ "pressureSensor.getStatus": [{
46041
+ name: "deviceId",
46042
+ form: "single",
46043
+ optional: false
46044
+ }],
45579
46045
  "privacyMask.getOptions": [{
45580
46046
  name: "deviceId",
45581
46047
  form: "single",
45582
46048
  optional: false
45583
46049
  }],
46050
+ "privacyMask.getStatus": [{
46051
+ name: "deviceId",
46052
+ form: "single",
46053
+ optional: false
46054
+ }],
45584
46055
  "privacyMask.setAudioEnabled": [{
45585
46056
  name: "deviceId",
45586
46057
  form: "single",
@@ -45631,6 +46102,11 @@ Object.freeze({
45631
46102
  form: "single",
45632
46103
  optional: false
45633
46104
  }],
46105
+ "ptz.getStatus": [{
46106
+ name: "deviceId",
46107
+ form: "single",
46108
+ optional: false
46109
+ }],
45634
46110
  "ptz.goHome": [{
45635
46111
  name: "deviceId",
45636
46112
  form: "single",
@@ -45721,6 +46197,11 @@ Object.freeze({
45721
46197
  form: "single",
45722
46198
  optional: false
45723
46199
  }],
46200
+ "recordingArchive.applyDeviceSettingsPatch": [{
46201
+ name: "deviceId",
46202
+ form: "single",
46203
+ optional: false
46204
+ }],
45724
46205
  "recordingArchive.deleteFootprint": [{
45725
46206
  name: "deviceId",
45726
46207
  form: "single",
@@ -45741,6 +46222,21 @@ Object.freeze({
45741
46222
  form: "single",
45742
46223
  optional: false
45743
46224
  }],
46225
+ "recordingArchive.getDeviceLiveContribution": [{
46226
+ name: "deviceId",
46227
+ form: "single",
46228
+ optional: false
46229
+ }],
46230
+ "recordingArchive.getDeviceSettingsContribution": [{
46231
+ name: "deviceId",
46232
+ form: "single",
46233
+ optional: false
46234
+ }],
46235
+ "recordingArchive.getStatus": [{
46236
+ name: "deviceId",
46237
+ form: "single",
46238
+ optional: false
46239
+ }],
45744
46240
  "recordingArchive.listOpsLog": [{
45745
46241
  name: "deviceId",
45746
46242
  form: "single",
@@ -45826,6 +46322,11 @@ Object.freeze({
45826
46322
  form: "single",
45827
46323
  optional: false
45828
46324
  }],
46325
+ "recordingOnboard.getStatus": [{
46326
+ name: "deviceId",
46327
+ form: "single",
46328
+ optional: false
46329
+ }],
45829
46330
  "recordingOnboard.setSettings": [{
45830
46331
  name: "deviceId",
45831
46332
  form: "single",
@@ -45856,6 +46357,11 @@ Object.freeze({
45856
46357
  form: "single",
45857
46358
  optional: false
45858
46359
  }],
46360
+ "sceneMonitor.getStatus": [{
46361
+ name: "deviceId",
46362
+ form: "single",
46363
+ optional: false
46364
+ }],
45859
46365
  "sceneMonitor.listScenes": [{
45860
46366
  name: "deviceId",
45861
46367
  form: "single",
@@ -45881,6 +46387,11 @@ Object.freeze({
45881
46387
  form: "single",
45882
46388
  optional: false
45883
46389
  }],
46390
+ "scriptRunner.getStatus": [{
46391
+ name: "deviceId",
46392
+ form: "single",
46393
+ optional: false
46394
+ }],
45884
46395
  "scriptRunner.run": [{
45885
46396
  name: "deviceId",
45886
46397
  form: "single",
@@ -45891,11 +46402,31 @@ Object.freeze({
45891
46402
  form: "single",
45892
46403
  optional: false
45893
46404
  }],
46405
+ "smoke.getStatus": [{
46406
+ name: "deviceId",
46407
+ form: "single",
46408
+ optional: false
46409
+ }],
46410
+ "snapshot.applyDeviceSettingsPatch": [{
46411
+ name: "deviceId",
46412
+ form: "single",
46413
+ optional: false
46414
+ }],
45894
46415
  "snapshot.getDebugState": [{
45895
46416
  name: "deviceId",
45896
46417
  form: "single",
45897
46418
  optional: false
45898
46419
  }],
46420
+ "snapshot.getDeviceLiveContribution": [{
46421
+ name: "deviceId",
46422
+ form: "single",
46423
+ optional: false
46424
+ }],
46425
+ "snapshot.getDeviceSettingsContribution": [{
46426
+ name: "deviceId",
46427
+ form: "single",
46428
+ optional: false
46429
+ }],
45899
46430
  "snapshot.getSnapshot": [{
45900
46431
  name: "deviceId",
45901
46432
  form: "single",
@@ -45912,6 +46443,11 @@ Object.freeze({
45912
46443
  form: "array",
45913
46444
  optional: false
45914
46445
  }],
46446
+ "snapshot.getStatus": [{
46447
+ name: "deviceId",
46448
+ form: "single",
46449
+ optional: false
46450
+ }],
45915
46451
  "snapshot.invalidateCache": [{
45916
46452
  name: "deviceId",
45917
46453
  form: "single",
@@ -45922,6 +46458,11 @@ Object.freeze({
45922
46458
  form: "single",
45923
46459
  optional: false
45924
46460
  }],
46461
+ "streamBroker.applyDeviceSettingsPatch": [{
46462
+ name: "deviceId",
46463
+ form: "single",
46464
+ optional: false
46465
+ }],
45925
46466
  "streamBroker.assignProfile": [{
45926
46467
  name: "deviceId",
45927
46468
  form: "single",
@@ -45937,6 +46478,16 @@ Object.freeze({
45937
46478
  form: "single",
45938
46479
  optional: false
45939
46480
  }],
46481
+ "streamBroker.getDeviceLiveContribution": [{
46482
+ name: "deviceId",
46483
+ form: "single",
46484
+ optional: false
46485
+ }],
46486
+ "streamBroker.getDeviceSettingsContribution": [{
46487
+ name: "deviceId",
46488
+ form: "single",
46489
+ optional: false
46490
+ }],
45940
46491
  "streamBroker.getStreamWithCodec": [{
45941
46492
  name: "deviceId",
45942
46493
  form: "single",
@@ -45997,16 +46548,46 @@ Object.freeze({
45997
46548
  form: "single",
45998
46549
  optional: false
45999
46550
  }],
46551
+ "streamParams.getStatus": [{
46552
+ name: "deviceId",
46553
+ form: "single",
46554
+ optional: false
46555
+ }],
46000
46556
  "streamParams.setProfile": [{
46001
46557
  name: "deviceId",
46002
46558
  form: "single",
46003
46559
  optional: false
46004
46560
  }],
46561
+ "switch.getStatus": [{
46562
+ name: "deviceId",
46563
+ form: "single",
46564
+ optional: false
46565
+ }],
46005
46566
  "switch.setState": [{
46006
46567
  name: "deviceId",
46007
46568
  form: "single",
46008
46569
  optional: false
46009
46570
  }],
46571
+ "tamper.getStatus": [{
46572
+ name: "deviceId",
46573
+ form: "single",
46574
+ optional: false
46575
+ }],
46576
+ "temperatureSensor.getStatus": [{
46577
+ name: "deviceId",
46578
+ form: "single",
46579
+ optional: false
46580
+ }],
46581
+ "update.getStatus": [{
46582
+ name: "deviceId",
46583
+ form: "single",
46584
+ optional: false
46585
+ }],
46586
+ "vacuumControl.getStatus": [{
46587
+ name: "deviceId",
46588
+ form: "single",
46589
+ optional: false
46590
+ }],
46010
46591
  "vacuumControl.locate": [{
46011
46592
  name: "deviceId",
46012
46593
  form: "single",
@@ -46042,6 +46623,11 @@ Object.freeze({
46042
46623
  form: "single",
46043
46624
  optional: false
46044
46625
  }],
46626
+ "valve.getStatus": [{
46627
+ name: "deviceId",
46628
+ form: "single",
46629
+ optional: false
46630
+ }],
46045
46631
  "valve.open": [{
46046
46632
  name: "deviceId",
46047
46633
  form: "single",
@@ -46057,6 +46643,11 @@ Object.freeze({
46057
46643
  form: "single",
46058
46644
  optional: false
46059
46645
  }],
46646
+ "vibration.getStatus": [{
46647
+ name: "deviceId",
46648
+ form: "single",
46649
+ optional: false
46650
+ }],
46060
46651
  "videoclips.dialClipStream": [{
46061
46652
  name: "deviceId",
46062
46653
  form: "single",
@@ -46092,6 +46683,11 @@ Object.freeze({
46092
46683
  form: "single",
46093
46684
  optional: false
46094
46685
  }],
46686
+ "waterHeater.getStatus": [{
46687
+ name: "deviceId",
46688
+ form: "single",
46689
+ optional: false
46690
+ }],
46095
46691
  "waterHeater.setAway": [{
46096
46692
  name: "deviceId",
46097
46693
  form: "single",
@@ -46107,6 +46703,11 @@ Object.freeze({
46107
46703
  form: "single",
46108
46704
  optional: false
46109
46705
  }],
46706
+ "weather.getStatus": [{
46707
+ name: "deviceId",
46708
+ form: "single",
46709
+ optional: false
46710
+ }],
46110
46711
  "webrtcSession.addIceCandidate": [{
46111
46712
  name: "deviceId",
46112
46713
  form: "single",
@@ -46844,7 +47445,7 @@ var PreviewTextField = string().max(200);
46844
47445
  * Whether the notification's preview is a STILL or a short animation.
46845
47446
  *
46846
47447
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
46847
- * rule)"* — the other rules already offer an animated preview, and a ten-hour
47448
+ * rule)"* [sent as a gif or video (as for the other rules)] — the other rules already offer an animated preview, and a ten-hour
46848
47449
  * night reads better as three seconds of motion than as one frame of it. Both
46849
47450
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
46850
47451
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -46982,6 +47583,7 @@ TimelapseRuleInputSchema.extend({
46982
47583
  createdAt: number(),
46983
47584
  updatedAt: number()
46984
47585
  });
47586
+ MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
46985
47587
  object({
46986
47588
  /**
46987
47589
  * Fraction of the box's own size added on EACH side before cutting.