@camstack/addon-mqtt-broker 1.2.116 → 1.2.117

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.
@@ -39,7 +39,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
40
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
41
41
  //#endregion
42
- //#region ../types/dist/event-category-C5xZWqz6.mjs
42
+ //#region ../types/dist/event-category-BGmz_oxW.mjs
43
43
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
44
44
  EventCategory["SystemBoot"] = "system.boot";
45
45
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -572,6 +572,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
572
572
  */
573
573
  EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
574
574
  EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
575
+ /**
576
+ * A camera provider's RAW onboard motion push (D673): the `MotionOnMotionChanged`
577
+ * data shape without `triggeredBy`, built by `providerMotionEvent`. Its one
578
+ * consumer is the orchestrator's camera-sensors bridge, which re-emits the
579
+ * camera's EFFECTIVE onboard motion as `MotionOnMotionChanged` (a pass-through
580
+ * for a camera with no attached sensor). Providers must not emit
581
+ * `MotionOnMotionChanged` themselves; `scripts/check-onboard-motion-emitters.ts`
582
+ * keeps it so (camera-grid is a named, deferred exception).
583
+ */
575
584
  EventCategory["ProviderMotion"] = "provider.motion";
576
585
  EventCategory["ProviderDetection"] = "provider.detection";
577
586
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
@@ -5410,7 +5419,7 @@ var ZodIssueCode = {
5410
5419
  var ZodFirstPartyTypeKind;
5411
5420
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5412
5421
  //#endregion
5413
- //#region ../types/dist/sleep-gU6hqv4_.mjs
5422
+ //#region ../types/dist/sleep-CDCONfkt.mjs
5414
5423
  /**
5415
5424
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5416
5425
  * window to float samples (D455).
@@ -7439,7 +7448,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7439
7448
  return DeviceRole;
7440
7449
  }({});
7441
7450
  //#endregion
7442
- //#region ../types/dist/composition-Dq42XWp0.mjs
7451
+ //#region ../types/dist/composition-Bzr7GwgP.mjs
7443
7452
  /**
7444
7453
  * The ffmpeg build every CamStack node downloads, per platform and arch.
7445
7454
  *
@@ -7493,21 +7502,20 @@ function portable(target) {
7493
7502
  }
7494
7503
  portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7495
7504
  /**
7496
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7497
- * valutatore puro del suo FRONTE.
7505
+ * THE table "which boolean of this device type counts as HIGH", and the
7506
+ * pure evaluator of its EDGE.
7498
7507
  *
7499
- * Viveva dentro il builtin virtual-doorbell
7500
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
7501
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
7502
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
7503
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
7504
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
7505
- * SPOSTA qui e il doorbell la ri-esporta.
7508
+ * ONE table for everyone who asks "is this device HIGH?": the
7509
+ * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
7510
+ * sensor markers, the orchestrator's camera-sensors bridge and
7511
+ * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
7512
+ * A second copy would diverge at the first cap added, and the symptom — "the
7513
+ * sensor rings the doorbell but does not record" — is exactly D62.
7506
7514
  *
7507
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
7508
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
7509
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
7510
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
7515
+ * ⚠ NOT `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), and the
7516
+ * two must not be unified: that one answers "what is the state WORD for a
7517
+ * rule" (and includes `presence`, `cover`, `alarm-panel`), this one "which
7518
+ * boolean's EDGE counts". Deliberately different vocabularies.
7511
7519
  */
7512
7520
  /**
7513
7521
  * Known binary / switch source caps → the boolean slice field whose
@@ -7528,6 +7536,8 @@ var SOURCE_CAP_ACTIVE_FIELD = {
7528
7536
  vibration: "detected",
7529
7537
  tamper: "tampered"
7530
7538
  };
7539
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
7540
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
7531
7541
  /**
7532
7542
  * Error types for the safe expression engine. Two distinct classes so callers
7533
7543
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -9114,22 +9124,22 @@ var CLASS_MAP_MACRO_TARGETS = [
9114
9124
  "package"
9115
9125
  ];
9116
9126
  /**
9117
- * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
9118
- * un operatore può selezionare.
9127
+ * The FIRST-LEVEL macro classes: those an object detector emits and that
9128
+ * an operator can select.
9119
9129
  *
9120
- * Sono le tre offerte dallo step `object-detection`
9130
+ * They are the three offered by the `object-detection` step
9121
9131
  * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
9122
- * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
9123
- * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
9124
- * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
9125
- * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
9132
+ * `enabledMacroClasses`). `package` is in {@link CLASS_MAP_MACRO_TARGETS} and in
9133
+ * `MACRO_LABELS` — it is a true macro — but NOT here: it belongs to the step
9134
+ * `package-detection`, whose enablement is driven by zones, and offering the
9135
+ * same word twice has already made an operator turn on the COCO proxy
9126
9136
  * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
9127
9137
  *
9128
- * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
9129
- * dello step e una seconda volta come union `FirstLevelMacro`
9130
- * (`types/detection.ts`); una terza copia per il trigger di registrazione
9131
- * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
9132
- * successiva.
9138
+ * ONE list. Before today the same three were hand-written in the step's offer
9139
+ * and a second time as the union `FirstLevelMacro`
9140
+ * (`types/detection.ts`); a third copy for the recording trigger
9141
+ * (`RecordingTriggers.objectClasses`) would have made the later divergence
9142
+ * invisible.
9133
9143
  */
9134
9144
  var FIRST_LEVEL_MACRO_CLASSES = [
9135
9145
  "person",
@@ -9194,17 +9204,17 @@ var RecordingStorageModeSchema = _enum([
9194
9204
  "on-device-decision"
9195
9205
  ]);
9196
9206
  /**
9197
- * Le macro classi che possono aprire una finestra di registrazione — le stesse
9198
- * tre offerte dallo step `object-detection`, da UNA lista
9207
+ * The macro classes that can open a recording window — the same
9208
+ * three offered by the `object-detection` step, from ONE list
9199
9209
  * ({@link FIRST_LEVEL_MACRO_CLASSES}).
9200
9210
  */
9201
9211
  var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
9202
9212
  /**
9203
- * True quando `values` non ripete un elemento.
9213
+ * True when `values` does not repeat an element.
9204
9214
  *
9205
- * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
9206
- * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
9207
- * conterebbe due volte le sue finestre in `segmentMissedByMs`.
9215
+ * A duplicate is not harmless: every entry of `objectClasses` / `sensorDeviceIds`
9216
+ * becomes a SOURCE in `bandTriggerSources`, and the same source twice
9217
+ * would count its windows twice in `segmentMissedByMs`.
9208
9218
  */
9209
9219
  var noDuplicates = (values) => new Set(values).size === values.length;
9210
9220
  /** Which detectors trigger an `events`-mode band. */
@@ -9212,34 +9222,34 @@ var RecordingTriggersSchema = object({
9212
9222
  motion: boolean().optional(),
9213
9223
  audioThresholdDbfs: number().optional(),
9214
9224
  /**
9215
- * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
9216
- * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
9217
- * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
9218
- * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
9225
+ * The macro classes whose detection opens a window. ABSENT = the source
9226
+ * is not listened to; an EMPTY array is rejected, because "events band, object
9227
+ * trigger on, no class" is the same shape "enabled and records
9228
+ * nothing, forever" that `eventsBandCanEverDemand` is written against.
9219
9229
  *
9220
- * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
9221
- * quelle che hanno attraversato `enabledMacroClasses`, i
9222
- * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
9223
- * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
9224
- * finestre — vedi `recorder/object-trigger.ts`.
9230
+ * The signal read is ALREADY FILTERED: only `source: 'pipeline'` detections, i.e.
9231
+ * those that went through `enabledMacroClasses`, the
9232
+ * `minConfidence<Macro>` and the full-frame guard. The camera's onboard AI
9233
+ * (`source: 'onboard'`) goes through none of those gates and does NOT open
9234
+ * windows — see `recorder/object-trigger.ts`.
9225
9235
  */
9226
9236
  objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
9227
9237
  /**
9228
- * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
9229
- * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
9238
+ * The LINKED devices whose RISING EDGE opens a window. Absent = the source
9239
+ * is not listened to; an empty array is rejected for the same reason as
9230
9240
  * `objectClasses`.
9231
9241
  *
9232
- * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
9233
- * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
9234
- * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
9235
- * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9236
- * device (D12) — mai un elenco globale di cap.
9242
+ * These are SOURCE device ids, not cameras: the band names them, so the
9243
+ * hot path (`DeviceStateChanged`, at bus rate across the whole fleet)
9244
+ * does no RPC. The OFFER from which the operator picks them is another question, and
9245
+ * is resolved with `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9246
+ * device (D12) — never a global list of caps.
9237
9247
  *
9238
- * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
9239
- * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
9240
- * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
9241
- * il livello: un contatto trovato già aperto al riavvio del runner non fa
9242
- * registrare.
9248
+ * What "high" means depends on the device TYPE and is not decided here:
9249
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) is THE table,
9250
+ * the same one as the composite doorbell (D671). And it is the EDGE, not
9251
+ * the level: a contact found already open at runner restart does not
9252
+ * record.
9243
9253
  */
9244
9254
  sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
9245
9255
  });
@@ -10316,7 +10326,7 @@ var DecoderSessionConfigSchema = object({
10316
10326
  * - **VALUE** — at which level, for which scope, until when. That stays ONE
10317
10327
  * thing: the logging settings document on the `system` cap. Two authorities
10318
10328
  * over the values is the exact defect
10319
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
10329
+ * `docs/design/plans/2026-08-26-per-component-logging.md` was written to
10320
10330
  * remove; re-introducing it from the cure side would be grotesque.
10321
10331
  *
10322
10332
  * Nothing in this file reads a clock, an env var or a store. The registry is
@@ -12027,7 +12037,7 @@ var BrokerStatusEnum = _enum([
12027
12037
  "unreachable",
12028
12038
  "error"
12029
12039
  ]);
12030
- var BrokerInfoSchema$1 = object({
12040
+ var BrokerInfoSchema = object({
12031
12041
  /** Stable broker id. Persisted; survives addon restarts. */
12032
12042
  id: string(),
12033
12043
  /** Addon id of the provider that OWNS this broker.
@@ -12170,8 +12180,8 @@ var brokerCapability = {
12170
12180
  kind: "poll"
12171
12181
  },
12172
12182
  methods: {
12173
- list: method(ListInputSchema, array(BrokerInfoSchema$1)),
12174
- get: method(GetInputSchema, BrokerInfoSchema$1.nullable()),
12183
+ list: method(ListInputSchema, array(BrokerInfoSchema)),
12184
+ get: method(GetInputSchema, BrokerInfoSchema.nullable()),
12175
12185
  /** Enumerate which addon provides which broker kind(s) for the
12176
12186
  * unified create picker. The auto-mount fans this array across
12177
12187
  * every registered `broker` provider (array-output method), so the
@@ -13818,7 +13828,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13818
13828
  *
13819
13829
  * ⚠ An earlier version of this comment blamed Zod stripping, and that
13820
13830
  * was wrong — corrected 2026-08-26 after the hop map
13821
- * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
13831
+ * (`docs/design/2026-08-26-topic-hop-map.md`) traced the path.
13822
13832
  * There is **no Zod parse at all** between the door and the engine: the
13823
13833
  * dispatcher forwards the payload verbatim and UDS carries it whole. A
13824
13834
  * field declared here reaches `SqliteSettingsBackend` either way.
@@ -14574,7 +14584,17 @@ var SiteZoneSchema = object({
14574
14584
  var ProviderStatusSchema = object({
14575
14585
  connected: boolean(),
14576
14586
  deviceCount: number(),
14577
- error: string().optional()
14587
+ error: string().optional(),
14588
+ /**
14589
+ * `'orchestrator'`: this provider writes NO camera's `motion` runtime slice
14590
+ * — the orchestrator is its only writer (D696). Set by `BaseDeviceProvider`,
14591
+ * which every provider bundles, so only a provider BUILT from D696-amended
14592
+ * code says it. ABSENT means an older provider that may still write it (raw
14593
+ * VMD writes, a boot seed), and the orchestrator's provider gate keeps that
14594
+ * provider's cameras unwritten (`camera-sensors/motion-slice-provider-gate.ts`)
14595
+ * — the provider twin of the runner's `getLocalLoad().motionSliceWriter`.
14596
+ */
14597
+ motionSliceWriter: literal("orchestrator").optional()
14578
14598
  });
14579
14599
  object({
14580
14600
  externalId: string(),
@@ -15372,28 +15392,10 @@ method(object({
15372
15392
  deviceIds: array(number()).readonly(),
15373
15393
  caps: array(string()).readonly().optional()
15374
15394
  }), record(string(), record(string(), unknown().nullable())));
15375
- object({
15376
- /** Ms epoch of the last press. null = never observed since this provider started. */
15377
- lastPressedAt: number().nullable(),
15378
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
15379
- pressCountSinceStart: number()
15380
- });
15381
- object({
15382
- deviceId: number(),
15383
- timestamp: number()
15384
- });
15385
- /**
15386
- * The `doorbell-source` extension slot's editable value (D648): the camera's
15387
- * virtual-doorbell trigger devices, exactly what the wrapper's
15388
- * `doorbellSources` persists (as `{ deviceId }` records).
15389
- */
15390
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
15395
+ var ExtensionSlotIdSchema = _enum(["linked-devices"]);
15391
15396
  DeviceType.Camera;
15392
- DeviceType.Button, DeviceType.Camera;
15393
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
15394
- DeviceType.Camera, DeviceType.Camera;
15395
15397
  /** A slot's editable value, discriminated on `slot`. */
15396
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15398
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15397
15399
  DeviceType.Camera;
15398
15400
  var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
15399
15401
  state: literal("unreachable"),
@@ -15430,10 +15432,11 @@ var ExtensionSlotSourceSchema = object({
15430
15432
  });
15431
15433
  /**
15432
15434
  * A DIFFERENT authority that gates whether the slot's sources do anything —
15433
- * reported read-only, named, never written through this contract (D62). The
15434
- * virtual doorbell's sources ring only while the wrapper is bound to the
15435
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
15436
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
15435
+ * reported read-only, named, never written through this contract (D62) — e.g.
15436
+ * a slot whose sources act only while a wrapper is bound to the host, a
15437
+ * binding that belongs to `deviceManager.setWrapperActive`. No slot reports
15438
+ * one today (`linked-devices` answers `null`). `unknown` means the gate could
15439
+ * not be read; it is not "inactive" (D315).
15437
15440
  */
15438
15441
  var ExtensionSlotActivationSchema = object({
15439
15442
  state: _enum([
@@ -15449,7 +15452,7 @@ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
15449
15452
  /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
15450
15453
  slotRef: string(),
15451
15454
  slot: ExtensionSlotIdSchema,
15452
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
15455
+ /** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
15453
15456
  authority: string(),
15454
15457
  activation: ExtensionSlotActivationSchema.nullable(),
15455
15458
  value: ExtensionSlotValueSchema,
@@ -15727,7 +15730,7 @@ method(object({
15727
15730
  * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
15728
15731
  * ends in a log line, and a log line is the thing the operator asked to stop
15729
15732
  * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
15730
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
15733
+ * 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
15731
15734
  * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
15732
15735
  * media blackout were both diagnosed. The counters stay; this is where they can
15733
15736
  * be READ.
@@ -17103,7 +17106,7 @@ var BrokerStatusSchema$1 = _enum([
17103
17106
  "unreachable",
17104
17107
  "tls-error"
17105
17108
  ]);
17106
- var BrokerInfoSchema = object({
17109
+ var BrokerInfoSchema$1 = object({
17107
17110
  id: string(),
17108
17111
  name: string(),
17109
17112
  url: string(),
@@ -17176,7 +17179,7 @@ var mqttBrokerCapability = {
17176
17179
  kind: "poll"
17177
17180
  },
17178
17181
  methods: {
17179
- listBrokers: method(_void(), array(BrokerInfoSchema)),
17182
+ listBrokers: method(_void(), array(BrokerInfoSchema$1)),
17180
17183
  getBrokerConfig: method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }),
17181
17184
  addBroker: method(AddBrokerInputSchema, AddBrokerResultSchema, {
17182
17185
  kind: "mutation",
@@ -19361,78 +19364,16 @@ var NcRuleInputSchema = object({
19361
19364
  */
19362
19365
  confirm: NcConfirmSchema.optional(),
19363
19366
  /**
19364
- * WAIT for face/plate recognition before saying anything.
19365
- *
19366
- * A notification's TEXT is frozen at enqueue and its media is re-resolved at
19367
- * send; the identity is neither. A face is confirmed after `confirmFrames`
19368
- * agreeing observations — p50 **11.4 s** after the track was first seen,
19369
- * measured on this hub — and an `immediate` rule enqueues on the first object
19370
- * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
19371
- * immediate path, and no amount of media re-resolution fixes a sentence.
19372
- *
19373
- * Only two honest answers exist, and this flag picks between them. It has
19374
- * effect ONLY on a rule that declares a recognition scope
19375
- * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
19376
- * other rule there is nothing to wait for and the flag is inert.
19377
- *
19378
- * | value | what happens |
19379
- * | --- | --- |
19380
- * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
19381
- * | 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) |
19382
- *
19383
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19384
- * on the addon cap path, and absent has to keep meaning exactly what every
19385
- * rule authored before this field meant.
19386
- *
19387
- * The cost of `true` is stated here because the editor states it too: a rule
19388
- * that waits also inherits track-close SEMANTICS — its `zones` condition
19389
- * tests every zone the track visited and a `crossing` condition can no longer
19390
- * be satisfied, because a closed track carries no crossing.
19391
- */
19392
- waitForEnhancement: boolean().optional(),
19393
- /**
19394
- * GROUP a burst of subjects into ONE notification that grows.
19395
- *
19396
- * Seconds of quiet after the last matching subject before the burst is
19397
- * considered over. While it is open, the first subject enqueues immediately —
19398
- * **exactly as today, with no added latency** — and every real growth (a new
19399
- * subject, or a name confirmed on one already in it) REPLACES that
19400
- * notification with an updated one naming everybody. The push carries the
19401
- * group's own coalescing tag, so the phone replaces rather than stacks.
19402
- *
19403
- * `0` / absent = off, and off is today's behaviour byte for byte.
19404
- *
19405
- * ### Why an idle cutoff and not a window
19406
- *
19407
- * The measured seven-person arrival on device 590 spans 110 s with every
19408
- * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
19409
- * idle cutoff holds it as one and ends it when the arrival actually ends.
19410
- * 30 s is a widely shipped value for the same decision.
19411
- *
19412
- * ### What it replaces
19413
- *
19414
- * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
19415
- * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
19416
- * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
19417
- * track that did fire and 7 carrying a confirmed identity nobody heard about.
19418
- * A group collapses the same volume by MERGING, so the cooldown becomes a
19419
- * budget over GROUPS — which is what it always meant — and a growth is never
19420
- * throttled by the window its own first member spent.
19421
- *
19422
- * ### Interaction with {@link waitForEnhancement}
19423
- *
19424
- * They compose, and the order matters. `waitForEnhancement` defers the rule to
19425
- * TRACK CLOSE, so with both set the group is opened by the first member to
19426
- * CLOSE — already carrying its name — and grows as later members close. That
19427
- * is later, and complete. With grouping alone the group opens on the first
19428
- * object event and picks up names as they are confirmed, through the growth
19429
- * path. Neither combination fires twice for one subject.
19430
- *
19431
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19432
- * on the addon cap path, so absent must keep meaning what it meant before this
19433
- * field existed.
19367
+ * IMMEDIATE GROUPED (D692): the first matching subject opens a window of this many
19368
+ * seconds; the rule sends ONE message when the window closes — early, once every
19369
+ * subject in it is final, never before {@link NC_GROUPING_MIN_SEC} — naming everybody
19370
+ * the rule matched, with one mosaic tile per subject.
19371
+ * Absent = plain immediate (one message per track, at the first match).
19372
+ * Only an `immediate` DETECTION rule may carry it (`ruleGroupingApplies`, enforced on
19373
+ * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
19374
+ * path does not run Zod defaults.
19434
19375
  */
19435
- groupIdleSec: number().int().min(0).max(600).optional()
19376
+ groupingSec: number().int().min(5).max(60).optional()
19436
19377
  });
19437
19378
  /**
19438
19379
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -19480,9 +19421,10 @@ var NcRuleClearableKeySchema = _enum([
19480
19421
  "targetUsers",
19481
19422
  "snoozeOptions",
19482
19423
  "snoozeAllowGlobal",
19483
- "waitForEnhancement",
19484
19424
  "actions",
19485
19425
  "confirm",
19426
+ "groupingSec",
19427
+ "waitForEnhancement",
19486
19428
  "groupIdleSec"
19487
19429
  ]);
19488
19430
  var NcRuleUpdateInputSchema = object({
@@ -19504,9 +19446,25 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19504
19446
  */
19505
19447
  disabledTargetIds: array(string()).default([])
19506
19448
  });
19449
+ /**
19450
+ * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19451
+ * still-true state. `backups-exports` and `alarm` are not sections — a completed
19452
+ * job is not an open state, and the alarm stays instant.
19453
+ */
19454
+ var NcStateSummarySectionIdSchema = _enum([
19455
+ "device-state",
19456
+ "streams",
19457
+ "battery",
19458
+ "consumables",
19459
+ "tanks",
19460
+ "detection-blind",
19461
+ "nodes",
19462
+ "addons",
19463
+ "updates"
19464
+ ]);
19507
19465
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
19508
19466
  var NcStateSummaryPreviewSectionSchema = object({
19509
- id: string(),
19467
+ id: NcStateSummarySectionIdSchema,
19510
19468
  status: _enum(["read", "unavailable"]),
19511
19469
  items: number().int().nonnegative(),
19512
19470
  reason: string().optional()
@@ -19516,7 +19474,7 @@ var NcStateSummaryPreviewSectionSchema = object({
19516
19474
  * a `rule` split (no `section`), one section's on a `section` split.
19517
19475
  */
19518
19476
  var NcStateSummaryPreviewRowSchema = object({
19519
- section: string().optional(),
19477
+ section: NcStateSummarySectionIdSchema.optional(),
19520
19478
  /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
19521
19479
  title: string(),
19522
19480
  text: string(),
@@ -19953,6 +19911,29 @@ object({
19953
19911
  firstAt: number(),
19954
19912
  lastAt: number()
19955
19913
  });
19914
+ var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
19915
+ var NcAlarmDelaySecSchema = number().int().min(0).max(600);
19916
+ var NcAlarmTriggeredSecSchema = number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
19917
+ var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
19918
+ var NcAlarmModeDelaysListSchema = array(object({
19919
+ mode: AlarmArmModeSchema,
19920
+ exitDelaySec: NcAlarmDelaySecSchema.optional(),
19921
+ entryDelaySec: NcAlarmDelaySecSchema.optional(),
19922
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
19923
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
19924
+ var NcAlarmModeDelaysEditListSchema = array(object({
19925
+ mode: AlarmArmModeSchema,
19926
+ exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
19927
+ entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
19928
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
19929
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
19930
+ /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
19931
+ var NcAlarmNonBlockingEditSchema = object({
19932
+ mode: AlarmArmModeSchema,
19933
+ deviceId: number().int().positive(),
19934
+ /** true = on the list (may be open, does not block); false = off the list (blocks). */
19935
+ nonBlocking: boolean()
19936
+ });
19956
19937
  /**
19957
19938
  * The per-mode lists, at most one per mode. Two lists for one mode have no
19958
19939
  * meaning a reader could agree on (union? the last one?), so they are refused
@@ -19961,10 +19942,10 @@ object({
19961
19942
  var NcAlarmNonBlockingListSchema = array(object({
19962
19943
  mode: AlarmArmModeSchema,
19963
19944
  deviceIds: array(number().int()).max(200)
19964
- })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
19945
+ })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
19965
19946
  /**
19966
- * The three durations the panel's state machine runs on, who hears about its
19967
- * transitions, and which openings each mode tolerates.
19947
+ * The three durations — global, and per mode since D697 — the panel's state
19948
+ * machine runs on, who hears about its transitions, and which openings each mode tolerates.
19968
19949
  *
19969
19950
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
19970
19951
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -19974,15 +19955,15 @@ var NcAlarmNonBlockingListSchema = array(object({
19974
19955
  */
19975
19956
  var NcAlarmSettingsSchema = object({
19976
19957
  /** Grace period between arming and the mode taking effect. 0 = immediate. */
19977
- exitDelaySec: number().int().min(0).max(600),
19958
+ exitDelaySec: NcAlarmDelaySecSchema,
19978
19959
  /** Grace period between a trigger and the alarm firing. 0 = immediate. */
19979
- entryDelaySec: number().int().min(0).max(600),
19960
+ entryDelaySec: NcAlarmDelaySecSchema,
19980
19961
  /**
19981
19962
  * How long `triggered` lasts before the panel re-arms itself.
19982
19963
  * **0 = until an operator disarms it** — the behaviour before this field
19983
19964
  * existed, and therefore what an untouched install keeps doing.
19984
19965
  */
19985
- triggeredDurationSec: number().int().min(0).max(3600),
19966
+ triggeredDurationSec: NcAlarmTriggeredSecSchema,
19986
19967
  /**
19987
19968
  * Deprecated by D630 — every transition is announced. Parsed, never read;
19988
19969
  * remove after one release. Optional so an older admin's patch (which still
@@ -20005,17 +19986,30 @@ var NcAlarmSettingsSchema = object({
20005
19986
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
20006
19987
  * parses as "every opening blocks" — the behaviour it was written under.
20007
19988
  */
20008
- nonBlocking: NcAlarmNonBlockingListSchema.default([])
19989
+ nonBlocking: NcAlarmNonBlockingListSchema.default([]),
19990
+ /**
19991
+ * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
19992
+ * without a field, uses the global value above — so `[]`, the default, is exactly the
19993
+ * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
19994
+ */
19995
+ modeDelays: NcAlarmModeDelaysListSchema.default([])
20009
19996
  });
20010
19997
  /**
20011
19998
  * Every field optional — a tab edits one control at a time.
20012
19999
  *
20013
- * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
20014
- * remove an inner `.default()`, so a patch that never named the field would
20015
- * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
20016
- * (the same trap {@link NcRulePatchSchema} documents).
20000
+ * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
20001
+ * `.partial()` does not remove an inner `.default()`, so a patch that never
20002
+ * named the field would parse to `[]` and wipe every mode's list on an
20003
+ * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
20004
+ * `modeDelays` means EDITS, not the stored overrides.
20017
20005
  */
20018
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
20006
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
20007
+ nonBlocking: NcAlarmNonBlockingListSchema,
20008
+ /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
20009
+ modeDelays: NcAlarmModeDelaysEditListSchema,
20010
+ /** ONE membership change; the panel applies it to the list it holds at its turn. */
20011
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema
20012
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
20019
20013
  /**
20020
20014
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
20021
20015
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -20115,6 +20109,28 @@ var NcAlarmConfigSchema = object({
20115
20109
  */
20116
20110
  live: NcAlarmLiveSchema.optional()
20117
20111
  });
20112
+ var NcAlarmSensorReadingSchema = _enum([
20113
+ "open",
20114
+ "closed",
20115
+ "unknown",
20116
+ "not-opening"
20117
+ ]);
20118
+ /** One device's state as the alarm reads it — the classifier the arm check uses. */
20119
+ var NcAlarmSensorStateSchema = object({
20120
+ deviceId: number().int().positive(),
20121
+ reading: NcAlarmSensorReadingSchema,
20122
+ /** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
20123
+ state: string().nullable(),
20124
+ /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
20125
+ unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
20126
+ /** The device manager's liveness as the centre mirrors it; null = not known. */
20127
+ online: boolean().nullable()
20128
+ });
20129
+ var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
20130
+ var NcAlarmSensorsSchema = object({
20131
+ sensors: array(NcAlarmSensorStateSchema),
20132
+ readAt: number().int().nonnegative()
20133
+ });
20118
20134
  /**
20119
20135
  * ONE rule's demand on ONE camera's clip ring.
20120
20136
  *
@@ -20205,7 +20221,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
20205
20221
  }), method(object({}), NcAlarmConfigSchema, { auth: "admin" }), method(object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
20206
20222
  kind: "mutation",
20207
20223
  auth: "admin"
20208
- });
20224
+ }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
20209
20225
  /**
20210
20226
  * Zod schemas for persisted record types.
20211
20227
  *
@@ -20552,12 +20568,26 @@ var CameraSensorRefSchema = object({
20552
20568
  function cameraSensorRefKey(ref) {
20553
20569
  return `${ref.addonId}/${ref.stableId}`;
20554
20570
  }
20555
- /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20556
- var CameraSensorsModeSchema = _enum(["in-addition"]);
20557
- var CameraSensorsConfigSchema = object({
20558
- mode: CameraSensorsModeSchema,
20559
- sensors: array(CameraSensorRefSchema).min(1).max(8)
20560
- }).superRefine((value, ctx) => {
20571
+ /**
20572
+ * A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
20573
+ * `replace`: its motion is the camera's, and while one is READY the camera's
20574
+ * own motion no longer counts on its own. `confirm`: while one is READY the
20575
+ * camera's own motion counts only while a confirm sensor is active — with or
20576
+ * without a replace sensor (spec C2, the controller's ruling on Q1).
20577
+ */
20578
+ var CameraSensorModeSchema = _enum([
20579
+ "in-addition",
20580
+ "confirm",
20581
+ "replace"
20582
+ ]);
20583
+ /** A stored sensor with no mode — every CS-1 row — is in addition. */
20584
+ var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
20585
+ /**
20586
+ * A camera's attached sensors. A CS-1 row carried an envelope
20587
+ * `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
20588
+ * what the write stores (D672 rule 3), so it is never written again.
20589
+ */
20590
+ var CameraSensorsConfigSchema = object({ sensors: array(CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) })).min(1).max(8) }).superRefine((value, ctx) => {
20561
20591
  const seen = /* @__PURE__ */ new Set();
20562
20592
  value.sensors.forEach((ref, index) => {
20563
20593
  const key = cameraSensorRefKey(ref);
@@ -20569,39 +20599,41 @@ var CameraSensorsConfigSchema = object({
20569
20599
  seen.add(key);
20570
20600
  });
20571
20601
  });
20572
- var RefFields = {
20602
+ /** Every attachment names its mode; an older orchestrator that does not is in addition. */
20603
+ var AttachmentFields = {
20573
20604
  addonId: string(),
20574
- stableId: string()
20605
+ stableId: string(),
20606
+ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
20575
20607
  };
20576
20608
  var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20577
20609
  object({
20578
- ...RefFields,
20610
+ ...AttachmentFields,
20579
20611
  state: literal("ready"),
20580
20612
  deviceId: number(),
20581
20613
  name: string(),
20582
20614
  caps: array(string()).readonly()
20583
20615
  }),
20584
20616
  object({
20585
- ...RefFields,
20617
+ ...AttachmentFields,
20586
20618
  state: literal("offline"),
20587
20619
  deviceId: number(),
20588
20620
  name: string(),
20589
20621
  reason: _enum(["offline", "disabled"])
20590
20622
  }),
20591
20623
  object({
20592
- ...RefFields,
20624
+ ...AttachmentFields,
20593
20625
  state: literal("missing"),
20594
20626
  reason: string()
20595
20627
  }),
20596
20628
  object({
20597
- ...RefFields,
20629
+ ...AttachmentFields,
20598
20630
  state: literal("unsupported"),
20599
20631
  deviceId: number(),
20600
20632
  name: string(),
20601
20633
  reason: string()
20602
20634
  }),
20603
20635
  object({
20604
- ...RefFields,
20636
+ ...AttachmentFields,
20605
20637
  state: literal("unreadable"),
20606
20638
  reason: string()
20607
20639
  })
@@ -20614,17 +20646,49 @@ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20614
20646
  * say.
20615
20647
  */
20616
20648
  var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20649
+ /** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
20650
+ var CameraOwnMotionSchema = _enum([
20651
+ "always",
20652
+ "when-confirmed",
20653
+ "never"
20654
+ ]);
20655
+ /** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
20656
+ var CameraSensorsNotEnforcedSchema = _enum(["provider-not-split"]);
20657
+ /**
20658
+ * The camera's OWN onboard motion (D696): the raw input the bridge combines,
20659
+ * never the effective value. The camera's `motion` slice is the effective one,
20660
+ * so a view of the camera's own row reads this.
20661
+ * - `atMs`: when the bridge received the camera's last own edge.
20662
+ * - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
20663
+ * AND it is younger than the window the bridge counts the camera's own motion
20664
+ * for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
20665
+ * sends a clear (heartbeats only) therefore reads `false` once no push came
20666
+ * within the window; a status read is a snapshot, so a live view re-asks.
20667
+ * Absent = this orchestrator has not received the camera's own motion since it
20668
+ * started (unknown, never "quiet"), or an orchestrator older than D696.
20669
+ */
20670
+ var CameraRawLevelSchema = object({
20671
+ detected: boolean(),
20672
+ atMs: number()
20673
+ });
20617
20674
  var CameraSensorsStatusSchema = discriminatedUnion("state", [
20618
- object({ state: literal("not-configured") }),
20675
+ object({
20676
+ state: literal("not-configured"),
20677
+ cameraLevel: CameraRawLevelSchema.optional()
20678
+ }),
20619
20679
  object({
20620
20680
  state: literal("unreadable"),
20621
- reason: string()
20681
+ reason: string(),
20682
+ cameraLevel: CameraRawLevelSchema.optional()
20622
20683
  }),
20623
20684
  object({
20624
20685
  state: literal("configured"),
20625
- mode: CameraSensorsModeSchema,
20686
+ cameraLevel: CameraRawLevelSchema.optional(),
20626
20687
  sensors: array(CameraSensorAttachmentSchema).readonly(),
20627
- notForwarded: CameraSensorsNotForwardedSchema.optional()
20688
+ /** Absent = an orchestrator older than CS-2: it does not say (D315). */
20689
+ cameraMotion: CameraOwnMotionSchema.optional(),
20690
+ notForwarded: CameraSensorsNotForwardedSchema.optional(),
20691
+ notEnforced: CameraSensorsNotEnforcedSchema.optional()
20628
20692
  })
20629
20693
  ]);
20630
20694
  Object.freeze({
@@ -21309,14 +21373,14 @@ var TrackSchema = object({
21309
21373
  * `derive-returned-null tile=standin`,
21310
21374
  * `native-tile tile=native no-key-frame-at-close`, …) written at close and
21311
21375
  * CLEARED by the late-keyFrame upgrade when a native tile lands after all.
21312
- * The operator-facing answer to "perché manca l'immagine?" on a track whose
21376
+ * The operator-facing answer to "perché manca l'immagine?" [why is the image missing?] on a track whose
21313
21377
  * tile is a face/plate stand-in, a raster crop, or an icon.
21314
21378
  *
21315
21379
  * **`no-key-frame-at-close` (D400).** The line used to be written ONLY when
21316
21380
  * the TILE ranked below native, so a track whose thumbnail landed natively
21317
21381
  * while its key frame missed carried no reason at all — three of the four
21318
21382
  * `MISSING_KEYFRAME` tracks in the 2026-09-07 operator audit were exactly
21319
- * that shape, and their "niente ultimo" had no cause anywhere on the row.
21383
+ * that shape, and their "niente ultimo" [no last frame] had no cause anywhere on the row.
21320
21384
  * The key frame is the head of a cascade (`keyFrame ⇒ keyFrameSmall ⇒ the
21321
21385
  * lastFrame copy source`), so its absence is now on this line whatever the
21322
21386
  * tile ended up being.
@@ -21403,7 +21467,7 @@ var MotionEventSchema = object({
21403
21467
  /**
21404
21468
  * Ms offsets from `timestamp` (the episode's own first rising edge, so the
21405
21469
  * first entry is always `0`) of every genuine off→on transition the
21406
- * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
21470
+ * episode saw — "ogni evento on si deve salvare" [every ON event must be saved] (D475). NOT one entry per
21407
21471
  * push: a firmware source that keepalives at ~1 Hz for the whole burst
21408
21472
  * (Reolink, Hikvision) produces exactly one edge; a source that reports an
21409
21473
  * explicit `false` mid-episode and then resumes before the quiet window
@@ -21927,6 +21991,7 @@ var KeyEventSchema = object({
21927
21991
  bestEventId: string(),
21928
21992
  /** Track lifetime in ms (lastSeen - firstSeen). */
21929
21993
  windowMs: number().optional(),
21994
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
21930
21995
  ...TrackFlagFields,
21931
21996
  ...TrackRetrainFields
21932
21997
  });
@@ -23815,8 +23880,10 @@ var RunnerCameraConfigSchema = object({
23815
23880
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
23816
23881
  * analyzer providers) running on decoded frames in this runner.
23817
23882
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
23818
- * analytics). The provider emits ProviderMotion
23819
- * events; the orchestrator forwards to `reportMotion`.
23883
+ * analytics). The provider emits its raw push as `ProviderMotion`
23884
+ * (D673); the orchestrator's camera-sensors bridge re-emits the camera's
23885
+ * effective motion as `MotionOnMotionChanged`, which is what reaches
23886
+ * `reportMotion`.
23820
23887
  */
23821
23888
  motionSources: MotionSourcesSchema.default(["analyzer"]),
23822
23889
  /**
@@ -23993,7 +24060,15 @@ var RunnerLocalLoadSchema = object({
23993
24060
  hasGpu: boolean(),
23994
24061
  inferenceBackend: string().optional(),
23995
24062
  cpuCores: number().optional()
23996
- })
24063
+ }),
24064
+ /**
24065
+ * Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
24066
+ * runner never writes it. ABSENT = a runner older than D696, which still
24067
+ * writes the slice from its phase machine; the orchestrator does not write
24068
+ * while any runner node is absent-or-unanswered, so one slice never has two
24069
+ * writers.
24070
+ */
24071
+ motionSliceWriter: literal("orchestrator").optional()
23997
24072
  });
23998
24073
  /**
23999
24074
  * Aggregate runtime metrics for the runner's whole local pool. Mirrors the
@@ -26629,7 +26704,7 @@ var ClipPlaybackSchema = object({
26629
26704
  * **One authorised yes is one wake.** `next-natural` — take the clip the next
26630
26705
  * time the camera is awake for its own reasons — is deliberately not a member:
26631
26706
  * it was proposed, it is free on the battery, and the operator declined it on
26632
- * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
26707
+ * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."* [When an export is requested the camera wakes up],
26633
26708
  * D558 "Considered and not taken").
26634
26709
  */
26635
26710
  var ClipWakeSchema = _enum(["authorised"]);
@@ -26890,7 +26965,7 @@ DeviceType.Camera, method(object({
26890
26965
  * It replaced `sources?: string[]`, a VIEW filter over source ids that
26891
26966
  * assumed the answer was a fan-out over everything a camera has. The
26892
26967
  * operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
26893
- * solo"* — so the list is asked of one provider at a time and there is
26968
+ * solo"* [there is only one user] — so the list is asked of one provider at a time and there is
26894
26969
  * nothing to filter out of it.
26895
26970
  */
26896
26971
  provider: string().min(1)
@@ -28834,6 +28909,17 @@ object({
28834
28909
  deviceId: number(),
28835
28910
  status: DeviceStatusSchema
28836
28911
  });
28912
+ object({
28913
+ /** Ms epoch of the last press. null = never observed since this provider started. */
28914
+ lastPressedAt: number().nullable(),
28915
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
28916
+ pressCountSinceStart: number()
28917
+ });
28918
+ object({
28919
+ deviceId: number(),
28920
+ timestamp: number()
28921
+ });
28922
+ DeviceType.Button, DeviceType.Camera;
28837
28923
  /**
28838
28924
  * Enum-state sensor — a string value picked from a finite option set.
28839
28925
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -30478,9 +30564,9 @@ object({
30478
30564
  /** Ms epoch of the last detected-true observation. Null if never detected. */
30479
30565
  lastDetectedAt: number().nullable(),
30480
30566
  /**
30481
- * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
30482
- * `null` while false and on every `Sensor` device (D475) — see that
30483
- * constant's doc for the one-authority rule.
30567
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
30568
+ * orchestrator writes it, D696), `null` while false and on every `Sensor`
30569
+ * device (D475) — see that constant's doc for the one-authority rule.
30484
30570
  *
30485
30571
  * ## Reading this field still arms nothing
30486
30572
  *
@@ -30498,38 +30584,41 @@ object({
30498
30584
  * `addon-export-alexa/src/motion-clear-hold.ts` and
30499
30585
  * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
30500
30586
  *
30501
- * ## Who writes it
30587
+ * ## Who writes it (D696)
30502
30588
  *
30503
- * - **Cameras** — the runner's phase machine, `active → watching` on
30504
- * `cooldown_expired`, which then writes this slice with
30505
- * `detected: false` (`handlePhaseChanged` in
30506
- * `pipeline-runner/index.ts`). It produces the FALLING edge, which
30507
- * matters most for the sources that only ever push a rising one:
30508
- * Reolink emits `MotionOnMotionChanged { detected: true }` and never
30509
- * a false.
30510
- * - **Sensors** (Home Assistant binary sensors, Homematic) — the
30511
- * provider pushes the false itself, from the upstream system's own
30512
- * state change. No phase machine is involved.
30589
+ * - **Cameras** — the orchestrator, and nothing else. It writes the camera's
30590
+ * EFFECTIVE motion (`addon-pipeline-orchestrator`
30591
+ * `camera-sensors/motion-slice.ts`), alongside the effective
30592
+ * `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
30593
+ * for a split camera, an unsplit emitter's direct event, the analyzer and
30594
+ * device activity, as the camera's `motionSources` counts them. A source
30595
+ * that never sends a clear falls one window after its last `true`.
30596
+ * `lastDetectedAt` moves only on an effective rise. No camera provider
30597
+ * writes this slice (not even a boot seed), and neither does the runner's
30598
+ * phase machine: its phase is "a session is attached" for every on-motion
30599
+ * session, not motion, and it now has its own `DetectionPhaseTransition`.
30600
+ * A provider's `isDetected` answers from the hub's slice.
30601
+ * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
30602
+ * imports) — the provider pushes both edges itself, from the upstream
30603
+ * system's own state change. That is the sensor's own fact.
30513
30604
  *
30514
- * ### The phase machine is CANONICAL, not sole — and that is a defect
30605
+ * `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
30606
+ * `motion` slice; each sensor writer is a named exemption.
30515
30607
  *
30516
- * An earlier revision of this docblock (mine, 2026-09-12) claimed the
30517
- * phase machine is the sole writer for a camera. It is not.
30518
- * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
30519
- * `setCapSlice(motionCapability, …)` on their own rising edge, and
30520
- * Hikvision's comment says why: it read THIS docblock, agreed the
30521
- * runner is canonical, and wrote anyway to avoid per-tick churn. So
30522
- * two authorities can disagree about one slice, which this repo
30523
- * forbids, and the doc said otherwise — which is worse than saying
30524
- * nothing, because it reads as verification.
30525
- *
30526
- * This predates D475 and is not fixed there: the fix touches every
30527
- * camera provider. Recorded in D475's Consequences. Do not restore the
30528
- * "sole writer" wording without also removing the other writers.
30608
+ * This replaces the D475 §4 rule ("the phase machine is the sole writer").
30609
+ * That rule was never true: Hikvision and Amcrest wrote the slice raw beside
30610
+ * it, every provider seeded it at boot, and every session left it
30611
+ * `detected: true` after its detach.
30529
30612
  */
30530
30613
  autoClearAfterMs: number().nullable()
30531
30614
  });
30532
- object({
30615
+ /**
30616
+ * Payload of `motion.onMotionChanged` event + the corresponding bus
30617
+ * event `EventCategory.MotionOnMotionChanged`. Single source of truth
30618
+ * — both the cap event surface and the bus payload type alias to this
30619
+ * schema.
30620
+ */
30621
+ var MotionOnMotionChangedDataSchema = object({
30533
30622
  deviceId: number(),
30534
30623
  detected: boolean(),
30535
30624
  timestamp: number(),
@@ -32994,7 +33083,7 @@ var ReadGopBytesResultSchema = object({
32994
33083
  /**
32995
33084
  * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
32996
33085
  * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
32997
- * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
33086
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-clip-replay-on-pipeline.md`):
32998
33087
  * a replay needs several seconds of native pixels, not one frame.
32999
33088
  *
33000
33089
  * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
@@ -35610,6 +35699,7 @@ DeviceType.Camera, method(object({
35610
35699
  detection: array(ZoneRuleSchema).readonly(),
35611
35700
  package: array(ZoneRuleSchema).readonly()
35612
35701
  });
35702
+ SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
35613
35703
  /**
35614
35704
  * Accessory device helpers — shared across drivers.
35615
35705
  *
@@ -40588,6 +40678,12 @@ Object.freeze({
40588
40678
  addonId: null,
40589
40679
  access: "view"
40590
40680
  },
40681
+ "notificationRules.getAlarmSensors": {
40682
+ capName: "notification-rules",
40683
+ capScope: "system",
40684
+ addonId: null,
40685
+ access: "view"
40686
+ },
40591
40687
  "notificationRules.getClipRetentionAsks": {
40592
40688
  capName: "notification-rules",
40593
40689
  capScope: "system",
@@ -44166,6 +44262,11 @@ Object.freeze({
44166
44262
  }
44167
44263
  });
44168
44264
  Object.freeze({
44265
+ "accessories.getStatus": [{
44266
+ name: "deviceId",
44267
+ form: "single",
44268
+ optional: false
44269
+ }],
44169
44270
  "accessories.setChildHidden": [{
44170
44271
  name: "childDeviceId",
44171
44272
  form: "single",
@@ -44185,6 +44286,11 @@ Object.freeze({
44185
44286
  form: "single",
44186
44287
  optional: false
44187
44288
  }],
44289
+ "airQualitySensor.getStatus": [{
44290
+ name: "deviceId",
44291
+ form: "single",
44292
+ optional: false
44293
+ }],
44188
44294
  "alarmPanel.arm": [{
44189
44295
  name: "deviceId",
44190
44296
  form: "single",
@@ -44195,11 +44301,36 @@ Object.freeze({
44195
44301
  form: "single",
44196
44302
  optional: false
44197
44303
  }],
44304
+ "alarmPanel.getStatus": [{
44305
+ name: "deviceId",
44306
+ form: "single",
44307
+ optional: false
44308
+ }],
44198
44309
  "alarmPanel.trigger": [{
44199
44310
  name: "deviceId",
44200
44311
  form: "single",
44201
44312
  optional: false
44202
44313
  }],
44314
+ "ambientLightSensor.getStatus": [{
44315
+ name: "deviceId",
44316
+ form: "single",
44317
+ optional: false
44318
+ }],
44319
+ "audioAnalysis.applyDeviceSettingsPatch": [{
44320
+ name: "deviceId",
44321
+ form: "single",
44322
+ optional: false
44323
+ }],
44324
+ "audioAnalysis.getDeviceLiveContribution": [{
44325
+ name: "deviceId",
44326
+ form: "single",
44327
+ optional: false
44328
+ }],
44329
+ "audioAnalysis.getDeviceSettingsContribution": [{
44330
+ name: "deviceId",
44331
+ form: "single",
44332
+ optional: false
44333
+ }],
44203
44334
  "audioAnalysis.resolveDeviceSettings": [{
44204
44335
  name: "deviceId",
44205
44336
  form: "single",
@@ -44240,16 +44371,36 @@ Object.freeze({
44240
44371
  form: "single",
44241
44372
  optional: false
44242
44373
  }],
44374
+ "automationControl.getStatus": [{
44375
+ name: "deviceId",
44376
+ form: "single",
44377
+ optional: false
44378
+ }],
44243
44379
  "automationControl.trigger": [{
44244
44380
  name: "deviceId",
44245
44381
  form: "single",
44246
44382
  optional: false
44247
44383
  }],
44384
+ "battery.getStatus": [{
44385
+ name: "deviceId",
44386
+ form: "single",
44387
+ optional: false
44388
+ }],
44248
44389
  "battery.wakeForStream": [{
44249
44390
  name: "deviceId",
44250
44391
  form: "single",
44251
44392
  optional: false
44252
44393
  }],
44394
+ "binary.getStatus": [{
44395
+ name: "deviceId",
44396
+ form: "single",
44397
+ optional: false
44398
+ }],
44399
+ "brightness.getStatus": [{
44400
+ name: "deviceId",
44401
+ form: "single",
44402
+ optional: false
44403
+ }],
44253
44404
  "brightness.setBrightness": [{
44254
44405
  name: "deviceId",
44255
44406
  form: "single",
@@ -44265,6 +44416,26 @@ Object.freeze({
44265
44416
  form: "single",
44266
44417
  optional: false
44267
44418
  }],
44419
+ "cameraCredentials.getStatus": [{
44420
+ name: "deviceId",
44421
+ form: "single",
44422
+ optional: false
44423
+ }],
44424
+ "cameraGridLayout.applyDeviceSettingsPatch": [{
44425
+ name: "deviceId",
44426
+ form: "single",
44427
+ optional: false
44428
+ }],
44429
+ "cameraGridLayout.getDeviceLiveContribution": [{
44430
+ name: "deviceId",
44431
+ form: "single",
44432
+ optional: false
44433
+ }],
44434
+ "cameraGridLayout.getDeviceSettingsContribution": [{
44435
+ name: "deviceId",
44436
+ form: "single",
44437
+ optional: false
44438
+ }],
44268
44439
  "cameraGridLayout.getLayout": [{
44269
44440
  name: "deviceId",
44270
44441
  form: "single",
@@ -44280,6 +44451,21 @@ Object.freeze({
44280
44451
  form: "single",
44281
44452
  optional: false
44282
44453
  }],
44454
+ "cameraPipelineConfig.applyDeviceSettingsPatch": [{
44455
+ name: "deviceId",
44456
+ form: "single",
44457
+ optional: false
44458
+ }],
44459
+ "cameraPipelineConfig.getDeviceLiveContribution": [{
44460
+ name: "deviceId",
44461
+ form: "single",
44462
+ optional: false
44463
+ }],
44464
+ "cameraPipelineConfig.getDeviceSettingsContribution": [{
44465
+ name: "deviceId",
44466
+ form: "single",
44467
+ optional: false
44468
+ }],
44283
44469
  "cameraStreams.getBrokerStreams": [{
44284
44470
  name: "deviceId",
44285
44471
  form: "single",
@@ -44305,6 +44491,16 @@ Object.freeze({
44305
44491
  form: "single",
44306
44492
  optional: false
44307
44493
  }],
44494
+ "carbonMonoxide.getStatus": [{
44495
+ name: "deviceId",
44496
+ form: "single",
44497
+ optional: false
44498
+ }],
44499
+ "climateControl.getStatus": [{
44500
+ name: "deviceId",
44501
+ form: "single",
44502
+ optional: false
44503
+ }],
44308
44504
  "climateControl.setFanMode": [{
44309
44505
  name: "deviceId",
44310
44506
  form: "single",
@@ -44345,16 +44541,41 @@ Object.freeze({
44345
44541
  form: "single",
44346
44542
  optional: false
44347
44543
  }],
44544
+ "color.getStatus": [{
44545
+ name: "deviceId",
44546
+ form: "single",
44547
+ optional: false
44548
+ }],
44348
44549
  "color.setColor": [{
44349
44550
  name: "deviceId",
44350
44551
  form: "single",
44351
44552
  optional: false
44352
44553
  }],
44554
+ "connectivity.getStatus": [{
44555
+ name: "deviceId",
44556
+ form: "single",
44557
+ optional: false
44558
+ }],
44559
+ "consumables.getStatus": [{
44560
+ name: "deviceId",
44561
+ form: "single",
44562
+ optional: false
44563
+ }],
44353
44564
  "consumables.reset": [{
44354
44565
  name: "deviceId",
44355
44566
  form: "single",
44356
44567
  optional: false
44357
44568
  }],
44569
+ "contact.getStatus": [{
44570
+ name: "deviceId",
44571
+ form: "single",
44572
+ optional: false
44573
+ }],
44574
+ "control.getStatus": [{
44575
+ name: "deviceId",
44576
+ form: "single",
44577
+ optional: false
44578
+ }],
44358
44579
  "control.setValue": [{
44359
44580
  name: "deviceId",
44360
44581
  form: "single",
@@ -44365,6 +44586,11 @@ Object.freeze({
44365
44586
  form: "single",
44366
44587
  optional: false
44367
44588
  }],
44589
+ "cover.getStatus": [{
44590
+ name: "deviceId",
44591
+ form: "single",
44592
+ optional: false
44593
+ }],
44368
44594
  "cover.open": [{
44369
44595
  name: "deviceId",
44370
44596
  form: "single",
@@ -44390,6 +44616,11 @@ Object.freeze({
44390
44616
  form: "single",
44391
44617
  optional: false
44392
44618
  }],
44619
+ "dayNight.getStatus": [{
44620
+ name: "deviceId",
44621
+ form: "single",
44622
+ optional: false
44623
+ }],
44393
44624
  "dayNight.setSettings": [{
44394
44625
  name: "deviceId",
44395
44626
  form: "single",
@@ -44400,11 +44631,31 @@ Object.freeze({
44400
44631
  form: "single",
44401
44632
  optional: true
44402
44633
  }],
44634
+ "detectionPipeline.applyDeviceSettingsPatch": [{
44635
+ name: "deviceId",
44636
+ form: "single",
44637
+ optional: false
44638
+ }],
44639
+ "detectionPipeline.getDeviceLiveContribution": [{
44640
+ name: "deviceId",
44641
+ form: "single",
44642
+ optional: false
44643
+ }],
44644
+ "detectionPipeline.getDeviceSettingsContribution": [{
44645
+ name: "deviceId",
44646
+ form: "single",
44647
+ optional: false
44648
+ }],
44403
44649
  "deviceAdminLink.getAdminLink": [{
44404
44650
  name: "deviceId",
44405
44651
  form: "single",
44406
44652
  optional: false
44407
44653
  }],
44654
+ "deviceAdoption.getStatus": [{
44655
+ name: "deviceId",
44656
+ form: "single",
44657
+ optional: false
44658
+ }],
44408
44659
  "deviceAdoption.release": [{
44409
44660
  name: "camDeviceId",
44410
44661
  form: "single",
@@ -44420,6 +44671,11 @@ Object.freeze({
44420
44671
  form: "single",
44421
44672
  optional: false
44422
44673
  }],
44674
+ "deviceDiscovery.getStatus": [{
44675
+ name: "deviceId",
44676
+ form: "single",
44677
+ optional: false
44678
+ }],
44423
44679
  "deviceDiscovery.listDiscovered": [{
44424
44680
  name: "deviceId",
44425
44681
  form: "single",
@@ -44439,6 +44695,21 @@ Object.freeze({
44439
44695
  form: "single",
44440
44696
  optional: false
44441
44697
  }],
44698
+ "deviceExport.applyDeviceSettingsPatch": [{
44699
+ name: "deviceId",
44700
+ form: "single",
44701
+ optional: false
44702
+ }],
44703
+ "deviceExport.getDeviceLiveContribution": [{
44704
+ name: "deviceId",
44705
+ form: "single",
44706
+ optional: false
44707
+ }],
44708
+ "deviceExport.getDeviceSettingsContribution": [{
44709
+ name: "deviceId",
44710
+ form: "single",
44711
+ optional: false
44712
+ }],
44442
44713
  "deviceExtension.listCandidates": [{
44443
44714
  name: "deviceId",
44444
44715
  form: "single",
@@ -44787,6 +45058,26 @@ Object.freeze({
44787
45058
  form: "single",
44788
45059
  optional: false
44789
45060
  }],
45061
+ "deviceStatus.getStatus": [{
45062
+ name: "deviceId",
45063
+ form: "single",
45064
+ optional: false
45065
+ }],
45066
+ "doorbell.getStatus": [{
45067
+ name: "deviceId",
45068
+ form: "single",
45069
+ optional: false
45070
+ }],
45071
+ "enumSensor.getStatus": [{
45072
+ name: "deviceId",
45073
+ form: "single",
45074
+ optional: false
45075
+ }],
45076
+ "eventEmitter.getStatus": [{
45077
+ name: "deviceId",
45078
+ form: "single",
45079
+ optional: false
45080
+ }],
44790
45081
  "faceGallery.getFaceByTrack": [{
44791
45082
  name: "deviceId",
44792
45083
  form: "single",
@@ -44801,6 +45092,11 @@ Object.freeze({
44801
45092
  form: "array",
44802
45093
  optional: true
44803
45094
  }],
45095
+ "fanControl.getStatus": [{
45096
+ name: "deviceId",
45097
+ form: "single",
45098
+ optional: false
45099
+ }],
44804
45100
  "fanControl.setDirection": [{
44805
45101
  name: "deviceId",
44806
45102
  form: "single",
@@ -44821,6 +45117,26 @@ Object.freeze({
44821
45117
  form: "single",
44822
45118
  optional: false
44823
45119
  }],
45120
+ "featureProbe.getStatus": [{
45121
+ name: "deviceId",
45122
+ form: "single",
45123
+ optional: false
45124
+ }],
45125
+ "flood.getStatus": [{
45126
+ name: "deviceId",
45127
+ form: "single",
45128
+ optional: false
45129
+ }],
45130
+ "gas.getStatus": [{
45131
+ name: "deviceId",
45132
+ form: "single",
45133
+ optional: false
45134
+ }],
45135
+ "humidifier.getStatus": [{
45136
+ name: "deviceId",
45137
+ form: "single",
45138
+ optional: false
45139
+ }],
44824
45140
  "humidifier.setMode": [{
44825
45141
  name: "deviceId",
44826
45142
  form: "single",
@@ -44836,11 +45152,26 @@ Object.freeze({
44836
45152
  form: "single",
44837
45153
  optional: false
44838
45154
  }],
45155
+ "humiditySensor.getStatus": [{
45156
+ name: "deviceId",
45157
+ form: "single",
45158
+ optional: false
45159
+ }],
45160
+ "image.getStatus": [{
45161
+ name: "deviceId",
45162
+ form: "single",
45163
+ optional: false
45164
+ }],
44839
45165
  "imageSettings.getOptions": [{
44840
45166
  name: "deviceId",
44841
45167
  form: "single",
44842
45168
  optional: false
44843
45169
  }],
45170
+ "imageSettings.getStatus": [{
45171
+ name: "deviceId",
45172
+ form: "single",
45173
+ optional: false
45174
+ }],
44844
45175
  "imageSettings.setSettings": [{
44845
45176
  name: "deviceId",
44846
45177
  form: "single",
@@ -44851,6 +45182,11 @@ Object.freeze({
44851
45182
  form: "single",
44852
45183
  optional: false
44853
45184
  }],
45185
+ "intercom.getStatus": [{
45186
+ name: "deviceId",
45187
+ form: "single",
45188
+ optional: false
45189
+ }],
44854
45190
  "intercom.handleAnswer": [{
44855
45191
  name: "deviceId",
44856
45192
  form: "single",
@@ -44881,6 +45217,11 @@ Object.freeze({
44881
45217
  form: "single",
44882
45218
  optional: false
44883
45219
  }],
45220
+ "lawnMowerControl.getStatus": [{
45221
+ name: "deviceId",
45222
+ form: "single",
45223
+ optional: false
45224
+ }],
44884
45225
  "lawnMowerControl.pause": [{
44885
45226
  name: "deviceId",
44886
45227
  form: "single",
@@ -44891,6 +45232,11 @@ Object.freeze({
44891
45232
  form: "single",
44892
45233
  optional: false
44893
45234
  }],
45235
+ "lockControl.getStatus": [{
45236
+ name: "deviceId",
45237
+ form: "single",
45238
+ optional: false
45239
+ }],
44894
45240
  "lockControl.lock": [{
44895
45241
  name: "deviceId",
44896
45242
  form: "single",
@@ -44906,6 +45252,11 @@ Object.freeze({
44906
45252
  form: "single",
44907
45253
  optional: false
44908
45254
  }],
45255
+ "mediaPlayer.getStatus": [{
45256
+ name: "deviceId",
45257
+ form: "single",
45258
+ optional: false
45259
+ }],
44909
45260
  "mediaPlayer.next": [{
44910
45261
  name: "deviceId",
44911
45262
  form: "single",
@@ -44966,6 +45317,11 @@ Object.freeze({
44966
45317
  form: "single",
44967
45318
  optional: false
44968
45319
  }],
45320
+ "motion.getStatus": [{
45321
+ name: "deviceId",
45322
+ form: "single",
45323
+ optional: false
45324
+ }],
44969
45325
  "motion.isDetected": [{
44970
45326
  name: "deviceId",
44971
45327
  form: "single",
@@ -44976,11 +45332,31 @@ Object.freeze({
44976
45332
  form: "single",
44977
45333
  optional: false
44978
45334
  }],
45335
+ "motionDetection.applyDeviceSettingsPatch": [{
45336
+ name: "deviceId",
45337
+ form: "single",
45338
+ optional: false
45339
+ }],
45340
+ "motionDetection.getDeviceLiveContribution": [{
45341
+ name: "deviceId",
45342
+ form: "single",
45343
+ optional: false
45344
+ }],
45345
+ "motionDetection.getDeviceSettingsContribution": [{
45346
+ name: "deviceId",
45347
+ form: "single",
45348
+ optional: false
45349
+ }],
44979
45350
  "motionDetection.removeCamera": [{
44980
45351
  name: "deviceId",
44981
45352
  form: "single",
44982
45353
  optional: false
44983
45354
  }],
45355
+ "motionTrigger.getStatus": [{
45356
+ name: "deviceId",
45357
+ form: "single",
45358
+ optional: false
45359
+ }],
44984
45360
  "motionTrigger.setMotionTrigger": [{
44985
45361
  name: "deviceId",
44986
45362
  form: "single",
@@ -44991,6 +45367,11 @@ Object.freeze({
44991
45367
  form: "single",
44992
45368
  optional: false
44993
45369
  }],
45370
+ "motionZones.getStatus": [{
45371
+ name: "deviceId",
45372
+ form: "single",
45373
+ optional: false
45374
+ }],
44994
45375
  "motionZones.setZone": [{
44995
45376
  name: "deviceId",
44996
45377
  form: "single",
@@ -45001,6 +45382,11 @@ Object.freeze({
45001
45382
  form: "single",
45002
45383
  optional: false
45003
45384
  }],
45385
+ "nativeObjectDetection.getStatus": [{
45386
+ name: "deviceId",
45387
+ form: "single",
45388
+ optional: false
45389
+ }],
45004
45390
  "nativeObjectDetection.setEnabled": [{
45005
45391
  name: "deviceId",
45006
45392
  form: "single",
@@ -45016,6 +45402,11 @@ Object.freeze({
45016
45402
  form: "single",
45017
45403
  optional: false
45018
45404
  }],
45405
+ "navigation.getStatus": [{
45406
+ name: "deviceId",
45407
+ form: "single",
45408
+ optional: false
45409
+ }],
45019
45410
  "navigation.goToPoint": [{
45020
45411
  name: "deviceId",
45021
45412
  form: "single",
@@ -45061,6 +45452,11 @@ Object.freeze({
45061
45452
  form: "single",
45062
45453
  optional: false
45063
45454
  }],
45455
+ "networkLink.getStatus": [{
45456
+ name: "deviceId",
45457
+ form: "single",
45458
+ optional: false
45459
+ }],
45064
45460
  "networkQuality.getDeviceStats": [{
45065
45461
  name: "deviceId",
45066
45462
  form: "single",
@@ -45071,6 +45467,11 @@ Object.freeze({
45071
45467
  form: "single",
45072
45468
  optional: false
45073
45469
  }],
45470
+ "notificationRules.getAlarmSensors": [{
45471
+ name: "deviceIds",
45472
+ form: "array",
45473
+ optional: false
45474
+ }],
45074
45475
  "notificationRules.setDeviceMuted": [{
45075
45476
  name: "deviceId",
45076
45477
  form: "single",
@@ -45081,11 +45482,26 @@ Object.freeze({
45081
45482
  form: "single",
45082
45483
  optional: false
45083
45484
  }],
45485
+ "notifier.getStatus": [{
45486
+ name: "deviceId",
45487
+ form: "single",
45488
+ optional: false
45489
+ }],
45084
45490
  "notifier.send": [{
45085
45491
  name: "deviceId",
45086
45492
  form: "single",
45087
45493
  optional: false
45088
45494
  }],
45495
+ "numericSensor.getStatus": [{
45496
+ name: "deviceId",
45497
+ form: "single",
45498
+ optional: false
45499
+ }],
45500
+ "osd.getStatus": [{
45501
+ name: "deviceId",
45502
+ form: "single",
45503
+ optional: false
45504
+ }],
45089
45505
  "osd.setOverlay": [{
45090
45506
  name: "deviceId",
45091
45507
  form: "single",
@@ -45145,6 +45561,11 @@ Object.freeze({
45145
45561
  form: "single",
45146
45562
  optional: false
45147
45563
  }],
45564
+ "petFeeder.getStatus": [{
45565
+ name: "deviceId",
45566
+ form: "single",
45567
+ optional: false
45568
+ }],
45148
45569
  "petFeeder.markFoodReplenished": [{
45149
45570
  name: "deviceId",
45150
45571
  form: "single",
@@ -45180,6 +45601,11 @@ Object.freeze({
45180
45601
  form: "single",
45181
45602
  optional: false
45182
45603
  }],
45604
+ "pipelineAnalytics.applyDeviceSettingsPatch": [{
45605
+ name: "deviceId",
45606
+ form: "single",
45607
+ optional: false
45608
+ }],
45183
45609
  "pipelineAnalytics.clearTrackRelation": [{
45184
45610
  name: "deviceId",
45185
45611
  form: "single",
@@ -45220,6 +45646,16 @@ Object.freeze({
45220
45646
  form: "single",
45221
45647
  optional: false
45222
45648
  }],
45649
+ "pipelineAnalytics.getDeviceLiveContribution": [{
45650
+ name: "deviceId",
45651
+ form: "single",
45652
+ optional: false
45653
+ }],
45654
+ "pipelineAnalytics.getDeviceSettingsContribution": [{
45655
+ name: "deviceId",
45656
+ form: "single",
45657
+ optional: false
45658
+ }],
45223
45659
  "pipelineAnalytics.getEventDensity": [{
45224
45660
  name: "deviceId",
45225
45661
  form: "single",
@@ -45435,6 +45871,11 @@ Object.freeze({
45435
45871
  form: "single",
45436
45872
  optional: true
45437
45873
  }],
45874
+ "pipelineOrchestrator.applyDeviceSettingsPatch": [{
45875
+ name: "deviceId",
45876
+ form: "single",
45877
+ optional: false
45878
+ }],
45438
45879
  "pipelineOrchestrator.assignAudio": [{
45439
45880
  name: "deviceId",
45440
45881
  form: "single",
@@ -45480,6 +45921,16 @@ Object.freeze({
45480
45921
  form: "single",
45481
45922
  optional: false
45482
45923
  }],
45924
+ "pipelineOrchestrator.getDeviceLiveContribution": [{
45925
+ name: "deviceId",
45926
+ form: "single",
45927
+ optional: false
45928
+ }],
45929
+ "pipelineOrchestrator.getDeviceSettingsContribution": [{
45930
+ name: "deviceId",
45931
+ form: "single",
45932
+ optional: false
45933
+ }],
45483
45934
  "pipelineOrchestrator.getPipelineAssignment": [{
45484
45935
  name: "deviceId",
45485
45936
  form: "single",
@@ -45600,11 +46051,31 @@ Object.freeze({
45600
46051
  form: "single",
45601
46052
  optional: true
45602
46053
  }],
46054
+ "powerMeter.getStatus": [{
46055
+ name: "deviceId",
46056
+ form: "single",
46057
+ optional: false
46058
+ }],
46059
+ "presence.getStatus": [{
46060
+ name: "deviceId",
46061
+ form: "single",
46062
+ optional: false
46063
+ }],
46064
+ "pressureSensor.getStatus": [{
46065
+ name: "deviceId",
46066
+ form: "single",
46067
+ optional: false
46068
+ }],
45603
46069
  "privacyMask.getOptions": [{
45604
46070
  name: "deviceId",
45605
46071
  form: "single",
45606
46072
  optional: false
45607
46073
  }],
46074
+ "privacyMask.getStatus": [{
46075
+ name: "deviceId",
46076
+ form: "single",
46077
+ optional: false
46078
+ }],
45608
46079
  "privacyMask.setAudioEnabled": [{
45609
46080
  name: "deviceId",
45610
46081
  form: "single",
@@ -45655,6 +46126,11 @@ Object.freeze({
45655
46126
  form: "single",
45656
46127
  optional: false
45657
46128
  }],
46129
+ "ptz.getStatus": [{
46130
+ name: "deviceId",
46131
+ form: "single",
46132
+ optional: false
46133
+ }],
45658
46134
  "ptz.goHome": [{
45659
46135
  name: "deviceId",
45660
46136
  form: "single",
@@ -45745,6 +46221,11 @@ Object.freeze({
45745
46221
  form: "single",
45746
46222
  optional: false
45747
46223
  }],
46224
+ "recordingArchive.applyDeviceSettingsPatch": [{
46225
+ name: "deviceId",
46226
+ form: "single",
46227
+ optional: false
46228
+ }],
45748
46229
  "recordingArchive.deleteFootprint": [{
45749
46230
  name: "deviceId",
45750
46231
  form: "single",
@@ -45765,6 +46246,21 @@ Object.freeze({
45765
46246
  form: "single",
45766
46247
  optional: false
45767
46248
  }],
46249
+ "recordingArchive.getDeviceLiveContribution": [{
46250
+ name: "deviceId",
46251
+ form: "single",
46252
+ optional: false
46253
+ }],
46254
+ "recordingArchive.getDeviceSettingsContribution": [{
46255
+ name: "deviceId",
46256
+ form: "single",
46257
+ optional: false
46258
+ }],
46259
+ "recordingArchive.getStatus": [{
46260
+ name: "deviceId",
46261
+ form: "single",
46262
+ optional: false
46263
+ }],
45768
46264
  "recordingArchive.listOpsLog": [{
45769
46265
  name: "deviceId",
45770
46266
  form: "single",
@@ -45850,6 +46346,11 @@ Object.freeze({
45850
46346
  form: "single",
45851
46347
  optional: false
45852
46348
  }],
46349
+ "recordingOnboard.getStatus": [{
46350
+ name: "deviceId",
46351
+ form: "single",
46352
+ optional: false
46353
+ }],
45853
46354
  "recordingOnboard.setSettings": [{
45854
46355
  name: "deviceId",
45855
46356
  form: "single",
@@ -45880,6 +46381,11 @@ Object.freeze({
45880
46381
  form: "single",
45881
46382
  optional: false
45882
46383
  }],
46384
+ "sceneMonitor.getStatus": [{
46385
+ name: "deviceId",
46386
+ form: "single",
46387
+ optional: false
46388
+ }],
45883
46389
  "sceneMonitor.listScenes": [{
45884
46390
  name: "deviceId",
45885
46391
  form: "single",
@@ -45905,6 +46411,11 @@ Object.freeze({
45905
46411
  form: "single",
45906
46412
  optional: false
45907
46413
  }],
46414
+ "scriptRunner.getStatus": [{
46415
+ name: "deviceId",
46416
+ form: "single",
46417
+ optional: false
46418
+ }],
45908
46419
  "scriptRunner.run": [{
45909
46420
  name: "deviceId",
45910
46421
  form: "single",
@@ -45915,11 +46426,31 @@ Object.freeze({
45915
46426
  form: "single",
45916
46427
  optional: false
45917
46428
  }],
46429
+ "smoke.getStatus": [{
46430
+ name: "deviceId",
46431
+ form: "single",
46432
+ optional: false
46433
+ }],
46434
+ "snapshot.applyDeviceSettingsPatch": [{
46435
+ name: "deviceId",
46436
+ form: "single",
46437
+ optional: false
46438
+ }],
45918
46439
  "snapshot.getDebugState": [{
45919
46440
  name: "deviceId",
45920
46441
  form: "single",
45921
46442
  optional: false
45922
46443
  }],
46444
+ "snapshot.getDeviceLiveContribution": [{
46445
+ name: "deviceId",
46446
+ form: "single",
46447
+ optional: false
46448
+ }],
46449
+ "snapshot.getDeviceSettingsContribution": [{
46450
+ name: "deviceId",
46451
+ form: "single",
46452
+ optional: false
46453
+ }],
45923
46454
  "snapshot.getSnapshot": [{
45924
46455
  name: "deviceId",
45925
46456
  form: "single",
@@ -45936,6 +46467,11 @@ Object.freeze({
45936
46467
  form: "array",
45937
46468
  optional: false
45938
46469
  }],
46470
+ "snapshot.getStatus": [{
46471
+ name: "deviceId",
46472
+ form: "single",
46473
+ optional: false
46474
+ }],
45939
46475
  "snapshot.invalidateCache": [{
45940
46476
  name: "deviceId",
45941
46477
  form: "single",
@@ -45946,6 +46482,11 @@ Object.freeze({
45946
46482
  form: "single",
45947
46483
  optional: false
45948
46484
  }],
46485
+ "streamBroker.applyDeviceSettingsPatch": [{
46486
+ name: "deviceId",
46487
+ form: "single",
46488
+ optional: false
46489
+ }],
45949
46490
  "streamBroker.assignProfile": [{
45950
46491
  name: "deviceId",
45951
46492
  form: "single",
@@ -45961,6 +46502,16 @@ Object.freeze({
45961
46502
  form: "single",
45962
46503
  optional: false
45963
46504
  }],
46505
+ "streamBroker.getDeviceLiveContribution": [{
46506
+ name: "deviceId",
46507
+ form: "single",
46508
+ optional: false
46509
+ }],
46510
+ "streamBroker.getDeviceSettingsContribution": [{
46511
+ name: "deviceId",
46512
+ form: "single",
46513
+ optional: false
46514
+ }],
45964
46515
  "streamBroker.getStreamWithCodec": [{
45965
46516
  name: "deviceId",
45966
46517
  form: "single",
@@ -46021,16 +46572,46 @@ Object.freeze({
46021
46572
  form: "single",
46022
46573
  optional: false
46023
46574
  }],
46575
+ "streamParams.getStatus": [{
46576
+ name: "deviceId",
46577
+ form: "single",
46578
+ optional: false
46579
+ }],
46024
46580
  "streamParams.setProfile": [{
46025
46581
  name: "deviceId",
46026
46582
  form: "single",
46027
46583
  optional: false
46028
46584
  }],
46585
+ "switch.getStatus": [{
46586
+ name: "deviceId",
46587
+ form: "single",
46588
+ optional: false
46589
+ }],
46029
46590
  "switch.setState": [{
46030
46591
  name: "deviceId",
46031
46592
  form: "single",
46032
46593
  optional: false
46033
46594
  }],
46595
+ "tamper.getStatus": [{
46596
+ name: "deviceId",
46597
+ form: "single",
46598
+ optional: false
46599
+ }],
46600
+ "temperatureSensor.getStatus": [{
46601
+ name: "deviceId",
46602
+ form: "single",
46603
+ optional: false
46604
+ }],
46605
+ "update.getStatus": [{
46606
+ name: "deviceId",
46607
+ form: "single",
46608
+ optional: false
46609
+ }],
46610
+ "vacuumControl.getStatus": [{
46611
+ name: "deviceId",
46612
+ form: "single",
46613
+ optional: false
46614
+ }],
46034
46615
  "vacuumControl.locate": [{
46035
46616
  name: "deviceId",
46036
46617
  form: "single",
@@ -46066,6 +46647,11 @@ Object.freeze({
46066
46647
  form: "single",
46067
46648
  optional: false
46068
46649
  }],
46650
+ "valve.getStatus": [{
46651
+ name: "deviceId",
46652
+ form: "single",
46653
+ optional: false
46654
+ }],
46069
46655
  "valve.open": [{
46070
46656
  name: "deviceId",
46071
46657
  form: "single",
@@ -46081,6 +46667,11 @@ Object.freeze({
46081
46667
  form: "single",
46082
46668
  optional: false
46083
46669
  }],
46670
+ "vibration.getStatus": [{
46671
+ name: "deviceId",
46672
+ form: "single",
46673
+ optional: false
46674
+ }],
46084
46675
  "videoclips.dialClipStream": [{
46085
46676
  name: "deviceId",
46086
46677
  form: "single",
@@ -46116,6 +46707,11 @@ Object.freeze({
46116
46707
  form: "single",
46117
46708
  optional: false
46118
46709
  }],
46710
+ "waterHeater.getStatus": [{
46711
+ name: "deviceId",
46712
+ form: "single",
46713
+ optional: false
46714
+ }],
46119
46715
  "waterHeater.setAway": [{
46120
46716
  name: "deviceId",
46121
46717
  form: "single",
@@ -46131,6 +46727,11 @@ Object.freeze({
46131
46727
  form: "single",
46132
46728
  optional: false
46133
46729
  }],
46730
+ "weather.getStatus": [{
46731
+ name: "deviceId",
46732
+ form: "single",
46733
+ optional: false
46734
+ }],
46134
46735
  "webrtcSession.addIceCandidate": [{
46135
46736
  name: "deviceId",
46136
46737
  form: "single",
@@ -46868,7 +47469,7 @@ var PreviewTextField = string().max(200);
46868
47469
  * Whether the notification's preview is a STILL or a short animation.
46869
47470
  *
46870
47471
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
46871
- * rule)"* — the other rules already offer an animated preview, and a ten-hour
47472
+ * rule)"* [sent as a gif or video (as for the other rules)] — the other rules already offer an animated preview, and a ten-hour
46872
47473
  * night reads better as three seconds of motion than as one frame of it. Both
46873
47474
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
46874
47475
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -47006,6 +47607,7 @@ TimelapseRuleInputSchema.extend({
47006
47607
  createdAt: number(),
47007
47608
  updatedAt: number()
47008
47609
  });
47610
+ MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
47009
47611
  object({
47010
47612
  /**
47011
47613
  * Fraction of the box's own size added on EACH side before cutting.