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