@camstack/addon-provider-onvif 1.2.117 → 1.2.118

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 +841 -238
  2. package/dist/addon.mjs +841 -238
  3. package/package.json +1 -3
package/dist/addon.mjs CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
3
3
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
4
4
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  //#endregion
6
- //#region ../types/dist/event-category-C5xZWqz6.mjs
6
+ //#region ../types/dist/event-category-BGmz_oxW.mjs
7
7
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
8
8
  EventCategory["SystemBoot"] = "system.boot";
9
9
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -536,6 +536,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
536
536
  */
537
537
  EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
538
538
  EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
539
+ /**
540
+ * A camera provider's RAW onboard motion push (D673): the `MotionOnMotionChanged`
541
+ * data shape without `triggeredBy`, built by `providerMotionEvent`. Its one
542
+ * consumer is the orchestrator's camera-sensors bridge, which re-emits the
543
+ * camera's EFFECTIVE onboard motion as `MotionOnMotionChanged` (a pass-through
544
+ * for a camera with no attached sensor). Providers must not emit
545
+ * `MotionOnMotionChanged` themselves; `scripts/check-onboard-motion-emitters.ts`
546
+ * keeps it so (camera-grid is a named, deferred exception).
547
+ */
539
548
  EventCategory["ProviderMotion"] = "provider.motion";
540
549
  EventCategory["ProviderDetection"] = "provider.detection";
541
550
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
@@ -5374,7 +5383,7 @@ var ZodIssueCode = {
5374
5383
  var ZodFirstPartyTypeKind;
5375
5384
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5376
5385
  //#endregion
5377
- //#region ../types/dist/sleep-gU6hqv4_.mjs
5386
+ //#region ../types/dist/sleep-CDCONfkt.mjs
5378
5387
  /**
5379
5388
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5380
5389
  * window to float samples (D455).
@@ -7413,7 +7422,7 @@ function errMsg(err) {
7413
7422
  return String(err);
7414
7423
  }
7415
7424
  //#endregion
7416
- //#region ../types/dist/composition-Dq42XWp0.mjs
7425
+ //#region ../types/dist/composition-Bzr7GwgP.mjs
7417
7426
  /**
7418
7427
  * The ffmpeg build every CamStack node downloads, per platform and arch.
7419
7428
  *
@@ -7467,21 +7476,20 @@ function portable(target) {
7467
7476
  }
7468
7477
  portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7469
7478
  /**
7470
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7471
- * valutatore puro del suo FRONTE.
7479
+ * THE table "which boolean of this device type counts as HIGH", and the
7480
+ * pure evaluator of its EDGE.
7472
7481
  *
7473
- * Viveva dentro il builtin virtual-doorbell
7474
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
7475
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
7476
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
7477
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
7478
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
7479
- * SPOSTA qui e il doorbell la ri-esporta.
7482
+ * ONE table for everyone who asks "is this device HIGH?": the
7483
+ * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
7484
+ * sensor markers, the orchestrator's camera-sensors bridge and
7485
+ * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
7486
+ * A second copy would diverge at the first cap added, and the symptom — "the
7487
+ * sensor rings the doorbell but does not record" — is exactly D62.
7480
7488
  *
7481
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
7482
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
7483
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
7484
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
7489
+ * ⚠ NOT `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), and the
7490
+ * two must not be unified: that one answers "what is the state WORD for a
7491
+ * rule" (and includes `presence`, `cover`, `alarm-panel`), this one "which
7492
+ * boolean's EDGE counts". Deliberately different vocabularies.
7485
7493
  */
7486
7494
  /**
7487
7495
  * Known binary / switch source caps → the boolean slice field whose
@@ -7502,6 +7510,8 @@ var SOURCE_CAP_ACTIVE_FIELD = {
7502
7510
  vibration: "detected",
7503
7511
  tamper: "tampered"
7504
7512
  };
7513
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
7514
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
7505
7515
  /**
7506
7516
  * Error types for the safe expression engine. Two distinct classes so callers
7507
7517
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -9088,22 +9098,22 @@ var CLASS_MAP_MACRO_TARGETS = [
9088
9098
  "package"
9089
9099
  ];
9090
9100
  /**
9091
- * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
9092
- * un operatore può selezionare.
9101
+ * The FIRST-LEVEL macro classes: those an object detector emits and that
9102
+ * an operator can select.
9093
9103
  *
9094
- * Sono le tre offerte dallo step `object-detection`
9104
+ * They are the three offered by the `object-detection` step
9095
9105
  * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
9096
- * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
9097
- * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
9098
- * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
9099
- * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
9106
+ * `enabledMacroClasses`). `package` is in {@link CLASS_MAP_MACRO_TARGETS} and in
9107
+ * `MACRO_LABELS` — it is a true macro — but NOT here: it belongs to the step
9108
+ * `package-detection`, whose enablement is driven by zones, and offering the
9109
+ * same word twice has already made an operator turn on the COCO proxy
9100
9110
  * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
9101
9111
  *
9102
- * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
9103
- * dello step e una seconda volta come union `FirstLevelMacro`
9104
- * (`types/detection.ts`); una terza copia per il trigger di registrazione
9105
- * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
9106
- * successiva.
9112
+ * ONE list. Before today the same three were hand-written in the step's offer
9113
+ * and a second time as the union `FirstLevelMacro`
9114
+ * (`types/detection.ts`); a third copy for the recording trigger
9115
+ * (`RecordingTriggers.objectClasses`) would have made the later divergence
9116
+ * invisible.
9107
9117
  */
9108
9118
  var FIRST_LEVEL_MACRO_CLASSES = [
9109
9119
  "person",
@@ -9168,17 +9178,17 @@ var RecordingStorageModeSchema = _enum([
9168
9178
  "on-device-decision"
9169
9179
  ]);
9170
9180
  /**
9171
- * Le macro classi che possono aprire una finestra di registrazione — le stesse
9172
- * tre offerte dallo step `object-detection`, da UNA lista
9181
+ * The macro classes that can open a recording window — the same
9182
+ * three offered by the `object-detection` step, from ONE list
9173
9183
  * ({@link FIRST_LEVEL_MACRO_CLASSES}).
9174
9184
  */
9175
9185
  var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
9176
9186
  /**
9177
- * True quando `values` non ripete un elemento.
9187
+ * True when `values` does not repeat an element.
9178
9188
  *
9179
- * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
9180
- * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
9181
- * conterebbe due volte le sue finestre in `segmentMissedByMs`.
9189
+ * A duplicate is not harmless: every entry of `objectClasses` / `sensorDeviceIds`
9190
+ * becomes a SOURCE in `bandTriggerSources`, and the same source twice
9191
+ * would count its windows twice in `segmentMissedByMs`.
9182
9192
  */
9183
9193
  var noDuplicates = (values) => new Set(values).size === values.length;
9184
9194
  /** Which detectors trigger an `events`-mode band. */
@@ -9186,34 +9196,34 @@ var RecordingTriggersSchema = object({
9186
9196
  motion: boolean().optional(),
9187
9197
  audioThresholdDbfs: number().optional(),
9188
9198
  /**
9189
- * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
9190
- * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
9191
- * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
9192
- * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
9199
+ * The macro classes whose detection opens a window. ABSENT = the source
9200
+ * is not listened to; an EMPTY array is rejected, because "events band, object
9201
+ * trigger on, no class" is the same shape "enabled and records
9202
+ * nothing, forever" that `eventsBandCanEverDemand` is written against.
9193
9203
  *
9194
- * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
9195
- * quelle che hanno attraversato `enabledMacroClasses`, i
9196
- * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
9197
- * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
9198
- * finestre — vedi `recorder/object-trigger.ts`.
9204
+ * The signal read is ALREADY FILTERED: only `source: 'pipeline'` detections, i.e.
9205
+ * those that went through `enabledMacroClasses`, the
9206
+ * `minConfidence<Macro>` and the full-frame guard. The camera's onboard AI
9207
+ * (`source: 'onboard'`) goes through none of those gates and does NOT open
9208
+ * windows — see `recorder/object-trigger.ts`.
9199
9209
  */
9200
9210
  objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
9201
9211
  /**
9202
- * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
9203
- * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
9212
+ * The LINKED devices whose RISING EDGE opens a window. Absent = the source
9213
+ * is not listened to; an empty array is rejected for the same reason as
9204
9214
  * `objectClasses`.
9205
9215
  *
9206
- * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
9207
- * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
9208
- * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
9209
- * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9210
- * device (D12) — mai un elenco globale di cap.
9216
+ * These are SOURCE device ids, not cameras: the band names them, so the
9217
+ * hot path (`DeviceStateChanged`, at bus rate across the whole fleet)
9218
+ * does no RPC. The OFFER from which the operator picks them is another question, and
9219
+ * is resolved with `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9220
+ * device (D12) — never a global list of caps.
9211
9221
  *
9212
- * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
9213
- * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
9214
- * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
9215
- * il livello: un contatto trovato già aperto al riavvio del runner non fa
9216
- * registrare.
9222
+ * What "high" means depends on the device TYPE and is not decided here:
9223
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) is THE table,
9224
+ * the same one as the composite doorbell (D671). And it is the EDGE, not
9225
+ * the level: a contact found already open at runner restart does not
9226
+ * record.
9217
9227
  */
9218
9228
  sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
9219
9229
  });
@@ -10290,7 +10300,7 @@ var DecoderSessionConfigSchema = object({
10290
10300
  * - **VALUE** — at which level, for which scope, until when. That stays ONE
10291
10301
  * thing: the logging settings document on the `system` cap. Two authorities
10292
10302
  * over the values is the exact defect
10293
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
10303
+ * `docs/design/plans/2026-08-26-per-component-logging.md` was written to
10294
10304
  * remove; re-introducing it from the cure side would be grotesque.
10295
10305
  *
10296
10306
  * Nothing in this file reads a clock, an env var or a store. The registry is
@@ -12001,7 +12011,7 @@ var BrokerStatusEnum = _enum([
12001
12011
  "unreachable",
12002
12012
  "error"
12003
12013
  ]);
12004
- var BrokerInfoSchema$1 = object({
12014
+ var BrokerInfoSchema = object({
12005
12015
  /** Stable broker id. Persisted; survives addon restarts. */
12006
12016
  id: string(),
12007
12017
  /** Addon id of the provider that OWNS this broker.
@@ -12134,7 +12144,7 @@ var GetStateInputSchema = object({
12134
12144
  * HA: entity_id (returns the cached entity state). */
12135
12145
  key: string()
12136
12146
  });
12137
- method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, BrokerInfoSchema$1.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12147
+ method(ListInputSchema, array(BrokerInfoSchema)), method(GetInputSchema, BrokerInfoSchema.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12138
12148
  kind: "mutation",
12139
12149
  auth: "admin"
12140
12150
  }), method(RemoveInputSchema, _void(), {
@@ -13743,7 +13753,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13743
13753
  *
13744
13754
  * ⚠ An earlier version of this comment blamed Zod stripping, and that
13745
13755
  * was wrong — corrected 2026-08-26 after the hop map
13746
- * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
13756
+ * (`docs/design/2026-08-26-topic-hop-map.md`) traced the path.
13747
13757
  * There is **no Zod parse at all** between the door and the engine: the
13748
13758
  * dispatcher forwards the payload verbatim and UDS carries it whole. A
13749
13759
  * field declared here reaches `SqliteSettingsBackend` either way.
@@ -14612,7 +14622,17 @@ var SiteZoneSchema = object({
14612
14622
  var ProviderStatusSchema = object({
14613
14623
  connected: boolean(),
14614
14624
  deviceCount: number(),
14615
- error: string().optional()
14625
+ error: string().optional(),
14626
+ /**
14627
+ * `'orchestrator'`: this provider writes NO camera's `motion` runtime slice
14628
+ * — the orchestrator is its only writer (D696). Set by `BaseDeviceProvider`,
14629
+ * which every provider bundles, so only a provider BUILT from D696-amended
14630
+ * code says it. ABSENT means an older provider that may still write it (raw
14631
+ * VMD writes, a boot seed), and the orchestrator's provider gate keeps that
14632
+ * provider's cameras unwritten (`camera-sensors/motion-slice-provider-gate.ts`)
14633
+ * — the provider twin of the runner's `getLocalLoad().motionSliceWriter`.
14634
+ */
14635
+ motionSliceWriter: literal("orchestrator").optional()
14616
14636
  });
14617
14637
  object({
14618
14638
  externalId: string(),
@@ -15491,28 +15511,10 @@ method(object({
15491
15511
  deviceIds: array(number()).readonly(),
15492
15512
  caps: array(string()).readonly().optional()
15493
15513
  }), record(string(), record(string(), unknown().nullable())));
15494
- object({
15495
- /** Ms epoch of the last press. null = never observed since this provider started. */
15496
- lastPressedAt: number().nullable(),
15497
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
15498
- pressCountSinceStart: number()
15499
- });
15500
- object({
15501
- deviceId: number(),
15502
- timestamp: number()
15503
- });
15504
- /**
15505
- * The `doorbell-source` extension slot's editable value (D648): the camera's
15506
- * virtual-doorbell trigger devices, exactly what the wrapper's
15507
- * `doorbellSources` persists (as `{ deviceId }` records).
15508
- */
15509
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
15514
+ var ExtensionSlotIdSchema = _enum(["linked-devices"]);
15510
15515
  DeviceType.Camera;
15511
- DeviceType.Button, DeviceType.Camera;
15512
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
15513
- DeviceType.Camera, DeviceType.Camera;
15514
15516
  /** A slot's editable value, discriminated on `slot`. */
15515
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15517
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15516
15518
  DeviceType.Camera;
15517
15519
  var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
15518
15520
  state: literal("unreachable"),
@@ -15549,10 +15551,11 @@ var ExtensionSlotSourceSchema = object({
15549
15551
  });
15550
15552
  /**
15551
15553
  * A DIFFERENT authority that gates whether the slot's sources do anything —
15552
- * reported read-only, named, never written through this contract (D62). The
15553
- * virtual doorbell's sources ring only while the wrapper is bound to the
15554
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
15555
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
15554
+ * reported read-only, named, never written through this contract (D62) — e.g.
15555
+ * a slot whose sources act only while a wrapper is bound to the host, a
15556
+ * binding that belongs to `deviceManager.setWrapperActive`. No slot reports
15557
+ * one today (`linked-devices` answers `null`). `unknown` means the gate could
15558
+ * not be read; it is not "inactive" (D315).
15556
15559
  */
15557
15560
  var ExtensionSlotActivationSchema = object({
15558
15561
  state: _enum([
@@ -15568,7 +15571,7 @@ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
15568
15571
  /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
15569
15572
  slotRef: string(),
15570
15573
  slot: ExtensionSlotIdSchema,
15571
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
15574
+ /** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
15572
15575
  authority: string(),
15573
15576
  activation: ExtensionSlotActivationSchema.nullable(),
15574
15577
  value: ExtensionSlotValueSchema,
@@ -15846,7 +15849,7 @@ method(object({
15846
15849
  * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
15847
15850
  * ends in a log line, and a log line is the thing the operator asked to stop
15848
15851
  * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
15849
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
15852
+ * 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
15850
15853
  * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
15851
15854
  * media blackout were both diagnosed. The counters stay; this is where they can
15852
15855
  * be READ.
@@ -17222,7 +17225,7 @@ var BrokerStatusSchema$1 = _enum([
17222
17225
  "unreachable",
17223
17226
  "tls-error"
17224
17227
  ]);
17225
- var BrokerInfoSchema = object({
17228
+ var BrokerInfoSchema$1 = object({
17226
17229
  id: string(),
17227
17230
  name: string(),
17228
17231
  url: string(),
@@ -17285,7 +17288,7 @@ var StatusSchema = object({
17285
17288
  brokerCount: number(),
17286
17289
  embeddedRunning: boolean()
17287
17290
  });
17288
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17291
+ method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17289
17292
  kind: "mutation",
17290
17293
  auth: "admin"
17291
17294
  }), method(IdInputSchema, _void(), {
@@ -19461,78 +19464,16 @@ var NcRuleInputSchema = object({
19461
19464
  */
19462
19465
  confirm: NcConfirmSchema.optional(),
19463
19466
  /**
19464
- * WAIT for face/plate recognition before saying anything.
19465
- *
19466
- * A notification's TEXT is frozen at enqueue and its media is re-resolved at
19467
- * send; the identity is neither. A face is confirmed after `confirmFrames`
19468
- * agreeing observations — p50 **11.4 s** after the track was first seen,
19469
- * measured on this hub — and an `immediate` rule enqueues on the first object
19470
- * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
19471
- * immediate path, and no amount of media re-resolution fixes a sentence.
19472
- *
19473
- * Only two honest answers exist, and this flag picks between them. It has
19474
- * effect ONLY on a rule that declares a recognition scope
19475
- * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
19476
- * other rule there is nothing to wait for and the flag is inert.
19477
- *
19478
- * | value | what happens |
19479
- * | --- | --- |
19480
- * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
19481
- * | 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) |
19482
- *
19483
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19484
- * on the addon cap path, and absent has to keep meaning exactly what every
19485
- * rule authored before this field meant.
19486
- *
19487
- * The cost of `true` is stated here because the editor states it too: a rule
19488
- * that waits also inherits track-close SEMANTICS — its `zones` condition
19489
- * tests every zone the track visited and a `crossing` condition can no longer
19490
- * be satisfied, because a closed track carries no crossing.
19491
- */
19492
- waitForEnhancement: boolean().optional(),
19493
- /**
19494
- * GROUP a burst of subjects into ONE notification that grows.
19495
- *
19496
- * Seconds of quiet after the last matching subject before the burst is
19497
- * considered over. While it is open, the first subject enqueues immediately —
19498
- * **exactly as today, with no added latency** — and every real growth (a new
19499
- * subject, or a name confirmed on one already in it) REPLACES that
19500
- * notification with an updated one naming everybody. The push carries the
19501
- * group's own coalescing tag, so the phone replaces rather than stacks.
19502
- *
19503
- * `0` / absent = off, and off is today's behaviour byte for byte.
19504
- *
19505
- * ### Why an idle cutoff and not a window
19506
- *
19507
- * The measured seven-person arrival on device 590 spans 110 s with every
19508
- * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
19509
- * idle cutoff holds it as one and ends it when the arrival actually ends.
19510
- * 30 s is a widely shipped value for the same decision.
19511
- *
19512
- * ### What it replaces
19513
- *
19514
- * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
19515
- * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
19516
- * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
19517
- * track that did fire and 7 carrying a confirmed identity nobody heard about.
19518
- * A group collapses the same volume by MERGING, so the cooldown becomes a
19519
- * budget over GROUPS — which is what it always meant — and a growth is never
19520
- * throttled by the window its own first member spent.
19521
- *
19522
- * ### Interaction with {@link waitForEnhancement}
19523
- *
19524
- * They compose, and the order matters. `waitForEnhancement` defers the rule to
19525
- * TRACK CLOSE, so with both set the group is opened by the first member to
19526
- * CLOSE — already carrying its name — and grows as later members close. That
19527
- * is later, and complete. With grouping alone the group opens on the first
19528
- * object event and picks up names as they are confirmed, through the growth
19529
- * path. Neither combination fires twice for one subject.
19530
- *
19531
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19532
- * on the addon cap path, so absent must keep meaning what it meant before this
19533
- * field existed.
19467
+ * IMMEDIATE GROUPED (D692): the first matching subject opens a window of this many
19468
+ * seconds; the rule sends ONE message when the window closes — early, once every
19469
+ * subject in it is final, never before {@link NC_GROUPING_MIN_SEC} — naming everybody
19470
+ * the rule matched, with one mosaic tile per subject.
19471
+ * Absent = plain immediate (one message per track, at the first match).
19472
+ * Only an `immediate` DETECTION rule may carry it (`ruleGroupingApplies`, enforced on
19473
+ * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
19474
+ * path does not run Zod defaults.
19534
19475
  */
19535
- groupIdleSec: number().int().min(0).max(600).optional()
19476
+ groupingSec: number().int().min(5).max(60).optional()
19536
19477
  });
19537
19478
  /**
19538
19479
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -19580,9 +19521,10 @@ var NcRuleClearableKeySchema = _enum([
19580
19521
  "targetUsers",
19581
19522
  "snoozeOptions",
19582
19523
  "snoozeAllowGlobal",
19583
- "waitForEnhancement",
19584
19524
  "actions",
19585
19525
  "confirm",
19526
+ "groupingSec",
19527
+ "waitForEnhancement",
19586
19528
  "groupIdleSec"
19587
19529
  ]);
19588
19530
  var NcRuleUpdateInputSchema = object({
@@ -19604,9 +19546,25 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19604
19546
  */
19605
19547
  disabledTargetIds: array(string()).default([])
19606
19548
  });
19549
+ /**
19550
+ * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19551
+ * still-true state. `backups-exports` and `alarm` are not sections — a completed
19552
+ * job is not an open state, and the alarm stays instant.
19553
+ */
19554
+ var NcStateSummarySectionIdSchema = _enum([
19555
+ "device-state",
19556
+ "streams",
19557
+ "battery",
19558
+ "consumables",
19559
+ "tanks",
19560
+ "detection-blind",
19561
+ "nodes",
19562
+ "addons",
19563
+ "updates"
19564
+ ]);
19607
19565
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
19608
19566
  var NcStateSummaryPreviewSectionSchema = object({
19609
- id: string(),
19567
+ id: NcStateSummarySectionIdSchema,
19610
19568
  status: _enum(["read", "unavailable"]),
19611
19569
  items: number().int().nonnegative(),
19612
19570
  reason: string().optional()
@@ -19616,7 +19574,7 @@ var NcStateSummaryPreviewSectionSchema = object({
19616
19574
  * a `rule` split (no `section`), one section's on a `section` split.
19617
19575
  */
19618
19576
  var NcStateSummaryPreviewRowSchema = object({
19619
- section: string().optional(),
19577
+ section: NcStateSummarySectionIdSchema.optional(),
19620
19578
  /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
19621
19579
  title: string(),
19622
19580
  text: string(),
@@ -20053,6 +20011,29 @@ object({
20053
20011
  firstAt: number(),
20054
20012
  lastAt: number()
20055
20013
  });
20014
+ var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
20015
+ var NcAlarmDelaySecSchema = number().int().min(0).max(600);
20016
+ var NcAlarmTriggeredSecSchema = number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
20017
+ var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
20018
+ var NcAlarmModeDelaysListSchema = array(object({
20019
+ mode: AlarmArmModeSchema,
20020
+ exitDelaySec: NcAlarmDelaySecSchema.optional(),
20021
+ entryDelaySec: NcAlarmDelaySecSchema.optional(),
20022
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
20023
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20024
+ var NcAlarmModeDelaysEditListSchema = array(object({
20025
+ mode: AlarmArmModeSchema,
20026
+ exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20027
+ entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20028
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
20029
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20030
+ /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20031
+ var NcAlarmNonBlockingEditSchema = object({
20032
+ mode: AlarmArmModeSchema,
20033
+ deviceId: number().int().positive(),
20034
+ /** true = on the list (may be open, does not block); false = off the list (blocks). */
20035
+ nonBlocking: boolean()
20036
+ });
20056
20037
  /**
20057
20038
  * The per-mode lists, at most one per mode. Two lists for one mode have no
20058
20039
  * meaning a reader could agree on (union? the last one?), so they are refused
@@ -20061,10 +20042,10 @@ object({
20061
20042
  var NcAlarmNonBlockingListSchema = array(object({
20062
20043
  mode: AlarmArmModeSchema,
20063
20044
  deviceIds: array(number().int()).max(200)
20064
- })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
20045
+ })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
20065
20046
  /**
20066
- * The three durations the panel's state machine runs on, who hears about its
20067
- * transitions, and which openings each mode tolerates.
20047
+ * The three durations — global, and per mode since D697 — the panel's state
20048
+ * machine runs on, who hears about its transitions, and which openings each mode tolerates.
20068
20049
  *
20069
20050
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
20070
20051
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -20074,15 +20055,15 @@ var NcAlarmNonBlockingListSchema = array(object({
20074
20055
  */
20075
20056
  var NcAlarmSettingsSchema = object({
20076
20057
  /** Grace period between arming and the mode taking effect. 0 = immediate. */
20077
- exitDelaySec: number().int().min(0).max(600),
20058
+ exitDelaySec: NcAlarmDelaySecSchema,
20078
20059
  /** Grace period between a trigger and the alarm firing. 0 = immediate. */
20079
- entryDelaySec: number().int().min(0).max(600),
20060
+ entryDelaySec: NcAlarmDelaySecSchema,
20080
20061
  /**
20081
20062
  * How long `triggered` lasts before the panel re-arms itself.
20082
20063
  * **0 = until an operator disarms it** — the behaviour before this field
20083
20064
  * existed, and therefore what an untouched install keeps doing.
20084
20065
  */
20085
- triggeredDurationSec: number().int().min(0).max(3600),
20066
+ triggeredDurationSec: NcAlarmTriggeredSecSchema,
20086
20067
  /**
20087
20068
  * Deprecated by D630 — every transition is announced. Parsed, never read;
20088
20069
  * remove after one release. Optional so an older admin's patch (which still
@@ -20105,17 +20086,30 @@ var NcAlarmSettingsSchema = object({
20105
20086
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
20106
20087
  * parses as "every opening blocks" — the behaviour it was written under.
20107
20088
  */
20108
- nonBlocking: NcAlarmNonBlockingListSchema.default([])
20089
+ nonBlocking: NcAlarmNonBlockingListSchema.default([]),
20090
+ /**
20091
+ * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
20092
+ * without a field, uses the global value above — so `[]`, the default, is exactly the
20093
+ * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
20094
+ */
20095
+ modeDelays: NcAlarmModeDelaysListSchema.default([])
20109
20096
  });
20110
20097
  /**
20111
20098
  * Every field optional — a tab edits one control at a time.
20112
20099
  *
20113
- * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
20114
- * remove an inner `.default()`, so a patch that never named the field would
20115
- * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
20116
- * (the same trap {@link NcRulePatchSchema} documents).
20100
+ * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
20101
+ * `.partial()` does not remove an inner `.default()`, so a patch that never
20102
+ * named the field would parse to `[]` and wipe every mode's list on an
20103
+ * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
20104
+ * `modeDelays` means EDITS, not the stored overrides.
20117
20105
  */
20118
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
20106
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
20107
+ nonBlocking: NcAlarmNonBlockingListSchema,
20108
+ /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
20109
+ modeDelays: NcAlarmModeDelaysEditListSchema,
20110
+ /** ONE membership change; the panel applies it to the list it holds at its turn. */
20111
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema
20112
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
20119
20113
  /**
20120
20114
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
20121
20115
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -20215,6 +20209,28 @@ var NcAlarmConfigSchema = object({
20215
20209
  */
20216
20210
  live: NcAlarmLiveSchema.optional()
20217
20211
  });
20212
+ var NcAlarmSensorReadingSchema = _enum([
20213
+ "open",
20214
+ "closed",
20215
+ "unknown",
20216
+ "not-opening"
20217
+ ]);
20218
+ /** One device's state as the alarm reads it — the classifier the arm check uses. */
20219
+ var NcAlarmSensorStateSchema = object({
20220
+ deviceId: number().int().positive(),
20221
+ reading: NcAlarmSensorReadingSchema,
20222
+ /** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
20223
+ state: string().nullable(),
20224
+ /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
20225
+ unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
20226
+ /** The device manager's liveness as the centre mirrors it; null = not known. */
20227
+ online: boolean().nullable()
20228
+ });
20229
+ var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
20230
+ var NcAlarmSensorsSchema = object({
20231
+ sensors: array(NcAlarmSensorStateSchema),
20232
+ readAt: number().int().nonnegative()
20233
+ });
20218
20234
  /**
20219
20235
  * ONE rule's demand on ONE camera's clip ring.
20220
20236
  *
@@ -20305,7 +20321,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
20305
20321
  }), method(object({}), NcAlarmConfigSchema, { auth: "admin" }), method(object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
20306
20322
  kind: "mutation",
20307
20323
  auth: "admin"
20308
- });
20324
+ }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
20309
20325
  /**
20310
20326
  * Zod schemas for persisted record types.
20311
20327
  *
@@ -20652,12 +20668,26 @@ var CameraSensorRefSchema = object({
20652
20668
  function cameraSensorRefKey(ref) {
20653
20669
  return `${ref.addonId}/${ref.stableId}`;
20654
20670
  }
20655
- /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20656
- var CameraSensorsModeSchema = _enum(["in-addition"]);
20657
- var CameraSensorsConfigSchema = object({
20658
- mode: CameraSensorsModeSchema,
20659
- sensors: array(CameraSensorRefSchema).min(1).max(8)
20660
- }).superRefine((value, ctx) => {
20671
+ /**
20672
+ * A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
20673
+ * `replace`: its motion is the camera's, and while one is READY the camera's
20674
+ * own motion no longer counts on its own. `confirm`: while one is READY the
20675
+ * camera's own motion counts only while a confirm sensor is active — with or
20676
+ * without a replace sensor (spec C2, the controller's ruling on Q1).
20677
+ */
20678
+ var CameraSensorModeSchema = _enum([
20679
+ "in-addition",
20680
+ "confirm",
20681
+ "replace"
20682
+ ]);
20683
+ /** A stored sensor with no mode — every CS-1 row — is in addition. */
20684
+ var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
20685
+ /**
20686
+ * A camera's attached sensors. A CS-1 row carried an envelope
20687
+ * `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
20688
+ * what the write stores (D672 rule 3), so it is never written again.
20689
+ */
20690
+ var CameraSensorsConfigSchema = object({ sensors: array(CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) })).min(1).max(8) }).superRefine((value, ctx) => {
20661
20691
  const seen = /* @__PURE__ */ new Set();
20662
20692
  value.sensors.forEach((ref, index) => {
20663
20693
  const key = cameraSensorRefKey(ref);
@@ -20669,39 +20699,41 @@ var CameraSensorsConfigSchema = object({
20669
20699
  seen.add(key);
20670
20700
  });
20671
20701
  });
20672
- var RefFields = {
20702
+ /** Every attachment names its mode; an older orchestrator that does not is in addition. */
20703
+ var AttachmentFields = {
20673
20704
  addonId: string(),
20674
- stableId: string()
20705
+ stableId: string(),
20706
+ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
20675
20707
  };
20676
20708
  var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20677
20709
  object({
20678
- ...RefFields,
20710
+ ...AttachmentFields,
20679
20711
  state: literal("ready"),
20680
20712
  deviceId: number(),
20681
20713
  name: string(),
20682
20714
  caps: array(string()).readonly()
20683
20715
  }),
20684
20716
  object({
20685
- ...RefFields,
20717
+ ...AttachmentFields,
20686
20718
  state: literal("offline"),
20687
20719
  deviceId: number(),
20688
20720
  name: string(),
20689
20721
  reason: _enum(["offline", "disabled"])
20690
20722
  }),
20691
20723
  object({
20692
- ...RefFields,
20724
+ ...AttachmentFields,
20693
20725
  state: literal("missing"),
20694
20726
  reason: string()
20695
20727
  }),
20696
20728
  object({
20697
- ...RefFields,
20729
+ ...AttachmentFields,
20698
20730
  state: literal("unsupported"),
20699
20731
  deviceId: number(),
20700
20732
  name: string(),
20701
20733
  reason: string()
20702
20734
  }),
20703
20735
  object({
20704
- ...RefFields,
20736
+ ...AttachmentFields,
20705
20737
  state: literal("unreadable"),
20706
20738
  reason: string()
20707
20739
  })
@@ -20714,17 +20746,49 @@ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20714
20746
  * say.
20715
20747
  */
20716
20748
  var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20749
+ /** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
20750
+ var CameraOwnMotionSchema = _enum([
20751
+ "always",
20752
+ "when-confirmed",
20753
+ "never"
20754
+ ]);
20755
+ /** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
20756
+ var CameraSensorsNotEnforcedSchema = _enum(["provider-not-split"]);
20757
+ /**
20758
+ * The camera's OWN onboard motion (D696): the raw input the bridge combines,
20759
+ * never the effective value. The camera's `motion` slice is the effective one,
20760
+ * so a view of the camera's own row reads this.
20761
+ * - `atMs`: when the bridge received the camera's last own edge.
20762
+ * - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
20763
+ * AND it is younger than the window the bridge counts the camera's own motion
20764
+ * for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
20765
+ * sends a clear (heartbeats only) therefore reads `false` once no push came
20766
+ * within the window; a status read is a snapshot, so a live view re-asks.
20767
+ * Absent = this orchestrator has not received the camera's own motion since it
20768
+ * started (unknown, never "quiet"), or an orchestrator older than D696.
20769
+ */
20770
+ var CameraRawLevelSchema = object({
20771
+ detected: boolean(),
20772
+ atMs: number()
20773
+ });
20717
20774
  var CameraSensorsStatusSchema = discriminatedUnion("state", [
20718
- object({ state: literal("not-configured") }),
20775
+ object({
20776
+ state: literal("not-configured"),
20777
+ cameraLevel: CameraRawLevelSchema.optional()
20778
+ }),
20719
20779
  object({
20720
20780
  state: literal("unreadable"),
20721
- reason: string()
20781
+ reason: string(),
20782
+ cameraLevel: CameraRawLevelSchema.optional()
20722
20783
  }),
20723
20784
  object({
20724
20785
  state: literal("configured"),
20725
- mode: CameraSensorsModeSchema,
20786
+ cameraLevel: CameraRawLevelSchema.optional(),
20726
20787
  sensors: array(CameraSensorAttachmentSchema).readonly(),
20727
- notForwarded: CameraSensorsNotForwardedSchema.optional()
20788
+ /** Absent = an orchestrator older than CS-2: it does not say (D315). */
20789
+ cameraMotion: CameraOwnMotionSchema.optional(),
20790
+ notForwarded: CameraSensorsNotForwardedSchema.optional(),
20791
+ notEnforced: CameraSensorsNotEnforcedSchema.optional()
20728
20792
  })
20729
20793
  ]);
20730
20794
  Object.freeze({
@@ -21409,14 +21473,14 @@ var TrackSchema = object({
21409
21473
  * `derive-returned-null tile=standin`,
21410
21474
  * `native-tile tile=native no-key-frame-at-close`, …) written at close and
21411
21475
  * CLEARED by the late-keyFrame upgrade when a native tile lands after all.
21412
- * The operator-facing answer to "perché manca l'immagine?" on a track whose
21476
+ * The operator-facing answer to "perché manca l'immagine?" [why is the image missing?] on a track whose
21413
21477
  * tile is a face/plate stand-in, a raster crop, or an icon.
21414
21478
  *
21415
21479
  * **`no-key-frame-at-close` (D400).** The line used to be written ONLY when
21416
21480
  * the TILE ranked below native, so a track whose thumbnail landed natively
21417
21481
  * while its key frame missed carried no reason at all — three of the four
21418
21482
  * `MISSING_KEYFRAME` tracks in the 2026-09-07 operator audit were exactly
21419
- * that shape, and their "niente ultimo" had no cause anywhere on the row.
21483
+ * that shape, and their "niente ultimo" [no last frame] had no cause anywhere on the row.
21420
21484
  * The key frame is the head of a cascade (`keyFrame ⇒ keyFrameSmall ⇒ the
21421
21485
  * lastFrame copy source`), so its absence is now on this line whatever the
21422
21486
  * tile ended up being.
@@ -21503,7 +21567,7 @@ var MotionEventSchema = object({
21503
21567
  /**
21504
21568
  * Ms offsets from `timestamp` (the episode's own first rising edge, so the
21505
21569
  * first entry is always `0`) of every genuine off→on transition the
21506
- * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
21570
+ * episode saw — "ogni evento on si deve salvare" [every ON event must be saved] (D475). NOT one entry per
21507
21571
  * push: a firmware source that keepalives at ~1 Hz for the whole burst
21508
21572
  * (Reolink, Hikvision) produces exactly one edge; a source that reports an
21509
21573
  * explicit `false` mid-episode and then resumes before the quiet window
@@ -22027,6 +22091,7 @@ var KeyEventSchema = object({
22027
22091
  bestEventId: string(),
22028
22092
  /** Track lifetime in ms (lastSeen - firstSeen). */
22029
22093
  windowMs: number().optional(),
22094
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
22030
22095
  ...TrackFlagFields,
22031
22096
  ...TrackRetrainFields
22032
22097
  });
@@ -23915,8 +23980,10 @@ var RunnerCameraConfigSchema = object({
23915
23980
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
23916
23981
  * analyzer providers) running on decoded frames in this runner.
23917
23982
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
23918
- * analytics). The provider emits ProviderMotion
23919
- * events; the orchestrator forwards to `reportMotion`.
23983
+ * analytics). The provider emits its raw push as `ProviderMotion`
23984
+ * (D673); the orchestrator's camera-sensors bridge re-emits the camera's
23985
+ * effective motion as `MotionOnMotionChanged`, which is what reaches
23986
+ * `reportMotion`.
23920
23987
  */
23921
23988
  motionSources: MotionSourcesSchema.default(["analyzer"]),
23922
23989
  /**
@@ -24093,7 +24160,15 @@ var RunnerLocalLoadSchema = object({
24093
24160
  hasGpu: boolean(),
24094
24161
  inferenceBackend: string().optional(),
24095
24162
  cpuCores: number().optional()
24096
- })
24163
+ }),
24164
+ /**
24165
+ * Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
24166
+ * runner never writes it. ABSENT = a runner older than D696, which still
24167
+ * writes the slice from its phase machine; the orchestrator does not write
24168
+ * while any runner node is absent-or-unanswered, so one slice never has two
24169
+ * writers.
24170
+ */
24171
+ motionSliceWriter: literal("orchestrator").optional()
24097
24172
  });
24098
24173
  /**
24099
24174
  * Aggregate runtime metrics for the runner's whole local pool. Mirrors the
@@ -26833,7 +26908,7 @@ var ClipPlaybackSchema = object({
26833
26908
  * **One authorised yes is one wake.** `next-natural` — take the clip the next
26834
26909
  * time the camera is awake for its own reasons — is deliberately not a member:
26835
26910
  * it was proposed, it is free on the battery, and the operator declined it on
26836
- * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
26911
+ * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."* [When an export is requested the camera wakes up],
26837
26912
  * D558 "Considered and not taken").
26838
26913
  */
26839
26914
  var ClipWakeSchema = _enum(["authorised"]);
@@ -27094,7 +27169,7 @@ DeviceType.Camera, method(object({
27094
27169
  * It replaced `sources?: string[]`, a VIEW filter over source ids that
27095
27170
  * assumed the answer was a fan-out over everything a camera has. The
27096
27171
  * operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
27097
- * solo"* — so the list is asked of one provider at a time and there is
27172
+ * solo"* [there is only one user] — so the list is asked of one provider at a time and there is
27098
27173
  * nothing to filter out of it.
27099
27174
  */
27100
27175
  provider: string().min(1)
@@ -29038,6 +29113,17 @@ object({
29038
29113
  deviceId: number(),
29039
29114
  status: DeviceStatusSchema
29040
29115
  });
29116
+ object({
29117
+ /** Ms epoch of the last press. null = never observed since this provider started. */
29118
+ lastPressedAt: number().nullable(),
29119
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
29120
+ pressCountSinceStart: number()
29121
+ });
29122
+ object({
29123
+ deviceId: number(),
29124
+ timestamp: number()
29125
+ });
29126
+ DeviceType.Button, DeviceType.Camera;
29041
29127
  /**
29042
29128
  * Enum-state sensor — a string value picked from a finite option set.
29043
29129
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -30682,9 +30768,9 @@ object({
30682
30768
  /** Ms epoch of the last detected-true observation. Null if never detected. */
30683
30769
  lastDetectedAt: number().nullable(),
30684
30770
  /**
30685
- * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
30686
- * `null` while false and on every `Sensor` device (D475) — see that
30687
- * constant's doc for the one-authority rule.
30771
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
30772
+ * orchestrator writes it, D696), `null` while false and on every `Sensor`
30773
+ * device (D475) — see that constant's doc for the one-authority rule.
30688
30774
  *
30689
30775
  * ## Reading this field still arms nothing
30690
30776
  *
@@ -30702,38 +30788,41 @@ object({
30702
30788
  * `addon-export-alexa/src/motion-clear-hold.ts` and
30703
30789
  * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
30704
30790
  *
30705
- * ## Who writes it
30706
- *
30707
- * - **Cameras** — the runner's phase machine, `active → watching` on
30708
- * `cooldown_expired`, which then writes this slice with
30709
- * `detected: false` (`handlePhaseChanged` in
30710
- * `pipeline-runner/index.ts`). It produces the FALLING edge, which
30711
- * matters most for the sources that only ever push a rising one:
30712
- * Reolink emits `MotionOnMotionChanged { detected: true }` and never
30713
- * a false.
30714
- * - **Sensors** (Home Assistant binary sensors, Homematic) — the
30715
- * provider pushes the false itself, from the upstream system's own
30716
- * state change. No phase machine is involved.
30717
- *
30718
- * ### The phase machine is CANONICAL, not sole — and that is a defect
30719
- *
30720
- * An earlier revision of this docblock (mine, 2026-09-12) claimed the
30721
- * phase machine is the sole writer for a camera. It is not.
30722
- * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
30723
- * `setCapSlice(motionCapability, …)` on their own rising edge, and
30724
- * Hikvision's comment says why: it read THIS docblock, agreed the
30725
- * runner is canonical, and wrote anyway to avoid per-tick churn. So
30726
- * two authorities can disagree about one slice, which this repo
30727
- * forbids, and the doc said otherwise — which is worse than saying
30728
- * nothing, because it reads as verification.
30729
- *
30730
- * This predates D475 and is not fixed there: the fix touches every
30731
- * camera provider. Recorded in D475's Consequences. Do not restore the
30732
- * "sole writer" wording without also removing the other writers.
30791
+ * ## Who writes it (D696)
30792
+ *
30793
+ * - **Cameras** — the orchestrator, and nothing else. It writes the camera's
30794
+ * EFFECTIVE motion (`addon-pipeline-orchestrator`
30795
+ * `camera-sensors/motion-slice.ts`), alongside the effective
30796
+ * `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
30797
+ * for a split camera, an unsplit emitter's direct event, the analyzer and
30798
+ * device activity, as the camera's `motionSources` counts them. A source
30799
+ * that never sends a clear falls one window after its last `true`.
30800
+ * `lastDetectedAt` moves only on an effective rise. No camera provider
30801
+ * writes this slice (not even a boot seed), and neither does the runner's
30802
+ * phase machine: its phase is "a session is attached" for every on-motion
30803
+ * session, not motion, and it now has its own `DetectionPhaseTransition`.
30804
+ * A provider's `isDetected` answers from the hub's slice.
30805
+ * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
30806
+ * imports) — the provider pushes both edges itself, from the upstream
30807
+ * system's own state change. That is the sensor's own fact.
30808
+ *
30809
+ * `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
30810
+ * `motion` slice; each sensor writer is a named exemption.
30811
+ *
30812
+ * This replaces the D475 §4 rule ("the phase machine is the sole writer").
30813
+ * That rule was never true: Hikvision and Amcrest wrote the slice raw beside
30814
+ * it, every provider seeded it at boot, and every session left it
30815
+ * `detected: true` after its detach.
30733
30816
  */
30734
30817
  autoClearAfterMs: number().nullable()
30735
30818
  });
30736
- object({
30819
+ /**
30820
+ * Payload of `motion.onMotionChanged` event + the corresponding bus
30821
+ * event `EventCategory.MotionOnMotionChanged`. Single source of truth
30822
+ * — both the cap event surface and the bus payload type alias to this
30823
+ * schema.
30824
+ */
30825
+ var MotionOnMotionChangedDataSchema = object({
30737
30826
  deviceId: number(),
30738
30827
  detected: boolean(),
30739
30828
  timestamp: number(),
@@ -33290,7 +33379,7 @@ var ReadGopBytesResultSchema = object({
33290
33379
  /**
33291
33380
  * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
33292
33381
  * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
33293
- * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
33382
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-clip-replay-on-pipeline.md`):
33294
33383
  * a replay needs several seconds of native pixels, not one frame.
33295
33384
  *
33296
33385
  * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
@@ -35906,6 +35995,7 @@ DeviceType.Camera, method(object({
35906
35995
  detection: array(ZoneRuleSchema).readonly(),
35907
35996
  package: array(ZoneRuleSchema).readonly()
35908
35997
  });
35998
+ SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
35909
35999
  /**
35910
36000
  * Accessory device helpers — shared across drivers.
35911
36001
  *
@@ -36260,13 +36350,14 @@ var BaseDeviceProvider = class extends BaseAddon {
36260
36350
  async getStatus() {
36261
36351
  const all = await this.ctx.kernel.devices?.getAll() ?? [];
36262
36352
  const summary = this.restoreFailureSummary();
36263
- if (summary === null) return {
36353
+ const base = {
36264
36354
  connected: true,
36265
- deviceCount: all.length
36355
+ deviceCount: all.length,
36356
+ motionSliceWriter: "orchestrator"
36266
36357
  };
36358
+ if (summary === null) return base;
36267
36359
  return {
36268
- connected: true,
36269
- deviceCount: all.length,
36360
+ ...base,
36270
36361
  error: summary
36271
36362
  };
36272
36363
  }
@@ -41768,6 +41859,12 @@ Object.freeze({
41768
41859
  addonId: null,
41769
41860
  access: "view"
41770
41861
  },
41862
+ "notificationRules.getAlarmSensors": {
41863
+ capName: "notification-rules",
41864
+ capScope: "system",
41865
+ addonId: null,
41866
+ access: "view"
41867
+ },
41771
41868
  "notificationRules.getClipRetentionAsks": {
41772
41869
  capName: "notification-rules",
41773
41870
  capScope: "system",
@@ -45346,6 +45443,11 @@ Object.freeze({
45346
45443
  }
45347
45444
  });
45348
45445
  Object.freeze({
45446
+ "accessories.getStatus": [{
45447
+ name: "deviceId",
45448
+ form: "single",
45449
+ optional: false
45450
+ }],
45349
45451
  "accessories.setChildHidden": [{
45350
45452
  name: "childDeviceId",
45351
45453
  form: "single",
@@ -45365,6 +45467,11 @@ Object.freeze({
45365
45467
  form: "single",
45366
45468
  optional: false
45367
45469
  }],
45470
+ "airQualitySensor.getStatus": [{
45471
+ name: "deviceId",
45472
+ form: "single",
45473
+ optional: false
45474
+ }],
45368
45475
  "alarmPanel.arm": [{
45369
45476
  name: "deviceId",
45370
45477
  form: "single",
@@ -45375,11 +45482,36 @@ Object.freeze({
45375
45482
  form: "single",
45376
45483
  optional: false
45377
45484
  }],
45485
+ "alarmPanel.getStatus": [{
45486
+ name: "deviceId",
45487
+ form: "single",
45488
+ optional: false
45489
+ }],
45378
45490
  "alarmPanel.trigger": [{
45379
45491
  name: "deviceId",
45380
45492
  form: "single",
45381
45493
  optional: false
45382
45494
  }],
45495
+ "ambientLightSensor.getStatus": [{
45496
+ name: "deviceId",
45497
+ form: "single",
45498
+ optional: false
45499
+ }],
45500
+ "audioAnalysis.applyDeviceSettingsPatch": [{
45501
+ name: "deviceId",
45502
+ form: "single",
45503
+ optional: false
45504
+ }],
45505
+ "audioAnalysis.getDeviceLiveContribution": [{
45506
+ name: "deviceId",
45507
+ form: "single",
45508
+ optional: false
45509
+ }],
45510
+ "audioAnalysis.getDeviceSettingsContribution": [{
45511
+ name: "deviceId",
45512
+ form: "single",
45513
+ optional: false
45514
+ }],
45383
45515
  "audioAnalysis.resolveDeviceSettings": [{
45384
45516
  name: "deviceId",
45385
45517
  form: "single",
@@ -45420,16 +45552,36 @@ Object.freeze({
45420
45552
  form: "single",
45421
45553
  optional: false
45422
45554
  }],
45555
+ "automationControl.getStatus": [{
45556
+ name: "deviceId",
45557
+ form: "single",
45558
+ optional: false
45559
+ }],
45423
45560
  "automationControl.trigger": [{
45424
45561
  name: "deviceId",
45425
45562
  form: "single",
45426
45563
  optional: false
45427
45564
  }],
45565
+ "battery.getStatus": [{
45566
+ name: "deviceId",
45567
+ form: "single",
45568
+ optional: false
45569
+ }],
45428
45570
  "battery.wakeForStream": [{
45429
45571
  name: "deviceId",
45430
45572
  form: "single",
45431
45573
  optional: false
45432
45574
  }],
45575
+ "binary.getStatus": [{
45576
+ name: "deviceId",
45577
+ form: "single",
45578
+ optional: false
45579
+ }],
45580
+ "brightness.getStatus": [{
45581
+ name: "deviceId",
45582
+ form: "single",
45583
+ optional: false
45584
+ }],
45433
45585
  "brightness.setBrightness": [{
45434
45586
  name: "deviceId",
45435
45587
  form: "single",
@@ -45445,6 +45597,26 @@ Object.freeze({
45445
45597
  form: "single",
45446
45598
  optional: false
45447
45599
  }],
45600
+ "cameraCredentials.getStatus": [{
45601
+ name: "deviceId",
45602
+ form: "single",
45603
+ optional: false
45604
+ }],
45605
+ "cameraGridLayout.applyDeviceSettingsPatch": [{
45606
+ name: "deviceId",
45607
+ form: "single",
45608
+ optional: false
45609
+ }],
45610
+ "cameraGridLayout.getDeviceLiveContribution": [{
45611
+ name: "deviceId",
45612
+ form: "single",
45613
+ optional: false
45614
+ }],
45615
+ "cameraGridLayout.getDeviceSettingsContribution": [{
45616
+ name: "deviceId",
45617
+ form: "single",
45618
+ optional: false
45619
+ }],
45448
45620
  "cameraGridLayout.getLayout": [{
45449
45621
  name: "deviceId",
45450
45622
  form: "single",
@@ -45460,6 +45632,21 @@ Object.freeze({
45460
45632
  form: "single",
45461
45633
  optional: false
45462
45634
  }],
45635
+ "cameraPipelineConfig.applyDeviceSettingsPatch": [{
45636
+ name: "deviceId",
45637
+ form: "single",
45638
+ optional: false
45639
+ }],
45640
+ "cameraPipelineConfig.getDeviceLiveContribution": [{
45641
+ name: "deviceId",
45642
+ form: "single",
45643
+ optional: false
45644
+ }],
45645
+ "cameraPipelineConfig.getDeviceSettingsContribution": [{
45646
+ name: "deviceId",
45647
+ form: "single",
45648
+ optional: false
45649
+ }],
45463
45650
  "cameraStreams.getBrokerStreams": [{
45464
45651
  name: "deviceId",
45465
45652
  form: "single",
@@ -45485,6 +45672,16 @@ Object.freeze({
45485
45672
  form: "single",
45486
45673
  optional: false
45487
45674
  }],
45675
+ "carbonMonoxide.getStatus": [{
45676
+ name: "deviceId",
45677
+ form: "single",
45678
+ optional: false
45679
+ }],
45680
+ "climateControl.getStatus": [{
45681
+ name: "deviceId",
45682
+ form: "single",
45683
+ optional: false
45684
+ }],
45488
45685
  "climateControl.setFanMode": [{
45489
45686
  name: "deviceId",
45490
45687
  form: "single",
@@ -45525,16 +45722,41 @@ Object.freeze({
45525
45722
  form: "single",
45526
45723
  optional: false
45527
45724
  }],
45725
+ "color.getStatus": [{
45726
+ name: "deviceId",
45727
+ form: "single",
45728
+ optional: false
45729
+ }],
45528
45730
  "color.setColor": [{
45529
45731
  name: "deviceId",
45530
45732
  form: "single",
45531
45733
  optional: false
45532
45734
  }],
45735
+ "connectivity.getStatus": [{
45736
+ name: "deviceId",
45737
+ form: "single",
45738
+ optional: false
45739
+ }],
45740
+ "consumables.getStatus": [{
45741
+ name: "deviceId",
45742
+ form: "single",
45743
+ optional: false
45744
+ }],
45533
45745
  "consumables.reset": [{
45534
45746
  name: "deviceId",
45535
45747
  form: "single",
45536
45748
  optional: false
45537
45749
  }],
45750
+ "contact.getStatus": [{
45751
+ name: "deviceId",
45752
+ form: "single",
45753
+ optional: false
45754
+ }],
45755
+ "control.getStatus": [{
45756
+ name: "deviceId",
45757
+ form: "single",
45758
+ optional: false
45759
+ }],
45538
45760
  "control.setValue": [{
45539
45761
  name: "deviceId",
45540
45762
  form: "single",
@@ -45545,6 +45767,11 @@ Object.freeze({
45545
45767
  form: "single",
45546
45768
  optional: false
45547
45769
  }],
45770
+ "cover.getStatus": [{
45771
+ name: "deviceId",
45772
+ form: "single",
45773
+ optional: false
45774
+ }],
45548
45775
  "cover.open": [{
45549
45776
  name: "deviceId",
45550
45777
  form: "single",
@@ -45570,6 +45797,11 @@ Object.freeze({
45570
45797
  form: "single",
45571
45798
  optional: false
45572
45799
  }],
45800
+ "dayNight.getStatus": [{
45801
+ name: "deviceId",
45802
+ form: "single",
45803
+ optional: false
45804
+ }],
45573
45805
  "dayNight.setSettings": [{
45574
45806
  name: "deviceId",
45575
45807
  form: "single",
@@ -45580,11 +45812,31 @@ Object.freeze({
45580
45812
  form: "single",
45581
45813
  optional: true
45582
45814
  }],
45815
+ "detectionPipeline.applyDeviceSettingsPatch": [{
45816
+ name: "deviceId",
45817
+ form: "single",
45818
+ optional: false
45819
+ }],
45820
+ "detectionPipeline.getDeviceLiveContribution": [{
45821
+ name: "deviceId",
45822
+ form: "single",
45823
+ optional: false
45824
+ }],
45825
+ "detectionPipeline.getDeviceSettingsContribution": [{
45826
+ name: "deviceId",
45827
+ form: "single",
45828
+ optional: false
45829
+ }],
45583
45830
  "deviceAdminLink.getAdminLink": [{
45584
45831
  name: "deviceId",
45585
45832
  form: "single",
45586
45833
  optional: false
45587
45834
  }],
45835
+ "deviceAdoption.getStatus": [{
45836
+ name: "deviceId",
45837
+ form: "single",
45838
+ optional: false
45839
+ }],
45588
45840
  "deviceAdoption.release": [{
45589
45841
  name: "camDeviceId",
45590
45842
  form: "single",
@@ -45600,6 +45852,11 @@ Object.freeze({
45600
45852
  form: "single",
45601
45853
  optional: false
45602
45854
  }],
45855
+ "deviceDiscovery.getStatus": [{
45856
+ name: "deviceId",
45857
+ form: "single",
45858
+ optional: false
45859
+ }],
45603
45860
  "deviceDiscovery.listDiscovered": [{
45604
45861
  name: "deviceId",
45605
45862
  form: "single",
@@ -45619,6 +45876,21 @@ Object.freeze({
45619
45876
  form: "single",
45620
45877
  optional: false
45621
45878
  }],
45879
+ "deviceExport.applyDeviceSettingsPatch": [{
45880
+ name: "deviceId",
45881
+ form: "single",
45882
+ optional: false
45883
+ }],
45884
+ "deviceExport.getDeviceLiveContribution": [{
45885
+ name: "deviceId",
45886
+ form: "single",
45887
+ optional: false
45888
+ }],
45889
+ "deviceExport.getDeviceSettingsContribution": [{
45890
+ name: "deviceId",
45891
+ form: "single",
45892
+ optional: false
45893
+ }],
45622
45894
  "deviceExtension.listCandidates": [{
45623
45895
  name: "deviceId",
45624
45896
  form: "single",
@@ -45967,6 +46239,26 @@ Object.freeze({
45967
46239
  form: "single",
45968
46240
  optional: false
45969
46241
  }],
46242
+ "deviceStatus.getStatus": [{
46243
+ name: "deviceId",
46244
+ form: "single",
46245
+ optional: false
46246
+ }],
46247
+ "doorbell.getStatus": [{
46248
+ name: "deviceId",
46249
+ form: "single",
46250
+ optional: false
46251
+ }],
46252
+ "enumSensor.getStatus": [{
46253
+ name: "deviceId",
46254
+ form: "single",
46255
+ optional: false
46256
+ }],
46257
+ "eventEmitter.getStatus": [{
46258
+ name: "deviceId",
46259
+ form: "single",
46260
+ optional: false
46261
+ }],
45970
46262
  "faceGallery.getFaceByTrack": [{
45971
46263
  name: "deviceId",
45972
46264
  form: "single",
@@ -45981,6 +46273,11 @@ Object.freeze({
45981
46273
  form: "array",
45982
46274
  optional: true
45983
46275
  }],
46276
+ "fanControl.getStatus": [{
46277
+ name: "deviceId",
46278
+ form: "single",
46279
+ optional: false
46280
+ }],
45984
46281
  "fanControl.setDirection": [{
45985
46282
  name: "deviceId",
45986
46283
  form: "single",
@@ -46001,6 +46298,26 @@ Object.freeze({
46001
46298
  form: "single",
46002
46299
  optional: false
46003
46300
  }],
46301
+ "featureProbe.getStatus": [{
46302
+ name: "deviceId",
46303
+ form: "single",
46304
+ optional: false
46305
+ }],
46306
+ "flood.getStatus": [{
46307
+ name: "deviceId",
46308
+ form: "single",
46309
+ optional: false
46310
+ }],
46311
+ "gas.getStatus": [{
46312
+ name: "deviceId",
46313
+ form: "single",
46314
+ optional: false
46315
+ }],
46316
+ "humidifier.getStatus": [{
46317
+ name: "deviceId",
46318
+ form: "single",
46319
+ optional: false
46320
+ }],
46004
46321
  "humidifier.setMode": [{
46005
46322
  name: "deviceId",
46006
46323
  form: "single",
@@ -46016,11 +46333,26 @@ Object.freeze({
46016
46333
  form: "single",
46017
46334
  optional: false
46018
46335
  }],
46336
+ "humiditySensor.getStatus": [{
46337
+ name: "deviceId",
46338
+ form: "single",
46339
+ optional: false
46340
+ }],
46341
+ "image.getStatus": [{
46342
+ name: "deviceId",
46343
+ form: "single",
46344
+ optional: false
46345
+ }],
46019
46346
  "imageSettings.getOptions": [{
46020
46347
  name: "deviceId",
46021
46348
  form: "single",
46022
46349
  optional: false
46023
46350
  }],
46351
+ "imageSettings.getStatus": [{
46352
+ name: "deviceId",
46353
+ form: "single",
46354
+ optional: false
46355
+ }],
46024
46356
  "imageSettings.setSettings": [{
46025
46357
  name: "deviceId",
46026
46358
  form: "single",
@@ -46031,6 +46363,11 @@ Object.freeze({
46031
46363
  form: "single",
46032
46364
  optional: false
46033
46365
  }],
46366
+ "intercom.getStatus": [{
46367
+ name: "deviceId",
46368
+ form: "single",
46369
+ optional: false
46370
+ }],
46034
46371
  "intercom.handleAnswer": [{
46035
46372
  name: "deviceId",
46036
46373
  form: "single",
@@ -46061,6 +46398,11 @@ Object.freeze({
46061
46398
  form: "single",
46062
46399
  optional: false
46063
46400
  }],
46401
+ "lawnMowerControl.getStatus": [{
46402
+ name: "deviceId",
46403
+ form: "single",
46404
+ optional: false
46405
+ }],
46064
46406
  "lawnMowerControl.pause": [{
46065
46407
  name: "deviceId",
46066
46408
  form: "single",
@@ -46071,6 +46413,11 @@ Object.freeze({
46071
46413
  form: "single",
46072
46414
  optional: false
46073
46415
  }],
46416
+ "lockControl.getStatus": [{
46417
+ name: "deviceId",
46418
+ form: "single",
46419
+ optional: false
46420
+ }],
46074
46421
  "lockControl.lock": [{
46075
46422
  name: "deviceId",
46076
46423
  form: "single",
@@ -46086,6 +46433,11 @@ Object.freeze({
46086
46433
  form: "single",
46087
46434
  optional: false
46088
46435
  }],
46436
+ "mediaPlayer.getStatus": [{
46437
+ name: "deviceId",
46438
+ form: "single",
46439
+ optional: false
46440
+ }],
46089
46441
  "mediaPlayer.next": [{
46090
46442
  name: "deviceId",
46091
46443
  form: "single",
@@ -46146,6 +46498,11 @@ Object.freeze({
46146
46498
  form: "single",
46147
46499
  optional: false
46148
46500
  }],
46501
+ "motion.getStatus": [{
46502
+ name: "deviceId",
46503
+ form: "single",
46504
+ optional: false
46505
+ }],
46149
46506
  "motion.isDetected": [{
46150
46507
  name: "deviceId",
46151
46508
  form: "single",
@@ -46156,11 +46513,31 @@ Object.freeze({
46156
46513
  form: "single",
46157
46514
  optional: false
46158
46515
  }],
46516
+ "motionDetection.applyDeviceSettingsPatch": [{
46517
+ name: "deviceId",
46518
+ form: "single",
46519
+ optional: false
46520
+ }],
46521
+ "motionDetection.getDeviceLiveContribution": [{
46522
+ name: "deviceId",
46523
+ form: "single",
46524
+ optional: false
46525
+ }],
46526
+ "motionDetection.getDeviceSettingsContribution": [{
46527
+ name: "deviceId",
46528
+ form: "single",
46529
+ optional: false
46530
+ }],
46159
46531
  "motionDetection.removeCamera": [{
46160
46532
  name: "deviceId",
46161
46533
  form: "single",
46162
46534
  optional: false
46163
46535
  }],
46536
+ "motionTrigger.getStatus": [{
46537
+ name: "deviceId",
46538
+ form: "single",
46539
+ optional: false
46540
+ }],
46164
46541
  "motionTrigger.setMotionTrigger": [{
46165
46542
  name: "deviceId",
46166
46543
  form: "single",
@@ -46171,6 +46548,11 @@ Object.freeze({
46171
46548
  form: "single",
46172
46549
  optional: false
46173
46550
  }],
46551
+ "motionZones.getStatus": [{
46552
+ name: "deviceId",
46553
+ form: "single",
46554
+ optional: false
46555
+ }],
46174
46556
  "motionZones.setZone": [{
46175
46557
  name: "deviceId",
46176
46558
  form: "single",
@@ -46181,6 +46563,11 @@ Object.freeze({
46181
46563
  form: "single",
46182
46564
  optional: false
46183
46565
  }],
46566
+ "nativeObjectDetection.getStatus": [{
46567
+ name: "deviceId",
46568
+ form: "single",
46569
+ optional: false
46570
+ }],
46184
46571
  "nativeObjectDetection.setEnabled": [{
46185
46572
  name: "deviceId",
46186
46573
  form: "single",
@@ -46196,6 +46583,11 @@ Object.freeze({
46196
46583
  form: "single",
46197
46584
  optional: false
46198
46585
  }],
46586
+ "navigation.getStatus": [{
46587
+ name: "deviceId",
46588
+ form: "single",
46589
+ optional: false
46590
+ }],
46199
46591
  "navigation.goToPoint": [{
46200
46592
  name: "deviceId",
46201
46593
  form: "single",
@@ -46241,6 +46633,11 @@ Object.freeze({
46241
46633
  form: "single",
46242
46634
  optional: false
46243
46635
  }],
46636
+ "networkLink.getStatus": [{
46637
+ name: "deviceId",
46638
+ form: "single",
46639
+ optional: false
46640
+ }],
46244
46641
  "networkQuality.getDeviceStats": [{
46245
46642
  name: "deviceId",
46246
46643
  form: "single",
@@ -46251,6 +46648,11 @@ Object.freeze({
46251
46648
  form: "single",
46252
46649
  optional: false
46253
46650
  }],
46651
+ "notificationRules.getAlarmSensors": [{
46652
+ name: "deviceIds",
46653
+ form: "array",
46654
+ optional: false
46655
+ }],
46254
46656
  "notificationRules.setDeviceMuted": [{
46255
46657
  name: "deviceId",
46256
46658
  form: "single",
@@ -46261,11 +46663,26 @@ Object.freeze({
46261
46663
  form: "single",
46262
46664
  optional: false
46263
46665
  }],
46666
+ "notifier.getStatus": [{
46667
+ name: "deviceId",
46668
+ form: "single",
46669
+ optional: false
46670
+ }],
46264
46671
  "notifier.send": [{
46265
46672
  name: "deviceId",
46266
46673
  form: "single",
46267
46674
  optional: false
46268
46675
  }],
46676
+ "numericSensor.getStatus": [{
46677
+ name: "deviceId",
46678
+ form: "single",
46679
+ optional: false
46680
+ }],
46681
+ "osd.getStatus": [{
46682
+ name: "deviceId",
46683
+ form: "single",
46684
+ optional: false
46685
+ }],
46269
46686
  "osd.setOverlay": [{
46270
46687
  name: "deviceId",
46271
46688
  form: "single",
@@ -46325,6 +46742,11 @@ Object.freeze({
46325
46742
  form: "single",
46326
46743
  optional: false
46327
46744
  }],
46745
+ "petFeeder.getStatus": [{
46746
+ name: "deviceId",
46747
+ form: "single",
46748
+ optional: false
46749
+ }],
46328
46750
  "petFeeder.markFoodReplenished": [{
46329
46751
  name: "deviceId",
46330
46752
  form: "single",
@@ -46360,6 +46782,11 @@ Object.freeze({
46360
46782
  form: "single",
46361
46783
  optional: false
46362
46784
  }],
46785
+ "pipelineAnalytics.applyDeviceSettingsPatch": [{
46786
+ name: "deviceId",
46787
+ form: "single",
46788
+ optional: false
46789
+ }],
46363
46790
  "pipelineAnalytics.clearTrackRelation": [{
46364
46791
  name: "deviceId",
46365
46792
  form: "single",
@@ -46400,6 +46827,16 @@ Object.freeze({
46400
46827
  form: "single",
46401
46828
  optional: false
46402
46829
  }],
46830
+ "pipelineAnalytics.getDeviceLiveContribution": [{
46831
+ name: "deviceId",
46832
+ form: "single",
46833
+ optional: false
46834
+ }],
46835
+ "pipelineAnalytics.getDeviceSettingsContribution": [{
46836
+ name: "deviceId",
46837
+ form: "single",
46838
+ optional: false
46839
+ }],
46403
46840
  "pipelineAnalytics.getEventDensity": [{
46404
46841
  name: "deviceId",
46405
46842
  form: "single",
@@ -46615,6 +47052,11 @@ Object.freeze({
46615
47052
  form: "single",
46616
47053
  optional: true
46617
47054
  }],
47055
+ "pipelineOrchestrator.applyDeviceSettingsPatch": [{
47056
+ name: "deviceId",
47057
+ form: "single",
47058
+ optional: false
47059
+ }],
46618
47060
  "pipelineOrchestrator.assignAudio": [{
46619
47061
  name: "deviceId",
46620
47062
  form: "single",
@@ -46660,6 +47102,16 @@ Object.freeze({
46660
47102
  form: "single",
46661
47103
  optional: false
46662
47104
  }],
47105
+ "pipelineOrchestrator.getDeviceLiveContribution": [{
47106
+ name: "deviceId",
47107
+ form: "single",
47108
+ optional: false
47109
+ }],
47110
+ "pipelineOrchestrator.getDeviceSettingsContribution": [{
47111
+ name: "deviceId",
47112
+ form: "single",
47113
+ optional: false
47114
+ }],
46663
47115
  "pipelineOrchestrator.getPipelineAssignment": [{
46664
47116
  name: "deviceId",
46665
47117
  form: "single",
@@ -46780,11 +47232,31 @@ Object.freeze({
46780
47232
  form: "single",
46781
47233
  optional: true
46782
47234
  }],
47235
+ "powerMeter.getStatus": [{
47236
+ name: "deviceId",
47237
+ form: "single",
47238
+ optional: false
47239
+ }],
47240
+ "presence.getStatus": [{
47241
+ name: "deviceId",
47242
+ form: "single",
47243
+ optional: false
47244
+ }],
47245
+ "pressureSensor.getStatus": [{
47246
+ name: "deviceId",
47247
+ form: "single",
47248
+ optional: false
47249
+ }],
46783
47250
  "privacyMask.getOptions": [{
46784
47251
  name: "deviceId",
46785
47252
  form: "single",
46786
47253
  optional: false
46787
47254
  }],
47255
+ "privacyMask.getStatus": [{
47256
+ name: "deviceId",
47257
+ form: "single",
47258
+ optional: false
47259
+ }],
46788
47260
  "privacyMask.setAudioEnabled": [{
46789
47261
  name: "deviceId",
46790
47262
  form: "single",
@@ -46835,6 +47307,11 @@ Object.freeze({
46835
47307
  form: "single",
46836
47308
  optional: false
46837
47309
  }],
47310
+ "ptz.getStatus": [{
47311
+ name: "deviceId",
47312
+ form: "single",
47313
+ optional: false
47314
+ }],
46838
47315
  "ptz.goHome": [{
46839
47316
  name: "deviceId",
46840
47317
  form: "single",
@@ -46925,6 +47402,11 @@ Object.freeze({
46925
47402
  form: "single",
46926
47403
  optional: false
46927
47404
  }],
47405
+ "recordingArchive.applyDeviceSettingsPatch": [{
47406
+ name: "deviceId",
47407
+ form: "single",
47408
+ optional: false
47409
+ }],
46928
47410
  "recordingArchive.deleteFootprint": [{
46929
47411
  name: "deviceId",
46930
47412
  form: "single",
@@ -46945,6 +47427,21 @@ Object.freeze({
46945
47427
  form: "single",
46946
47428
  optional: false
46947
47429
  }],
47430
+ "recordingArchive.getDeviceLiveContribution": [{
47431
+ name: "deviceId",
47432
+ form: "single",
47433
+ optional: false
47434
+ }],
47435
+ "recordingArchive.getDeviceSettingsContribution": [{
47436
+ name: "deviceId",
47437
+ form: "single",
47438
+ optional: false
47439
+ }],
47440
+ "recordingArchive.getStatus": [{
47441
+ name: "deviceId",
47442
+ form: "single",
47443
+ optional: false
47444
+ }],
46948
47445
  "recordingArchive.listOpsLog": [{
46949
47446
  name: "deviceId",
46950
47447
  form: "single",
@@ -47030,6 +47527,11 @@ Object.freeze({
47030
47527
  form: "single",
47031
47528
  optional: false
47032
47529
  }],
47530
+ "recordingOnboard.getStatus": [{
47531
+ name: "deviceId",
47532
+ form: "single",
47533
+ optional: false
47534
+ }],
47033
47535
  "recordingOnboard.setSettings": [{
47034
47536
  name: "deviceId",
47035
47537
  form: "single",
@@ -47060,6 +47562,11 @@ Object.freeze({
47060
47562
  form: "single",
47061
47563
  optional: false
47062
47564
  }],
47565
+ "sceneMonitor.getStatus": [{
47566
+ name: "deviceId",
47567
+ form: "single",
47568
+ optional: false
47569
+ }],
47063
47570
  "sceneMonitor.listScenes": [{
47064
47571
  name: "deviceId",
47065
47572
  form: "single",
@@ -47085,6 +47592,11 @@ Object.freeze({
47085
47592
  form: "single",
47086
47593
  optional: false
47087
47594
  }],
47595
+ "scriptRunner.getStatus": [{
47596
+ name: "deviceId",
47597
+ form: "single",
47598
+ optional: false
47599
+ }],
47088
47600
  "scriptRunner.run": [{
47089
47601
  name: "deviceId",
47090
47602
  form: "single",
@@ -47095,11 +47607,31 @@ Object.freeze({
47095
47607
  form: "single",
47096
47608
  optional: false
47097
47609
  }],
47610
+ "smoke.getStatus": [{
47611
+ name: "deviceId",
47612
+ form: "single",
47613
+ optional: false
47614
+ }],
47615
+ "snapshot.applyDeviceSettingsPatch": [{
47616
+ name: "deviceId",
47617
+ form: "single",
47618
+ optional: false
47619
+ }],
47098
47620
  "snapshot.getDebugState": [{
47099
47621
  name: "deviceId",
47100
47622
  form: "single",
47101
47623
  optional: false
47102
47624
  }],
47625
+ "snapshot.getDeviceLiveContribution": [{
47626
+ name: "deviceId",
47627
+ form: "single",
47628
+ optional: false
47629
+ }],
47630
+ "snapshot.getDeviceSettingsContribution": [{
47631
+ name: "deviceId",
47632
+ form: "single",
47633
+ optional: false
47634
+ }],
47103
47635
  "snapshot.getSnapshot": [{
47104
47636
  name: "deviceId",
47105
47637
  form: "single",
@@ -47116,6 +47648,11 @@ Object.freeze({
47116
47648
  form: "array",
47117
47649
  optional: false
47118
47650
  }],
47651
+ "snapshot.getStatus": [{
47652
+ name: "deviceId",
47653
+ form: "single",
47654
+ optional: false
47655
+ }],
47119
47656
  "snapshot.invalidateCache": [{
47120
47657
  name: "deviceId",
47121
47658
  form: "single",
@@ -47126,6 +47663,11 @@ Object.freeze({
47126
47663
  form: "single",
47127
47664
  optional: false
47128
47665
  }],
47666
+ "streamBroker.applyDeviceSettingsPatch": [{
47667
+ name: "deviceId",
47668
+ form: "single",
47669
+ optional: false
47670
+ }],
47129
47671
  "streamBroker.assignProfile": [{
47130
47672
  name: "deviceId",
47131
47673
  form: "single",
@@ -47141,6 +47683,16 @@ Object.freeze({
47141
47683
  form: "single",
47142
47684
  optional: false
47143
47685
  }],
47686
+ "streamBroker.getDeviceLiveContribution": [{
47687
+ name: "deviceId",
47688
+ form: "single",
47689
+ optional: false
47690
+ }],
47691
+ "streamBroker.getDeviceSettingsContribution": [{
47692
+ name: "deviceId",
47693
+ form: "single",
47694
+ optional: false
47695
+ }],
47144
47696
  "streamBroker.getStreamWithCodec": [{
47145
47697
  name: "deviceId",
47146
47698
  form: "single",
@@ -47201,16 +47753,46 @@ Object.freeze({
47201
47753
  form: "single",
47202
47754
  optional: false
47203
47755
  }],
47756
+ "streamParams.getStatus": [{
47757
+ name: "deviceId",
47758
+ form: "single",
47759
+ optional: false
47760
+ }],
47204
47761
  "streamParams.setProfile": [{
47205
47762
  name: "deviceId",
47206
47763
  form: "single",
47207
47764
  optional: false
47208
47765
  }],
47766
+ "switch.getStatus": [{
47767
+ name: "deviceId",
47768
+ form: "single",
47769
+ optional: false
47770
+ }],
47209
47771
  "switch.setState": [{
47210
47772
  name: "deviceId",
47211
47773
  form: "single",
47212
47774
  optional: false
47213
47775
  }],
47776
+ "tamper.getStatus": [{
47777
+ name: "deviceId",
47778
+ form: "single",
47779
+ optional: false
47780
+ }],
47781
+ "temperatureSensor.getStatus": [{
47782
+ name: "deviceId",
47783
+ form: "single",
47784
+ optional: false
47785
+ }],
47786
+ "update.getStatus": [{
47787
+ name: "deviceId",
47788
+ form: "single",
47789
+ optional: false
47790
+ }],
47791
+ "vacuumControl.getStatus": [{
47792
+ name: "deviceId",
47793
+ form: "single",
47794
+ optional: false
47795
+ }],
47214
47796
  "vacuumControl.locate": [{
47215
47797
  name: "deviceId",
47216
47798
  form: "single",
@@ -47246,6 +47828,11 @@ Object.freeze({
47246
47828
  form: "single",
47247
47829
  optional: false
47248
47830
  }],
47831
+ "valve.getStatus": [{
47832
+ name: "deviceId",
47833
+ form: "single",
47834
+ optional: false
47835
+ }],
47249
47836
  "valve.open": [{
47250
47837
  name: "deviceId",
47251
47838
  form: "single",
@@ -47261,6 +47848,11 @@ Object.freeze({
47261
47848
  form: "single",
47262
47849
  optional: false
47263
47850
  }],
47851
+ "vibration.getStatus": [{
47852
+ name: "deviceId",
47853
+ form: "single",
47854
+ optional: false
47855
+ }],
47264
47856
  "videoclips.dialClipStream": [{
47265
47857
  name: "deviceId",
47266
47858
  form: "single",
@@ -47296,6 +47888,11 @@ Object.freeze({
47296
47888
  form: "single",
47297
47889
  optional: false
47298
47890
  }],
47891
+ "waterHeater.getStatus": [{
47892
+ name: "deviceId",
47893
+ form: "single",
47894
+ optional: false
47895
+ }],
47299
47896
  "waterHeater.setAway": [{
47300
47897
  name: "deviceId",
47301
47898
  form: "single",
@@ -47311,6 +47908,11 @@ Object.freeze({
47311
47908
  form: "single",
47312
47909
  optional: false
47313
47910
  }],
47911
+ "weather.getStatus": [{
47912
+ name: "deviceId",
47913
+ form: "single",
47914
+ optional: false
47915
+ }],
47314
47916
  "webrtcSession.addIceCandidate": [{
47315
47917
  name: "deviceId",
47316
47918
  form: "single",
@@ -48048,7 +48650,7 @@ var PreviewTextField = string().max(200);
48048
48650
  * Whether the notification's preview is a STILL or a short animation.
48049
48651
  *
48050
48652
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
48051
- * rule)"* — the other rules already offer an animated preview, and a ten-hour
48653
+ * rule)"* [sent as a gif or video (as for the other rules)] — the other rules already offer an animated preview, and a ten-hour
48052
48654
  * night reads better as three seconds of motion than as one frame of it. Both
48053
48655
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
48054
48656
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -48186,6 +48788,7 @@ TimelapseRuleInputSchema.extend({
48186
48788
  createdAt: number(),
48187
48789
  updatedAt: number()
48188
48790
  });
48791
+ MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
48189
48792
  object({
48190
48793
  /**
48191
48794
  * Fraction of the box's own size added on EACH side before cutting.