@camstack/addon-export-alexa 1.2.123 → 1.2.125

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.
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  //#endregion
27
27
  let node_crypto = require("node:crypto");
28
28
  node_crypto = __toESM(node_crypto, 1);
29
- //#region ../types/dist/event-category-C5xZWqz6.mjs
29
+ //#region ../types/dist/event-category-BGmz_oxW.mjs
30
30
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
31
31
  EventCategory["SystemBoot"] = "system.boot";
32
32
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -559,6 +559,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
559
559
  */
560
560
  EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
561
561
  EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
562
+ /**
563
+ * A camera provider's RAW onboard motion push (D673): the `MotionOnMotionChanged`
564
+ * data shape without `triggeredBy`, built by `providerMotionEvent`. Its one
565
+ * consumer is the orchestrator's camera-sensors bridge, which re-emits the
566
+ * camera's EFFECTIVE onboard motion as `MotionOnMotionChanged` (a pass-through
567
+ * for a camera with no attached sensor). Providers must not emit
568
+ * `MotionOnMotionChanged` themselves; `scripts/check-onboard-motion-emitters.ts`
569
+ * keeps it so (camera-grid is a named, deferred exception).
570
+ */
562
571
  EventCategory["ProviderMotion"] = "provider.motion";
563
572
  EventCategory["ProviderDetection"] = "provider.detection";
564
573
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
@@ -5397,7 +5406,7 @@ var ZodIssueCode = {
5397
5406
  var ZodFirstPartyTypeKind;
5398
5407
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5399
5408
  //#endregion
5400
- //#region ../types/dist/sleep-gU6hqv4_.mjs
5409
+ //#region ../types/dist/sleep-CDCONfkt.mjs
5401
5410
  /**
5402
5411
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5403
5412
  * window to float samples (D455).
@@ -7458,7 +7467,7 @@ function errMsg(err) {
7458
7467
  return String(err);
7459
7468
  }
7460
7469
  //#endregion
7461
- //#region ../types/dist/composition-Dq42XWp0.mjs
7470
+ //#region ../types/dist/composition-Bzr7GwgP.mjs
7462
7471
  /**
7463
7472
  * The ffmpeg build every CamStack node downloads, per platform and arch.
7464
7473
  *
@@ -7512,21 +7521,20 @@ function portable(target) {
7512
7521
  }
7513
7522
  portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7514
7523
  /**
7515
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7516
- * valutatore puro del suo FRONTE.
7524
+ * THE table "which boolean of this device type counts as HIGH", and the
7525
+ * pure evaluator of its EDGE.
7517
7526
  *
7518
- * Viveva dentro il builtin virtual-doorbell
7519
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
7520
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
7521
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
7522
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
7523
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
7524
- * SPOSTA qui e il doorbell la ri-esporta.
7527
+ * ONE table for everyone who asks "is this device HIGH?": the
7528
+ * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
7529
+ * sensor markers, the orchestrator's camera-sensors bridge and
7530
+ * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
7531
+ * A second copy would diverge at the first cap added, and the symptom — "the
7532
+ * sensor rings the doorbell but does not record" — is exactly D62.
7525
7533
  *
7526
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
7527
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
7528
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
7529
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
7534
+ * ⚠ NOT `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), and the
7535
+ * two must not be unified: that one answers "what is the state WORD for a
7536
+ * rule" (and includes `presence`, `cover`, `alarm-panel`), this one "which
7537
+ * boolean's EDGE counts". Deliberately different vocabularies.
7530
7538
  */
7531
7539
  /**
7532
7540
  * Known binary / switch source caps → the boolean slice field whose
@@ -7547,6 +7555,8 @@ var SOURCE_CAP_ACTIVE_FIELD = {
7547
7555
  vibration: "detected",
7548
7556
  tamper: "tampered"
7549
7557
  };
7558
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
7559
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
7550
7560
  /**
7551
7561
  * Error types for the safe expression engine. Two distinct classes so callers
7552
7562
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -9302,22 +9312,22 @@ var CLASS_MAP_MACRO_TARGETS = [
9302
9312
  "package"
9303
9313
  ];
9304
9314
  /**
9305
- * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
9306
- * un operatore può selezionare.
9315
+ * The FIRST-LEVEL macro classes: those an object detector emits and that
9316
+ * an operator can select.
9307
9317
  *
9308
- * Sono le tre offerte dallo step `object-detection`
9318
+ * They are the three offered by the `object-detection` step
9309
9319
  * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
9310
- * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
9311
- * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
9312
- * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
9313
- * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
9320
+ * `enabledMacroClasses`). `package` is in {@link CLASS_MAP_MACRO_TARGETS} and in
9321
+ * `MACRO_LABELS` — it is a true macro — but NOT here: it belongs to the step
9322
+ * `package-detection`, whose enablement is driven by zones, and offering the
9323
+ * same word twice has already made an operator turn on the COCO proxy
9314
9324
  * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
9315
9325
  *
9316
- * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
9317
- * dello step e una seconda volta come union `FirstLevelMacro`
9318
- * (`types/detection.ts`); una terza copia per il trigger di registrazione
9319
- * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
9320
- * successiva.
9326
+ * ONE list. Before today the same three were hand-written in the step's offer
9327
+ * and a second time as the union `FirstLevelMacro`
9328
+ * (`types/detection.ts`); a third copy for the recording trigger
9329
+ * (`RecordingTriggers.objectClasses`) would have made the later divergence
9330
+ * invisible.
9321
9331
  */
9322
9332
  var FIRST_LEVEL_MACRO_CLASSES = [
9323
9333
  "person",
@@ -9382,17 +9392,17 @@ var RecordingStorageModeSchema = _enum([
9382
9392
  "on-device-decision"
9383
9393
  ]);
9384
9394
  /**
9385
- * Le macro classi che possono aprire una finestra di registrazione — le stesse
9386
- * tre offerte dallo step `object-detection`, da UNA lista
9395
+ * The macro classes that can open a recording window — the same
9396
+ * three offered by the `object-detection` step, from ONE list
9387
9397
  * ({@link FIRST_LEVEL_MACRO_CLASSES}).
9388
9398
  */
9389
9399
  var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
9390
9400
  /**
9391
- * True quando `values` non ripete un elemento.
9401
+ * True when `values` does not repeat an element.
9392
9402
  *
9393
- * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
9394
- * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
9395
- * conterebbe due volte le sue finestre in `segmentMissedByMs`.
9403
+ * A duplicate is not harmless: every entry of `objectClasses` / `sensorDeviceIds`
9404
+ * becomes a SOURCE in `bandTriggerSources`, and the same source twice
9405
+ * would count its windows twice in `segmentMissedByMs`.
9396
9406
  */
9397
9407
  var noDuplicates = (values) => new Set(values).size === values.length;
9398
9408
  /** Which detectors trigger an `events`-mode band. */
@@ -9400,34 +9410,34 @@ var RecordingTriggersSchema = object({
9400
9410
  motion: boolean().optional(),
9401
9411
  audioThresholdDbfs: number().optional(),
9402
9412
  /**
9403
- * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
9404
- * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
9405
- * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
9406
- * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
9413
+ * The macro classes whose detection opens a window. ABSENT = the source
9414
+ * is not listened to; an EMPTY array is rejected, because "events band, object
9415
+ * trigger on, no class" is the same shape "enabled and records
9416
+ * nothing, forever" that `eventsBandCanEverDemand` is written against.
9407
9417
  *
9408
- * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
9409
- * quelle che hanno attraversato `enabledMacroClasses`, i
9410
- * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
9411
- * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
9412
- * finestre — vedi `recorder/object-trigger.ts`.
9418
+ * The signal read is ALREADY FILTERED: only `source: 'pipeline'` detections, i.e.
9419
+ * those that went through `enabledMacroClasses`, the
9420
+ * `minConfidence<Macro>` and the full-frame guard. The camera's onboard AI
9421
+ * (`source: 'onboard'`) goes through none of those gates and does NOT open
9422
+ * windows — see `recorder/object-trigger.ts`.
9413
9423
  */
9414
9424
  objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
9415
9425
  /**
9416
- * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
9417
- * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
9426
+ * The LINKED devices whose RISING EDGE opens a window. Absent = the source
9427
+ * is not listened to; an empty array is rejected for the same reason as
9418
9428
  * `objectClasses`.
9419
9429
  *
9420
- * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
9421
- * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
9422
- * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
9423
- * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9424
- * device (D12) — mai un elenco globale di cap.
9430
+ * These are SOURCE device ids, not cameras: the band names them, so the
9431
+ * hot path (`DeviceStateChanged`, at bus rate across the whole fleet)
9432
+ * does no RPC. The OFFER from which the operator picks them is another question, and
9433
+ * is resolved with `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9434
+ * device (D12) — never a global list of caps.
9425
9435
  *
9426
- * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
9427
- * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
9428
- * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
9429
- * il livello: un contatto trovato già aperto al riavvio del runner non fa
9430
- * registrare.
9436
+ * What "high" means depends on the device TYPE and is not decided here:
9437
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) is THE table,
9438
+ * the same one as the composite doorbell (D671). And it is the EDGE, not
9439
+ * the level: a contact found already open at runner restart does not
9440
+ * record.
9431
9441
  */
9432
9442
  sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
9433
9443
  });
@@ -10504,7 +10514,7 @@ var DecoderSessionConfigSchema = object({
10504
10514
  * - **VALUE** — at which level, for which scope, until when. That stays ONE
10505
10515
  * thing: the logging settings document on the `system` cap. Two authorities
10506
10516
  * over the values is the exact defect
10507
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
10517
+ * `docs/design/plans/2026-08-26-per-component-logging.md` was written to
10508
10518
  * remove; re-introducing it from the cure side would be grotesque.
10509
10519
  *
10510
10520
  * Nothing in this file reads a clock, an env var or a store. The registry is
@@ -12329,7 +12339,7 @@ var BrokerStatusEnum = _enum([
12329
12339
  "unreachable",
12330
12340
  "error"
12331
12341
  ]);
12332
- var BrokerInfoSchema$1 = object({
12342
+ var BrokerInfoSchema = object({
12333
12343
  /** Stable broker id. Persisted; survives addon restarts. */
12334
12344
  id: string(),
12335
12345
  /** Addon id of the provider that OWNS this broker.
@@ -12462,7 +12472,7 @@ var GetStateInputSchema = object({
12462
12472
  * HA: entity_id (returns the cached entity state). */
12463
12473
  key: string()
12464
12474
  });
12465
- method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, BrokerInfoSchema$1.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12475
+ method(ListInputSchema, array(BrokerInfoSchema)), method(GetInputSchema, BrokerInfoSchema.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12466
12476
  kind: "mutation",
12467
12477
  auth: "admin"
12468
12478
  }), method(RemoveInputSchema, _void(), {
@@ -14071,7 +14081,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
14071
14081
  *
14072
14082
  * ⚠ An earlier version of this comment blamed Zod stripping, and that
14073
14083
  * was wrong — corrected 2026-08-26 after the hop map
14074
- * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
14084
+ * (`docs/design/2026-08-26-topic-hop-map.md`) traced the path.
14075
14085
  * There is **no Zod parse at all** between the door and the engine: the
14076
14086
  * dispatcher forwards the payload verbatim and UDS carries it whole. A
14077
14087
  * field declared here reaches `SqliteSettingsBackend` either way.
@@ -14852,7 +14862,17 @@ var SiteZoneSchema = object({
14852
14862
  var ProviderStatusSchema = object({
14853
14863
  connected: boolean(),
14854
14864
  deviceCount: number(),
14855
- error: string().optional()
14865
+ error: string().optional(),
14866
+ /**
14867
+ * `'orchestrator'`: this provider writes NO camera's `motion` runtime slice
14868
+ * — the orchestrator is its only writer (D696). Set by `BaseDeviceProvider`,
14869
+ * which every provider bundles, so only a provider BUILT from D696-amended
14870
+ * code says it. ABSENT means an older provider that may still write it (raw
14871
+ * VMD writes, a boot seed), and the orchestrator's provider gate keeps that
14872
+ * provider's cameras unwritten (`camera-sensors/motion-slice-provider-gate.ts`)
14873
+ * — the provider twin of the runner's `getLocalLoad().motionSliceWriter`.
14874
+ */
14875
+ motionSliceWriter: literal("orchestrator").optional()
14856
14876
  });
14857
14877
  object({
14858
14878
  externalId: string(),
@@ -15650,28 +15670,10 @@ method(object({
15650
15670
  deviceIds: array(number()).readonly(),
15651
15671
  caps: array(string()).readonly().optional()
15652
15672
  }), record(string(), record(string(), unknown().nullable())));
15653
- object({
15654
- /** Ms epoch of the last press. null = never observed since this provider started. */
15655
- lastPressedAt: number().nullable(),
15656
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
15657
- pressCountSinceStart: number()
15658
- });
15659
- object({
15660
- deviceId: number(),
15661
- timestamp: number()
15662
- });
15663
- /**
15664
- * The `doorbell-source` extension slot's editable value (D648): the camera's
15665
- * virtual-doorbell trigger devices, exactly what the wrapper's
15666
- * `doorbellSources` persists (as `{ deviceId }` records).
15667
- */
15668
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
15673
+ var ExtensionSlotIdSchema = _enum(["linked-devices"]);
15669
15674
  DeviceType.Camera;
15670
- DeviceType.Button, DeviceType.Camera;
15671
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
15672
- DeviceType.Camera, DeviceType.Camera;
15673
15675
  /** A slot's editable value, discriminated on `slot`. */
15674
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15676
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15675
15677
  DeviceType.Camera;
15676
15678
  var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
15677
15679
  state: literal("unreachable"),
@@ -15708,10 +15710,11 @@ var ExtensionSlotSourceSchema = object({
15708
15710
  });
15709
15711
  /**
15710
15712
  * A DIFFERENT authority that gates whether the slot's sources do anything —
15711
- * reported read-only, named, never written through this contract (D62). The
15712
- * virtual doorbell's sources ring only while the wrapper is bound to the
15713
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
15714
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
15713
+ * reported read-only, named, never written through this contract (D62) — e.g.
15714
+ * a slot whose sources act only while a wrapper is bound to the host, a
15715
+ * binding that belongs to `deviceManager.setWrapperActive`. No slot reports
15716
+ * one today (`linked-devices` answers `null`). `unknown` means the gate could
15717
+ * not be read; it is not "inactive" (D315).
15715
15718
  */
15716
15719
  var ExtensionSlotActivationSchema = object({
15717
15720
  state: _enum([
@@ -15727,7 +15730,7 @@ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
15727
15730
  /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
15728
15731
  slotRef: string(),
15729
15732
  slot: ExtensionSlotIdSchema,
15730
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
15733
+ /** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
15731
15734
  authority: string(),
15732
15735
  activation: ExtensionSlotActivationSchema.nullable(),
15733
15736
  value: ExtensionSlotValueSchema,
@@ -16005,7 +16008,7 @@ method(object({
16005
16008
  * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
16006
16009
  * ends in a log line, and a log line is the thing the operator asked to stop
16007
16010
  * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
16008
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
16011
+ * 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
16009
16012
  * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
16010
16013
  * media blackout were both diagnosed. The counters stay; this is where they can
16011
16014
  * be READ.
@@ -17381,7 +17384,7 @@ var BrokerStatusSchema$1 = _enum([
17381
17384
  "unreachable",
17382
17385
  "tls-error"
17383
17386
  ]);
17384
- var BrokerInfoSchema = object({
17387
+ var BrokerInfoSchema$1 = object({
17385
17388
  id: string(),
17386
17389
  name: string(),
17387
17390
  url: string(),
@@ -17444,7 +17447,7 @@ var StatusSchema = object({
17444
17447
  brokerCount: number(),
17445
17448
  embeddedRunning: boolean()
17446
17449
  });
17447
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17450
+ method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17448
17451
  kind: "mutation",
17449
17452
  auth: "admin"
17450
17453
  }), method(IdInputSchema, _void(), {
@@ -19620,78 +19623,16 @@ var NcRuleInputSchema = object({
19620
19623
  */
19621
19624
  confirm: NcConfirmSchema.optional(),
19622
19625
  /**
19623
- * WAIT for face/plate recognition before saying anything.
19624
- *
19625
- * A notification's TEXT is frozen at enqueue and its media is re-resolved at
19626
- * send; the identity is neither. A face is confirmed after `confirmFrames`
19627
- * agreeing observations — p50 **11.4 s** after the track was first seen,
19628
- * measured on this hub — and an `immediate` rule enqueues on the first object
19629
- * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
19630
- * immediate path, and no amount of media re-resolution fixes a sentence.
19631
- *
19632
- * Only two honest answers exist, and this flag picks between them. It has
19633
- * effect ONLY on a rule that declares a recognition scope
19634
- * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
19635
- * other rule there is nothing to wait for and the flag is inert.
19636
- *
19637
- * | value | what happens |
19638
- * | --- | --- |
19639
- * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
19640
- * | 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) |
19641
- *
19642
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19643
- * on the addon cap path, and absent has to keep meaning exactly what every
19644
- * rule authored before this field meant.
19645
- *
19646
- * The cost of `true` is stated here because the editor states it too: a rule
19647
- * that waits also inherits track-close SEMANTICS — its `zones` condition
19648
- * tests every zone the track visited and a `crossing` condition can no longer
19649
- * be satisfied, because a closed track carries no crossing.
19650
- */
19651
- waitForEnhancement: boolean().optional(),
19652
- /**
19653
- * GROUP a burst of subjects into ONE notification that grows.
19654
- *
19655
- * Seconds of quiet after the last matching subject before the burst is
19656
- * considered over. While it is open, the first subject enqueues immediately —
19657
- * **exactly as today, with no added latency** — and every real growth (a new
19658
- * subject, or a name confirmed on one already in it) REPLACES that
19659
- * notification with an updated one naming everybody. The push carries the
19660
- * group's own coalescing tag, so the phone replaces rather than stacks.
19661
- *
19662
- * `0` / absent = off, and off is today's behaviour byte for byte.
19663
- *
19664
- * ### Why an idle cutoff and not a window
19665
- *
19666
- * The measured seven-person arrival on device 590 spans 110 s with every
19667
- * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
19668
- * idle cutoff holds it as one and ends it when the arrival actually ends.
19669
- * 30 s is a widely shipped value for the same decision.
19670
- *
19671
- * ### What it replaces
19672
- *
19673
- * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
19674
- * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
19675
- * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
19676
- * track that did fire and 7 carrying a confirmed identity nobody heard about.
19677
- * A group collapses the same volume by MERGING, so the cooldown becomes a
19678
- * budget over GROUPS — which is what it always meant — and a growth is never
19679
- * throttled by the window its own first member spent.
19680
- *
19681
- * ### Interaction with {@link waitForEnhancement}
19682
- *
19683
- * They compose, and the order matters. `waitForEnhancement` defers the rule to
19684
- * TRACK CLOSE, so with both set the group is opened by the first member to
19685
- * CLOSE — already carrying its name — and grows as later members close. That
19686
- * is later, and complete. With grouping alone the group opens on the first
19687
- * object event and picks up names as they are confirmed, through the growth
19688
- * path. Neither combination fires twice for one subject.
19689
- *
19690
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19691
- * on the addon cap path, so absent must keep meaning what it meant before this
19692
- * field existed.
19626
+ * IMMEDIATE GROUPED (D692): the first matching subject opens a window of this many
19627
+ * seconds; the rule sends ONE message when the window closes — early, once every
19628
+ * subject in it is final, never before {@link NC_GROUPING_MIN_SEC} — naming everybody
19629
+ * the rule matched, with one mosaic tile per subject.
19630
+ * Absent = plain immediate (one message per track, at the first match).
19631
+ * Only an `immediate` DETECTION rule may carry it (`ruleGroupingApplies`, enforced on
19632
+ * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
19633
+ * path does not run Zod defaults.
19693
19634
  */
19694
- groupIdleSec: number().int().min(0).max(600).optional()
19635
+ groupingSec: number().int().min(5).max(60).optional()
19695
19636
  });
19696
19637
  /**
19697
19638
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -19739,9 +19680,10 @@ var NcRuleClearableKeySchema = _enum([
19739
19680
  "targetUsers",
19740
19681
  "snoozeOptions",
19741
19682
  "snoozeAllowGlobal",
19742
- "waitForEnhancement",
19743
19683
  "actions",
19744
19684
  "confirm",
19685
+ "groupingSec",
19686
+ "waitForEnhancement",
19745
19687
  "groupIdleSec"
19746
19688
  ]);
19747
19689
  var NcRuleUpdateInputSchema = object({
@@ -19763,9 +19705,25 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19763
19705
  */
19764
19706
  disabledTargetIds: array(string()).default([])
19765
19707
  });
19708
+ /**
19709
+ * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19710
+ * still-true state. `backups-exports` and `alarm` are not sections — a completed
19711
+ * job is not an open state, and the alarm stays instant.
19712
+ */
19713
+ var NcStateSummarySectionIdSchema = _enum([
19714
+ "device-state",
19715
+ "streams",
19716
+ "battery",
19717
+ "consumables",
19718
+ "tanks",
19719
+ "detection-blind",
19720
+ "nodes",
19721
+ "addons",
19722
+ "updates"
19723
+ ]);
19766
19724
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
19767
19725
  var NcStateSummaryPreviewSectionSchema = object({
19768
- id: string(),
19726
+ id: NcStateSummarySectionIdSchema,
19769
19727
  status: _enum(["read", "unavailable"]),
19770
19728
  items: number().int().nonnegative(),
19771
19729
  reason: string().optional()
@@ -19775,7 +19733,7 @@ var NcStateSummaryPreviewSectionSchema = object({
19775
19733
  * a `rule` split (no `section`), one section's on a `section` split.
19776
19734
  */
19777
19735
  var NcStateSummaryPreviewRowSchema = object({
19778
- section: string().optional(),
19736
+ section: NcStateSummarySectionIdSchema.optional(),
19779
19737
  /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
19780
19738
  title: string(),
19781
19739
  text: string(),
@@ -20212,6 +20170,29 @@ object({
20212
20170
  firstAt: number(),
20213
20171
  lastAt: number()
20214
20172
  });
20173
+ var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
20174
+ var NcAlarmDelaySecSchema = number().int().min(0).max(600);
20175
+ var NcAlarmTriggeredSecSchema = number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
20176
+ var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
20177
+ var NcAlarmModeDelaysListSchema = array(object({
20178
+ mode: AlarmArmModeSchema,
20179
+ exitDelaySec: NcAlarmDelaySecSchema.optional(),
20180
+ entryDelaySec: NcAlarmDelaySecSchema.optional(),
20181
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
20182
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20183
+ var NcAlarmModeDelaysEditListSchema = array(object({
20184
+ mode: AlarmArmModeSchema,
20185
+ exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20186
+ entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20187
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
20188
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20189
+ /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20190
+ var NcAlarmNonBlockingEditSchema = object({
20191
+ mode: AlarmArmModeSchema,
20192
+ deviceId: number().int().positive(),
20193
+ /** true = on the list (may be open, does not block); false = off the list (blocks). */
20194
+ nonBlocking: boolean()
20195
+ });
20215
20196
  /**
20216
20197
  * The per-mode lists, at most one per mode. Two lists for one mode have no
20217
20198
  * meaning a reader could agree on (union? the last one?), so they are refused
@@ -20220,10 +20201,10 @@ object({
20220
20201
  var NcAlarmNonBlockingListSchema = array(object({
20221
20202
  mode: AlarmArmModeSchema,
20222
20203
  deviceIds: array(number().int()).max(200)
20223
- })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
20204
+ })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
20224
20205
  /**
20225
- * The three durations the panel's state machine runs on, who hears about its
20226
- * transitions, and which openings each mode tolerates.
20206
+ * The three durations — global, and per mode since D697 — the panel's state
20207
+ * machine runs on, who hears about its transitions, and which openings each mode tolerates.
20227
20208
  *
20228
20209
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
20229
20210
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -20233,15 +20214,15 @@ var NcAlarmNonBlockingListSchema = array(object({
20233
20214
  */
20234
20215
  var NcAlarmSettingsSchema = object({
20235
20216
  /** Grace period between arming and the mode taking effect. 0 = immediate. */
20236
- exitDelaySec: number().int().min(0).max(600),
20217
+ exitDelaySec: NcAlarmDelaySecSchema,
20237
20218
  /** Grace period between a trigger and the alarm firing. 0 = immediate. */
20238
- entryDelaySec: number().int().min(0).max(600),
20219
+ entryDelaySec: NcAlarmDelaySecSchema,
20239
20220
  /**
20240
20221
  * How long `triggered` lasts before the panel re-arms itself.
20241
20222
  * **0 = until an operator disarms it** — the behaviour before this field
20242
20223
  * existed, and therefore what an untouched install keeps doing.
20243
20224
  */
20244
- triggeredDurationSec: number().int().min(0).max(3600),
20225
+ triggeredDurationSec: NcAlarmTriggeredSecSchema,
20245
20226
  /**
20246
20227
  * Deprecated by D630 — every transition is announced. Parsed, never read;
20247
20228
  * remove after one release. Optional so an older admin's patch (which still
@@ -20264,17 +20245,30 @@ var NcAlarmSettingsSchema = object({
20264
20245
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
20265
20246
  * parses as "every opening blocks" — the behaviour it was written under.
20266
20247
  */
20267
- nonBlocking: NcAlarmNonBlockingListSchema.default([])
20248
+ nonBlocking: NcAlarmNonBlockingListSchema.default([]),
20249
+ /**
20250
+ * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
20251
+ * without a field, uses the global value above — so `[]`, the default, is exactly the
20252
+ * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
20253
+ */
20254
+ modeDelays: NcAlarmModeDelaysListSchema.default([])
20268
20255
  });
20269
20256
  /**
20270
20257
  * Every field optional — a tab edits one control at a time.
20271
20258
  *
20272
- * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
20273
- * remove an inner `.default()`, so a patch that never named the field would
20274
- * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
20275
- * (the same trap {@link NcRulePatchSchema} documents).
20259
+ * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
20260
+ * `.partial()` does not remove an inner `.default()`, so a patch that never
20261
+ * named the field would parse to `[]` and wipe every mode's list on an
20262
+ * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
20263
+ * `modeDelays` means EDITS, not the stored overrides.
20276
20264
  */
20277
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
20265
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
20266
+ nonBlocking: NcAlarmNonBlockingListSchema,
20267
+ /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
20268
+ modeDelays: NcAlarmModeDelaysEditListSchema,
20269
+ /** ONE membership change; the panel applies it to the list it holds at its turn. */
20270
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema
20271
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
20278
20272
  /**
20279
20273
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
20280
20274
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -20374,6 +20368,28 @@ var NcAlarmConfigSchema = object({
20374
20368
  */
20375
20369
  live: NcAlarmLiveSchema.optional()
20376
20370
  });
20371
+ var NcAlarmSensorReadingSchema = _enum([
20372
+ "open",
20373
+ "closed",
20374
+ "unknown",
20375
+ "not-opening"
20376
+ ]);
20377
+ /** One device's state as the alarm reads it — the classifier the arm check uses. */
20378
+ var NcAlarmSensorStateSchema = object({
20379
+ deviceId: number().int().positive(),
20380
+ reading: NcAlarmSensorReadingSchema,
20381
+ /** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
20382
+ state: string().nullable(),
20383
+ /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
20384
+ unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
20385
+ /** The device manager's liveness as the centre mirrors it; null = not known. */
20386
+ online: boolean().nullable()
20387
+ });
20388
+ var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
20389
+ var NcAlarmSensorsSchema = object({
20390
+ sensors: array(NcAlarmSensorStateSchema),
20391
+ readAt: number().int().nonnegative()
20392
+ });
20377
20393
  /**
20378
20394
  * ONE rule's demand on ONE camera's clip ring.
20379
20395
  *
@@ -20464,7 +20480,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
20464
20480
  }), method(object({}), NcAlarmConfigSchema, { auth: "admin" }), method(object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
20465
20481
  kind: "mutation",
20466
20482
  auth: "admin"
20467
- });
20483
+ }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
20468
20484
  /**
20469
20485
  * Zod schemas for persisted record types.
20470
20486
  *
@@ -20827,12 +20843,26 @@ var CameraSensorRefSchema = object({
20827
20843
  function cameraSensorRefKey(ref) {
20828
20844
  return `${ref.addonId}/${ref.stableId}`;
20829
20845
  }
20830
- /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20831
- var CameraSensorsModeSchema = _enum(["in-addition"]);
20832
- var CameraSensorsConfigSchema = object({
20833
- mode: CameraSensorsModeSchema,
20834
- sensors: array(CameraSensorRefSchema).min(1).max(8)
20835
- }).superRefine((value, ctx) => {
20846
+ /**
20847
+ * A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
20848
+ * `replace`: its motion is the camera's, and while one is READY the camera's
20849
+ * own motion no longer counts on its own. `confirm`: while one is READY the
20850
+ * camera's own motion counts only while a confirm sensor is active — with or
20851
+ * without a replace sensor (spec C2, the controller's ruling on Q1).
20852
+ */
20853
+ var CameraSensorModeSchema = _enum([
20854
+ "in-addition",
20855
+ "confirm",
20856
+ "replace"
20857
+ ]);
20858
+ /** A stored sensor with no mode — every CS-1 row — is in addition. */
20859
+ var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
20860
+ /**
20861
+ * A camera's attached sensors. A CS-1 row carried an envelope
20862
+ * `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
20863
+ * what the write stores (D672 rule 3), so it is never written again.
20864
+ */
20865
+ var CameraSensorsConfigSchema = object({ sensors: array(CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) })).min(1).max(8) }).superRefine((value, ctx) => {
20836
20866
  const seen = /* @__PURE__ */ new Set();
20837
20867
  value.sensors.forEach((ref, index) => {
20838
20868
  const key = cameraSensorRefKey(ref);
@@ -20844,39 +20874,41 @@ var CameraSensorsConfigSchema = object({
20844
20874
  seen.add(key);
20845
20875
  });
20846
20876
  });
20847
- var RefFields = {
20877
+ /** Every attachment names its mode; an older orchestrator that does not is in addition. */
20878
+ var AttachmentFields = {
20848
20879
  addonId: string(),
20849
- stableId: string()
20880
+ stableId: string(),
20881
+ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
20850
20882
  };
20851
20883
  var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20852
20884
  object({
20853
- ...RefFields,
20885
+ ...AttachmentFields,
20854
20886
  state: literal("ready"),
20855
20887
  deviceId: number(),
20856
20888
  name: string(),
20857
20889
  caps: array(string()).readonly()
20858
20890
  }),
20859
20891
  object({
20860
- ...RefFields,
20892
+ ...AttachmentFields,
20861
20893
  state: literal("offline"),
20862
20894
  deviceId: number(),
20863
20895
  name: string(),
20864
20896
  reason: _enum(["offline", "disabled"])
20865
20897
  }),
20866
20898
  object({
20867
- ...RefFields,
20899
+ ...AttachmentFields,
20868
20900
  state: literal("missing"),
20869
20901
  reason: string()
20870
20902
  }),
20871
20903
  object({
20872
- ...RefFields,
20904
+ ...AttachmentFields,
20873
20905
  state: literal("unsupported"),
20874
20906
  deviceId: number(),
20875
20907
  name: string(),
20876
20908
  reason: string()
20877
20909
  }),
20878
20910
  object({
20879
- ...RefFields,
20911
+ ...AttachmentFields,
20880
20912
  state: literal("unreadable"),
20881
20913
  reason: string()
20882
20914
  })
@@ -20889,17 +20921,49 @@ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20889
20921
  * say.
20890
20922
  */
20891
20923
  var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20924
+ /** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
20925
+ var CameraOwnMotionSchema = _enum([
20926
+ "always",
20927
+ "when-confirmed",
20928
+ "never"
20929
+ ]);
20930
+ /** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
20931
+ var CameraSensorsNotEnforcedSchema = _enum(["provider-not-split"]);
20932
+ /**
20933
+ * The camera's OWN onboard motion (D696): the raw input the bridge combines,
20934
+ * never the effective value. The camera's `motion` slice is the effective one,
20935
+ * so a view of the camera's own row reads this.
20936
+ * - `atMs`: when the bridge received the camera's last own edge.
20937
+ * - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
20938
+ * AND it is younger than the window the bridge counts the camera's own motion
20939
+ * for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
20940
+ * sends a clear (heartbeats only) therefore reads `false` once no push came
20941
+ * within the window; a status read is a snapshot, so a live view re-asks.
20942
+ * Absent = this orchestrator has not received the camera's own motion since it
20943
+ * started (unknown, never "quiet"), or an orchestrator older than D696.
20944
+ */
20945
+ var CameraRawLevelSchema = object({
20946
+ detected: boolean(),
20947
+ atMs: number()
20948
+ });
20892
20949
  var CameraSensorsStatusSchema = discriminatedUnion("state", [
20893
- object({ state: literal("not-configured") }),
20950
+ object({
20951
+ state: literal("not-configured"),
20952
+ cameraLevel: CameraRawLevelSchema.optional()
20953
+ }),
20894
20954
  object({
20895
20955
  state: literal("unreadable"),
20896
- reason: string()
20956
+ reason: string(),
20957
+ cameraLevel: CameraRawLevelSchema.optional()
20897
20958
  }),
20898
20959
  object({
20899
20960
  state: literal("configured"),
20900
- mode: CameraSensorsModeSchema,
20961
+ cameraLevel: CameraRawLevelSchema.optional(),
20901
20962
  sensors: array(CameraSensorAttachmentSchema).readonly(),
20902
- notForwarded: CameraSensorsNotForwardedSchema.optional()
20963
+ /** Absent = an orchestrator older than CS-2: it does not say (D315). */
20964
+ cameraMotion: CameraOwnMotionSchema.optional(),
20965
+ notForwarded: CameraSensorsNotForwardedSchema.optional(),
20966
+ notEnforced: CameraSensorsNotEnforcedSchema.optional()
20903
20967
  })
20904
20968
  ]);
20905
20969
  Object.freeze({
@@ -21584,14 +21648,14 @@ var TrackSchema = object({
21584
21648
  * `derive-returned-null tile=standin`,
21585
21649
  * `native-tile tile=native no-key-frame-at-close`, …) written at close and
21586
21650
  * CLEARED by the late-keyFrame upgrade when a native tile lands after all.
21587
- * The operator-facing answer to "perché manca l'immagine?" on a track whose
21651
+ * The operator-facing answer to "perché manca l'immagine?" [why is the image missing?] on a track whose
21588
21652
  * tile is a face/plate stand-in, a raster crop, or an icon.
21589
21653
  *
21590
21654
  * **`no-key-frame-at-close` (D400).** The line used to be written ONLY when
21591
21655
  * the TILE ranked below native, so a track whose thumbnail landed natively
21592
21656
  * while its key frame missed carried no reason at all — three of the four
21593
21657
  * `MISSING_KEYFRAME` tracks in the 2026-09-07 operator audit were exactly
21594
- * that shape, and their "niente ultimo" had no cause anywhere on the row.
21658
+ * that shape, and their "niente ultimo" [no last frame] had no cause anywhere on the row.
21595
21659
  * The key frame is the head of a cascade (`keyFrame ⇒ keyFrameSmall ⇒ the
21596
21660
  * lastFrame copy source`), so its absence is now on this line whatever the
21597
21661
  * tile ended up being.
@@ -21678,7 +21742,7 @@ var MotionEventSchema = object({
21678
21742
  /**
21679
21743
  * Ms offsets from `timestamp` (the episode's own first rising edge, so the
21680
21744
  * first entry is always `0`) of every genuine off→on transition the
21681
- * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
21745
+ * episode saw — "ogni evento on si deve salvare" [every ON event must be saved] (D475). NOT one entry per
21682
21746
  * push: a firmware source that keepalives at ~1 Hz for the whole burst
21683
21747
  * (Reolink, Hikvision) produces exactly one edge; a source that reports an
21684
21748
  * explicit `false` mid-episode and then resumes before the quiet window
@@ -22202,6 +22266,7 @@ var KeyEventSchema = object({
22202
22266
  bestEventId: string(),
22203
22267
  /** Track lifetime in ms (lastSeen - firstSeen). */
22204
22268
  windowMs: number().optional(),
22269
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
22205
22270
  ...TrackFlagFields,
22206
22271
  ...TrackRetrainFields
22207
22272
  });
@@ -24090,8 +24155,10 @@ var RunnerCameraConfigSchema = object({
24090
24155
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
24091
24156
  * analyzer providers) running on decoded frames in this runner.
24092
24157
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
24093
- * analytics). The provider emits ProviderMotion
24094
- * events; the orchestrator forwards to `reportMotion`.
24158
+ * analytics). The provider emits its raw push as `ProviderMotion`
24159
+ * (D673); the orchestrator's camera-sensors bridge re-emits the camera's
24160
+ * effective motion as `MotionOnMotionChanged`, which is what reaches
24161
+ * `reportMotion`.
24095
24162
  */
24096
24163
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24097
24164
  /**
@@ -24268,7 +24335,15 @@ var RunnerLocalLoadSchema = object({
24268
24335
  hasGpu: boolean(),
24269
24336
  inferenceBackend: string().optional(),
24270
24337
  cpuCores: number().optional()
24271
- })
24338
+ }),
24339
+ /**
24340
+ * Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
24341
+ * runner never writes it. ABSENT = a runner older than D696, which still
24342
+ * writes the slice from its phase machine; the orchestrator does not write
24343
+ * while any runner node is absent-or-unanswered, so one slice never has two
24344
+ * writers.
24345
+ */
24346
+ motionSliceWriter: literal("orchestrator").optional()
24272
24347
  });
24273
24348
  /**
24274
24349
  * Aggregate runtime metrics for the runner's whole local pool. Mirrors the
@@ -26904,7 +26979,7 @@ var ClipPlaybackSchema = object({
26904
26979
  * **One authorised yes is one wake.** `next-natural` — take the clip the next
26905
26980
  * time the camera is awake for its own reasons — is deliberately not a member:
26906
26981
  * it was proposed, it is free on the battery, and the operator declined it on
26907
- * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
26982
+ * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."* [When an export is requested the camera wakes up],
26908
26983
  * D558 "Considered and not taken").
26909
26984
  */
26910
26985
  var ClipWakeSchema = _enum(["authorised"]);
@@ -27165,7 +27240,7 @@ DeviceType.Camera, method(object({
27165
27240
  * It replaced `sources?: string[]`, a VIEW filter over source ids that
27166
27241
  * assumed the answer was a fan-out over everything a camera has. The
27167
27242
  * operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
27168
- * solo"* — so the list is asked of one provider at a time and there is
27243
+ * solo"* [there is only one user] — so the list is asked of one provider at a time and there is
27169
27244
  * nothing to filter out of it.
27170
27245
  */
27171
27246
  provider: string().min(1)
@@ -29109,6 +29184,17 @@ object({
29109
29184
  deviceId: number(),
29110
29185
  status: DeviceStatusSchema
29111
29186
  });
29187
+ object({
29188
+ /** Ms epoch of the last press. null = never observed since this provider started. */
29189
+ lastPressedAt: number().nullable(),
29190
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
29191
+ pressCountSinceStart: number()
29192
+ });
29193
+ object({
29194
+ deviceId: number(),
29195
+ timestamp: number()
29196
+ });
29197
+ DeviceType.Button, DeviceType.Camera;
29112
29198
  /**
29113
29199
  * Enum-state sensor — a string value picked from a finite option set.
29114
29200
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -30753,9 +30839,9 @@ object({
30753
30839
  /** Ms epoch of the last detected-true observation. Null if never detected. */
30754
30840
  lastDetectedAt: number().nullable(),
30755
30841
  /**
30756
- * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
30757
- * `null` while false and on every `Sensor` device (D475) — see that
30758
- * constant's doc for the one-authority rule.
30842
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
30843
+ * orchestrator writes it, D696), `null` while false and on every `Sensor`
30844
+ * device (D475) — see that constant's doc for the one-authority rule.
30759
30845
  *
30760
30846
  * ## Reading this field still arms nothing
30761
30847
  *
@@ -30773,38 +30859,41 @@ object({
30773
30859
  * `addon-export-alexa/src/motion-clear-hold.ts` and
30774
30860
  * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
30775
30861
  *
30776
- * ## Who writes it
30777
- *
30778
- * - **Cameras** — the runner's phase machine, `active → watching` on
30779
- * `cooldown_expired`, which then writes this slice with
30780
- * `detected: false` (`handlePhaseChanged` in
30781
- * `pipeline-runner/index.ts`). It produces the FALLING edge, which
30782
- * matters most for the sources that only ever push a rising one:
30783
- * Reolink emits `MotionOnMotionChanged { detected: true }` and never
30784
- * a false.
30785
- * - **Sensors** (Home Assistant binary sensors, Homematic) — the
30786
- * provider pushes the false itself, from the upstream system's own
30787
- * state change. No phase machine is involved.
30788
- *
30789
- * ### The phase machine is CANONICAL, not sole — and that is a defect
30790
- *
30791
- * An earlier revision of this docblock (mine, 2026-09-12) claimed the
30792
- * phase machine is the sole writer for a camera. It is not.
30793
- * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
30794
- * `setCapSlice(motionCapability, …)` on their own rising edge, and
30795
- * Hikvision's comment says why: it read THIS docblock, agreed the
30796
- * runner is canonical, and wrote anyway to avoid per-tick churn. So
30797
- * two authorities can disagree about one slice, which this repo
30798
- * forbids, and the doc said otherwise — which is worse than saying
30799
- * nothing, because it reads as verification.
30800
- *
30801
- * This predates D475 and is not fixed there: the fix touches every
30802
- * camera provider. Recorded in D475's Consequences. Do not restore the
30803
- * "sole writer" wording without also removing the other writers.
30862
+ * ## Who writes it (D696)
30863
+ *
30864
+ * - **Cameras** — the orchestrator, and nothing else. It writes the camera's
30865
+ * EFFECTIVE motion (`addon-pipeline-orchestrator`
30866
+ * `camera-sensors/motion-slice.ts`), alongside the effective
30867
+ * `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
30868
+ * for a split camera, an unsplit emitter's direct event, the analyzer and
30869
+ * device activity, as the camera's `motionSources` counts them. A source
30870
+ * that never sends a clear falls one window after its last `true`.
30871
+ * `lastDetectedAt` moves only on an effective rise. No camera provider
30872
+ * writes this slice (not even a boot seed), and neither does the runner's
30873
+ * phase machine: its phase is "a session is attached" for every on-motion
30874
+ * session, not motion, and it now has its own `DetectionPhaseTransition`.
30875
+ * A provider's `isDetected` answers from the hub's slice.
30876
+ * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
30877
+ * imports) — the provider pushes both edges itself, from the upstream
30878
+ * system's own state change. That is the sensor's own fact.
30879
+ *
30880
+ * `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
30881
+ * `motion` slice; each sensor writer is a named exemption.
30882
+ *
30883
+ * This replaces the D475 §4 rule ("the phase machine is the sole writer").
30884
+ * That rule was never true: Hikvision and Amcrest wrote the slice raw beside
30885
+ * it, every provider seeded it at boot, and every session left it
30886
+ * `detected: true` after its detach.
30804
30887
  */
30805
30888
  autoClearAfterMs: number().nullable()
30806
30889
  });
30807
- object({
30890
+ /**
30891
+ * Payload of `motion.onMotionChanged` event + the corresponding bus
30892
+ * event `EventCategory.MotionOnMotionChanged`. Single source of truth
30893
+ * — both the cap event surface and the bus payload type alias to this
30894
+ * schema.
30895
+ */
30896
+ var MotionOnMotionChangedDataSchema = object({
30808
30897
  deviceId: number(),
30809
30898
  detected: boolean(),
30810
30899
  timestamp: number(),
@@ -33269,7 +33358,7 @@ var ReadGopBytesResultSchema = object({
33269
33358
  /**
33270
33359
  * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
33271
33360
  * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
33272
- * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
33361
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-clip-replay-on-pipeline.md`):
33273
33362
  * a replay needs several seconds of native pixels, not one frame.
33274
33363
  *
33275
33364
  * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
@@ -35885,6 +35974,7 @@ DeviceType.Camera, method(object({
35885
35974
  detection: array(ZoneRuleSchema).readonly(),
35886
35975
  package: array(ZoneRuleSchema).readonly()
35887
35976
  });
35977
+ SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
35888
35978
  /**
35889
35979
  * Accessory device helpers — shared across drivers.
35890
35980
  *
@@ -40863,6 +40953,12 @@ Object.freeze({
40863
40953
  addonId: null,
40864
40954
  access: "view"
40865
40955
  },
40956
+ "notificationRules.getAlarmSensors": {
40957
+ capName: "notification-rules",
40958
+ capScope: "system",
40959
+ addonId: null,
40960
+ access: "view"
40961
+ },
40866
40962
  "notificationRules.getClipRetentionAsks": {
40867
40963
  capName: "notification-rules",
40868
40964
  capScope: "system",
@@ -44441,6 +44537,11 @@ Object.freeze({
44441
44537
  }
44442
44538
  });
44443
44539
  Object.freeze({
44540
+ "accessories.getStatus": [{
44541
+ name: "deviceId",
44542
+ form: "single",
44543
+ optional: false
44544
+ }],
44444
44545
  "accessories.setChildHidden": [{
44445
44546
  name: "childDeviceId",
44446
44547
  form: "single",
@@ -44460,6 +44561,11 @@ Object.freeze({
44460
44561
  form: "single",
44461
44562
  optional: false
44462
44563
  }],
44564
+ "airQualitySensor.getStatus": [{
44565
+ name: "deviceId",
44566
+ form: "single",
44567
+ optional: false
44568
+ }],
44463
44569
  "alarmPanel.arm": [{
44464
44570
  name: "deviceId",
44465
44571
  form: "single",
@@ -44470,11 +44576,36 @@ Object.freeze({
44470
44576
  form: "single",
44471
44577
  optional: false
44472
44578
  }],
44579
+ "alarmPanel.getStatus": [{
44580
+ name: "deviceId",
44581
+ form: "single",
44582
+ optional: false
44583
+ }],
44473
44584
  "alarmPanel.trigger": [{
44474
44585
  name: "deviceId",
44475
44586
  form: "single",
44476
44587
  optional: false
44477
44588
  }],
44589
+ "ambientLightSensor.getStatus": [{
44590
+ name: "deviceId",
44591
+ form: "single",
44592
+ optional: false
44593
+ }],
44594
+ "audioAnalysis.applyDeviceSettingsPatch": [{
44595
+ name: "deviceId",
44596
+ form: "single",
44597
+ optional: false
44598
+ }],
44599
+ "audioAnalysis.getDeviceLiveContribution": [{
44600
+ name: "deviceId",
44601
+ form: "single",
44602
+ optional: false
44603
+ }],
44604
+ "audioAnalysis.getDeviceSettingsContribution": [{
44605
+ name: "deviceId",
44606
+ form: "single",
44607
+ optional: false
44608
+ }],
44478
44609
  "audioAnalysis.resolveDeviceSettings": [{
44479
44610
  name: "deviceId",
44480
44611
  form: "single",
@@ -44515,16 +44646,36 @@ Object.freeze({
44515
44646
  form: "single",
44516
44647
  optional: false
44517
44648
  }],
44649
+ "automationControl.getStatus": [{
44650
+ name: "deviceId",
44651
+ form: "single",
44652
+ optional: false
44653
+ }],
44518
44654
  "automationControl.trigger": [{
44519
44655
  name: "deviceId",
44520
44656
  form: "single",
44521
44657
  optional: false
44522
44658
  }],
44659
+ "battery.getStatus": [{
44660
+ name: "deviceId",
44661
+ form: "single",
44662
+ optional: false
44663
+ }],
44523
44664
  "battery.wakeForStream": [{
44524
44665
  name: "deviceId",
44525
44666
  form: "single",
44526
44667
  optional: false
44527
44668
  }],
44669
+ "binary.getStatus": [{
44670
+ name: "deviceId",
44671
+ form: "single",
44672
+ optional: false
44673
+ }],
44674
+ "brightness.getStatus": [{
44675
+ name: "deviceId",
44676
+ form: "single",
44677
+ optional: false
44678
+ }],
44528
44679
  "brightness.setBrightness": [{
44529
44680
  name: "deviceId",
44530
44681
  form: "single",
@@ -44540,6 +44691,26 @@ Object.freeze({
44540
44691
  form: "single",
44541
44692
  optional: false
44542
44693
  }],
44694
+ "cameraCredentials.getStatus": [{
44695
+ name: "deviceId",
44696
+ form: "single",
44697
+ optional: false
44698
+ }],
44699
+ "cameraGridLayout.applyDeviceSettingsPatch": [{
44700
+ name: "deviceId",
44701
+ form: "single",
44702
+ optional: false
44703
+ }],
44704
+ "cameraGridLayout.getDeviceLiveContribution": [{
44705
+ name: "deviceId",
44706
+ form: "single",
44707
+ optional: false
44708
+ }],
44709
+ "cameraGridLayout.getDeviceSettingsContribution": [{
44710
+ name: "deviceId",
44711
+ form: "single",
44712
+ optional: false
44713
+ }],
44543
44714
  "cameraGridLayout.getLayout": [{
44544
44715
  name: "deviceId",
44545
44716
  form: "single",
@@ -44555,6 +44726,21 @@ Object.freeze({
44555
44726
  form: "single",
44556
44727
  optional: false
44557
44728
  }],
44729
+ "cameraPipelineConfig.applyDeviceSettingsPatch": [{
44730
+ name: "deviceId",
44731
+ form: "single",
44732
+ optional: false
44733
+ }],
44734
+ "cameraPipelineConfig.getDeviceLiveContribution": [{
44735
+ name: "deviceId",
44736
+ form: "single",
44737
+ optional: false
44738
+ }],
44739
+ "cameraPipelineConfig.getDeviceSettingsContribution": [{
44740
+ name: "deviceId",
44741
+ form: "single",
44742
+ optional: false
44743
+ }],
44558
44744
  "cameraStreams.getBrokerStreams": [{
44559
44745
  name: "deviceId",
44560
44746
  form: "single",
@@ -44580,6 +44766,16 @@ Object.freeze({
44580
44766
  form: "single",
44581
44767
  optional: false
44582
44768
  }],
44769
+ "carbonMonoxide.getStatus": [{
44770
+ name: "deviceId",
44771
+ form: "single",
44772
+ optional: false
44773
+ }],
44774
+ "climateControl.getStatus": [{
44775
+ name: "deviceId",
44776
+ form: "single",
44777
+ optional: false
44778
+ }],
44583
44779
  "climateControl.setFanMode": [{
44584
44780
  name: "deviceId",
44585
44781
  form: "single",
@@ -44620,16 +44816,41 @@ Object.freeze({
44620
44816
  form: "single",
44621
44817
  optional: false
44622
44818
  }],
44819
+ "color.getStatus": [{
44820
+ name: "deviceId",
44821
+ form: "single",
44822
+ optional: false
44823
+ }],
44623
44824
  "color.setColor": [{
44624
44825
  name: "deviceId",
44625
44826
  form: "single",
44626
44827
  optional: false
44627
44828
  }],
44829
+ "connectivity.getStatus": [{
44830
+ name: "deviceId",
44831
+ form: "single",
44832
+ optional: false
44833
+ }],
44834
+ "consumables.getStatus": [{
44835
+ name: "deviceId",
44836
+ form: "single",
44837
+ optional: false
44838
+ }],
44628
44839
  "consumables.reset": [{
44629
44840
  name: "deviceId",
44630
44841
  form: "single",
44631
44842
  optional: false
44632
44843
  }],
44844
+ "contact.getStatus": [{
44845
+ name: "deviceId",
44846
+ form: "single",
44847
+ optional: false
44848
+ }],
44849
+ "control.getStatus": [{
44850
+ name: "deviceId",
44851
+ form: "single",
44852
+ optional: false
44853
+ }],
44633
44854
  "control.setValue": [{
44634
44855
  name: "deviceId",
44635
44856
  form: "single",
@@ -44640,6 +44861,11 @@ Object.freeze({
44640
44861
  form: "single",
44641
44862
  optional: false
44642
44863
  }],
44864
+ "cover.getStatus": [{
44865
+ name: "deviceId",
44866
+ form: "single",
44867
+ optional: false
44868
+ }],
44643
44869
  "cover.open": [{
44644
44870
  name: "deviceId",
44645
44871
  form: "single",
@@ -44665,6 +44891,11 @@ Object.freeze({
44665
44891
  form: "single",
44666
44892
  optional: false
44667
44893
  }],
44894
+ "dayNight.getStatus": [{
44895
+ name: "deviceId",
44896
+ form: "single",
44897
+ optional: false
44898
+ }],
44668
44899
  "dayNight.setSettings": [{
44669
44900
  name: "deviceId",
44670
44901
  form: "single",
@@ -44675,11 +44906,31 @@ Object.freeze({
44675
44906
  form: "single",
44676
44907
  optional: true
44677
44908
  }],
44909
+ "detectionPipeline.applyDeviceSettingsPatch": [{
44910
+ name: "deviceId",
44911
+ form: "single",
44912
+ optional: false
44913
+ }],
44914
+ "detectionPipeline.getDeviceLiveContribution": [{
44915
+ name: "deviceId",
44916
+ form: "single",
44917
+ optional: false
44918
+ }],
44919
+ "detectionPipeline.getDeviceSettingsContribution": [{
44920
+ name: "deviceId",
44921
+ form: "single",
44922
+ optional: false
44923
+ }],
44678
44924
  "deviceAdminLink.getAdminLink": [{
44679
44925
  name: "deviceId",
44680
44926
  form: "single",
44681
44927
  optional: false
44682
44928
  }],
44929
+ "deviceAdoption.getStatus": [{
44930
+ name: "deviceId",
44931
+ form: "single",
44932
+ optional: false
44933
+ }],
44683
44934
  "deviceAdoption.release": [{
44684
44935
  name: "camDeviceId",
44685
44936
  form: "single",
@@ -44695,6 +44946,11 @@ Object.freeze({
44695
44946
  form: "single",
44696
44947
  optional: false
44697
44948
  }],
44949
+ "deviceDiscovery.getStatus": [{
44950
+ name: "deviceId",
44951
+ form: "single",
44952
+ optional: false
44953
+ }],
44698
44954
  "deviceDiscovery.listDiscovered": [{
44699
44955
  name: "deviceId",
44700
44956
  form: "single",
@@ -44714,6 +44970,21 @@ Object.freeze({
44714
44970
  form: "single",
44715
44971
  optional: false
44716
44972
  }],
44973
+ "deviceExport.applyDeviceSettingsPatch": [{
44974
+ name: "deviceId",
44975
+ form: "single",
44976
+ optional: false
44977
+ }],
44978
+ "deviceExport.getDeviceLiveContribution": [{
44979
+ name: "deviceId",
44980
+ form: "single",
44981
+ optional: false
44982
+ }],
44983
+ "deviceExport.getDeviceSettingsContribution": [{
44984
+ name: "deviceId",
44985
+ form: "single",
44986
+ optional: false
44987
+ }],
44717
44988
  "deviceExtension.listCandidates": [{
44718
44989
  name: "deviceId",
44719
44990
  form: "single",
@@ -45062,6 +45333,26 @@ Object.freeze({
45062
45333
  form: "single",
45063
45334
  optional: false
45064
45335
  }],
45336
+ "deviceStatus.getStatus": [{
45337
+ name: "deviceId",
45338
+ form: "single",
45339
+ optional: false
45340
+ }],
45341
+ "doorbell.getStatus": [{
45342
+ name: "deviceId",
45343
+ form: "single",
45344
+ optional: false
45345
+ }],
45346
+ "enumSensor.getStatus": [{
45347
+ name: "deviceId",
45348
+ form: "single",
45349
+ optional: false
45350
+ }],
45351
+ "eventEmitter.getStatus": [{
45352
+ name: "deviceId",
45353
+ form: "single",
45354
+ optional: false
45355
+ }],
45065
45356
  "faceGallery.getFaceByTrack": [{
45066
45357
  name: "deviceId",
45067
45358
  form: "single",
@@ -45076,6 +45367,11 @@ Object.freeze({
45076
45367
  form: "array",
45077
45368
  optional: true
45078
45369
  }],
45370
+ "fanControl.getStatus": [{
45371
+ name: "deviceId",
45372
+ form: "single",
45373
+ optional: false
45374
+ }],
45079
45375
  "fanControl.setDirection": [{
45080
45376
  name: "deviceId",
45081
45377
  form: "single",
@@ -45096,6 +45392,26 @@ Object.freeze({
45096
45392
  form: "single",
45097
45393
  optional: false
45098
45394
  }],
45395
+ "featureProbe.getStatus": [{
45396
+ name: "deviceId",
45397
+ form: "single",
45398
+ optional: false
45399
+ }],
45400
+ "flood.getStatus": [{
45401
+ name: "deviceId",
45402
+ form: "single",
45403
+ optional: false
45404
+ }],
45405
+ "gas.getStatus": [{
45406
+ name: "deviceId",
45407
+ form: "single",
45408
+ optional: false
45409
+ }],
45410
+ "humidifier.getStatus": [{
45411
+ name: "deviceId",
45412
+ form: "single",
45413
+ optional: false
45414
+ }],
45099
45415
  "humidifier.setMode": [{
45100
45416
  name: "deviceId",
45101
45417
  form: "single",
@@ -45111,11 +45427,26 @@ Object.freeze({
45111
45427
  form: "single",
45112
45428
  optional: false
45113
45429
  }],
45430
+ "humiditySensor.getStatus": [{
45431
+ name: "deviceId",
45432
+ form: "single",
45433
+ optional: false
45434
+ }],
45435
+ "image.getStatus": [{
45436
+ name: "deviceId",
45437
+ form: "single",
45438
+ optional: false
45439
+ }],
45114
45440
  "imageSettings.getOptions": [{
45115
45441
  name: "deviceId",
45116
45442
  form: "single",
45117
45443
  optional: false
45118
45444
  }],
45445
+ "imageSettings.getStatus": [{
45446
+ name: "deviceId",
45447
+ form: "single",
45448
+ optional: false
45449
+ }],
45119
45450
  "imageSettings.setSettings": [{
45120
45451
  name: "deviceId",
45121
45452
  form: "single",
@@ -45126,6 +45457,11 @@ Object.freeze({
45126
45457
  form: "single",
45127
45458
  optional: false
45128
45459
  }],
45460
+ "intercom.getStatus": [{
45461
+ name: "deviceId",
45462
+ form: "single",
45463
+ optional: false
45464
+ }],
45129
45465
  "intercom.handleAnswer": [{
45130
45466
  name: "deviceId",
45131
45467
  form: "single",
@@ -45156,6 +45492,11 @@ Object.freeze({
45156
45492
  form: "single",
45157
45493
  optional: false
45158
45494
  }],
45495
+ "lawnMowerControl.getStatus": [{
45496
+ name: "deviceId",
45497
+ form: "single",
45498
+ optional: false
45499
+ }],
45159
45500
  "lawnMowerControl.pause": [{
45160
45501
  name: "deviceId",
45161
45502
  form: "single",
@@ -45166,6 +45507,11 @@ Object.freeze({
45166
45507
  form: "single",
45167
45508
  optional: false
45168
45509
  }],
45510
+ "lockControl.getStatus": [{
45511
+ name: "deviceId",
45512
+ form: "single",
45513
+ optional: false
45514
+ }],
45169
45515
  "lockControl.lock": [{
45170
45516
  name: "deviceId",
45171
45517
  form: "single",
@@ -45181,6 +45527,11 @@ Object.freeze({
45181
45527
  form: "single",
45182
45528
  optional: false
45183
45529
  }],
45530
+ "mediaPlayer.getStatus": [{
45531
+ name: "deviceId",
45532
+ form: "single",
45533
+ optional: false
45534
+ }],
45184
45535
  "mediaPlayer.next": [{
45185
45536
  name: "deviceId",
45186
45537
  form: "single",
@@ -45241,6 +45592,11 @@ Object.freeze({
45241
45592
  form: "single",
45242
45593
  optional: false
45243
45594
  }],
45595
+ "motion.getStatus": [{
45596
+ name: "deviceId",
45597
+ form: "single",
45598
+ optional: false
45599
+ }],
45244
45600
  "motion.isDetected": [{
45245
45601
  name: "deviceId",
45246
45602
  form: "single",
@@ -45251,11 +45607,31 @@ Object.freeze({
45251
45607
  form: "single",
45252
45608
  optional: false
45253
45609
  }],
45610
+ "motionDetection.applyDeviceSettingsPatch": [{
45611
+ name: "deviceId",
45612
+ form: "single",
45613
+ optional: false
45614
+ }],
45615
+ "motionDetection.getDeviceLiveContribution": [{
45616
+ name: "deviceId",
45617
+ form: "single",
45618
+ optional: false
45619
+ }],
45620
+ "motionDetection.getDeviceSettingsContribution": [{
45621
+ name: "deviceId",
45622
+ form: "single",
45623
+ optional: false
45624
+ }],
45254
45625
  "motionDetection.removeCamera": [{
45255
45626
  name: "deviceId",
45256
45627
  form: "single",
45257
45628
  optional: false
45258
45629
  }],
45630
+ "motionTrigger.getStatus": [{
45631
+ name: "deviceId",
45632
+ form: "single",
45633
+ optional: false
45634
+ }],
45259
45635
  "motionTrigger.setMotionTrigger": [{
45260
45636
  name: "deviceId",
45261
45637
  form: "single",
@@ -45266,6 +45642,11 @@ Object.freeze({
45266
45642
  form: "single",
45267
45643
  optional: false
45268
45644
  }],
45645
+ "motionZones.getStatus": [{
45646
+ name: "deviceId",
45647
+ form: "single",
45648
+ optional: false
45649
+ }],
45269
45650
  "motionZones.setZone": [{
45270
45651
  name: "deviceId",
45271
45652
  form: "single",
@@ -45276,6 +45657,11 @@ Object.freeze({
45276
45657
  form: "single",
45277
45658
  optional: false
45278
45659
  }],
45660
+ "nativeObjectDetection.getStatus": [{
45661
+ name: "deviceId",
45662
+ form: "single",
45663
+ optional: false
45664
+ }],
45279
45665
  "nativeObjectDetection.setEnabled": [{
45280
45666
  name: "deviceId",
45281
45667
  form: "single",
@@ -45291,6 +45677,11 @@ Object.freeze({
45291
45677
  form: "single",
45292
45678
  optional: false
45293
45679
  }],
45680
+ "navigation.getStatus": [{
45681
+ name: "deviceId",
45682
+ form: "single",
45683
+ optional: false
45684
+ }],
45294
45685
  "navigation.goToPoint": [{
45295
45686
  name: "deviceId",
45296
45687
  form: "single",
@@ -45336,6 +45727,11 @@ Object.freeze({
45336
45727
  form: "single",
45337
45728
  optional: false
45338
45729
  }],
45730
+ "networkLink.getStatus": [{
45731
+ name: "deviceId",
45732
+ form: "single",
45733
+ optional: false
45734
+ }],
45339
45735
  "networkQuality.getDeviceStats": [{
45340
45736
  name: "deviceId",
45341
45737
  form: "single",
@@ -45346,6 +45742,11 @@ Object.freeze({
45346
45742
  form: "single",
45347
45743
  optional: false
45348
45744
  }],
45745
+ "notificationRules.getAlarmSensors": [{
45746
+ name: "deviceIds",
45747
+ form: "array",
45748
+ optional: false
45749
+ }],
45349
45750
  "notificationRules.setDeviceMuted": [{
45350
45751
  name: "deviceId",
45351
45752
  form: "single",
@@ -45356,11 +45757,26 @@ Object.freeze({
45356
45757
  form: "single",
45357
45758
  optional: false
45358
45759
  }],
45760
+ "notifier.getStatus": [{
45761
+ name: "deviceId",
45762
+ form: "single",
45763
+ optional: false
45764
+ }],
45359
45765
  "notifier.send": [{
45360
45766
  name: "deviceId",
45361
45767
  form: "single",
45362
45768
  optional: false
45363
45769
  }],
45770
+ "numericSensor.getStatus": [{
45771
+ name: "deviceId",
45772
+ form: "single",
45773
+ optional: false
45774
+ }],
45775
+ "osd.getStatus": [{
45776
+ name: "deviceId",
45777
+ form: "single",
45778
+ optional: false
45779
+ }],
45364
45780
  "osd.setOverlay": [{
45365
45781
  name: "deviceId",
45366
45782
  form: "single",
@@ -45420,6 +45836,11 @@ Object.freeze({
45420
45836
  form: "single",
45421
45837
  optional: false
45422
45838
  }],
45839
+ "petFeeder.getStatus": [{
45840
+ name: "deviceId",
45841
+ form: "single",
45842
+ optional: false
45843
+ }],
45423
45844
  "petFeeder.markFoodReplenished": [{
45424
45845
  name: "deviceId",
45425
45846
  form: "single",
@@ -45455,6 +45876,11 @@ Object.freeze({
45455
45876
  form: "single",
45456
45877
  optional: false
45457
45878
  }],
45879
+ "pipelineAnalytics.applyDeviceSettingsPatch": [{
45880
+ name: "deviceId",
45881
+ form: "single",
45882
+ optional: false
45883
+ }],
45458
45884
  "pipelineAnalytics.clearTrackRelation": [{
45459
45885
  name: "deviceId",
45460
45886
  form: "single",
@@ -45495,6 +45921,16 @@ Object.freeze({
45495
45921
  form: "single",
45496
45922
  optional: false
45497
45923
  }],
45924
+ "pipelineAnalytics.getDeviceLiveContribution": [{
45925
+ name: "deviceId",
45926
+ form: "single",
45927
+ optional: false
45928
+ }],
45929
+ "pipelineAnalytics.getDeviceSettingsContribution": [{
45930
+ name: "deviceId",
45931
+ form: "single",
45932
+ optional: false
45933
+ }],
45498
45934
  "pipelineAnalytics.getEventDensity": [{
45499
45935
  name: "deviceId",
45500
45936
  form: "single",
@@ -45710,6 +46146,11 @@ Object.freeze({
45710
46146
  form: "single",
45711
46147
  optional: true
45712
46148
  }],
46149
+ "pipelineOrchestrator.applyDeviceSettingsPatch": [{
46150
+ name: "deviceId",
46151
+ form: "single",
46152
+ optional: false
46153
+ }],
45713
46154
  "pipelineOrchestrator.assignAudio": [{
45714
46155
  name: "deviceId",
45715
46156
  form: "single",
@@ -45755,6 +46196,16 @@ Object.freeze({
45755
46196
  form: "single",
45756
46197
  optional: false
45757
46198
  }],
46199
+ "pipelineOrchestrator.getDeviceLiveContribution": [{
46200
+ name: "deviceId",
46201
+ form: "single",
46202
+ optional: false
46203
+ }],
46204
+ "pipelineOrchestrator.getDeviceSettingsContribution": [{
46205
+ name: "deviceId",
46206
+ form: "single",
46207
+ optional: false
46208
+ }],
45758
46209
  "pipelineOrchestrator.getPipelineAssignment": [{
45759
46210
  name: "deviceId",
45760
46211
  form: "single",
@@ -45875,11 +46326,31 @@ Object.freeze({
45875
46326
  form: "single",
45876
46327
  optional: true
45877
46328
  }],
46329
+ "powerMeter.getStatus": [{
46330
+ name: "deviceId",
46331
+ form: "single",
46332
+ optional: false
46333
+ }],
46334
+ "presence.getStatus": [{
46335
+ name: "deviceId",
46336
+ form: "single",
46337
+ optional: false
46338
+ }],
46339
+ "pressureSensor.getStatus": [{
46340
+ name: "deviceId",
46341
+ form: "single",
46342
+ optional: false
46343
+ }],
45878
46344
  "privacyMask.getOptions": [{
45879
46345
  name: "deviceId",
45880
46346
  form: "single",
45881
46347
  optional: false
45882
46348
  }],
46349
+ "privacyMask.getStatus": [{
46350
+ name: "deviceId",
46351
+ form: "single",
46352
+ optional: false
46353
+ }],
45883
46354
  "privacyMask.setAudioEnabled": [{
45884
46355
  name: "deviceId",
45885
46356
  form: "single",
@@ -45930,6 +46401,11 @@ Object.freeze({
45930
46401
  form: "single",
45931
46402
  optional: false
45932
46403
  }],
46404
+ "ptz.getStatus": [{
46405
+ name: "deviceId",
46406
+ form: "single",
46407
+ optional: false
46408
+ }],
45933
46409
  "ptz.goHome": [{
45934
46410
  name: "deviceId",
45935
46411
  form: "single",
@@ -46020,6 +46496,11 @@ Object.freeze({
46020
46496
  form: "single",
46021
46497
  optional: false
46022
46498
  }],
46499
+ "recordingArchive.applyDeviceSettingsPatch": [{
46500
+ name: "deviceId",
46501
+ form: "single",
46502
+ optional: false
46503
+ }],
46023
46504
  "recordingArchive.deleteFootprint": [{
46024
46505
  name: "deviceId",
46025
46506
  form: "single",
@@ -46040,6 +46521,21 @@ Object.freeze({
46040
46521
  form: "single",
46041
46522
  optional: false
46042
46523
  }],
46524
+ "recordingArchive.getDeviceLiveContribution": [{
46525
+ name: "deviceId",
46526
+ form: "single",
46527
+ optional: false
46528
+ }],
46529
+ "recordingArchive.getDeviceSettingsContribution": [{
46530
+ name: "deviceId",
46531
+ form: "single",
46532
+ optional: false
46533
+ }],
46534
+ "recordingArchive.getStatus": [{
46535
+ name: "deviceId",
46536
+ form: "single",
46537
+ optional: false
46538
+ }],
46043
46539
  "recordingArchive.listOpsLog": [{
46044
46540
  name: "deviceId",
46045
46541
  form: "single",
@@ -46125,6 +46621,11 @@ Object.freeze({
46125
46621
  form: "single",
46126
46622
  optional: false
46127
46623
  }],
46624
+ "recordingOnboard.getStatus": [{
46625
+ name: "deviceId",
46626
+ form: "single",
46627
+ optional: false
46628
+ }],
46128
46629
  "recordingOnboard.setSettings": [{
46129
46630
  name: "deviceId",
46130
46631
  form: "single",
@@ -46155,6 +46656,11 @@ Object.freeze({
46155
46656
  form: "single",
46156
46657
  optional: false
46157
46658
  }],
46659
+ "sceneMonitor.getStatus": [{
46660
+ name: "deviceId",
46661
+ form: "single",
46662
+ optional: false
46663
+ }],
46158
46664
  "sceneMonitor.listScenes": [{
46159
46665
  name: "deviceId",
46160
46666
  form: "single",
@@ -46180,6 +46686,11 @@ Object.freeze({
46180
46686
  form: "single",
46181
46687
  optional: false
46182
46688
  }],
46689
+ "scriptRunner.getStatus": [{
46690
+ name: "deviceId",
46691
+ form: "single",
46692
+ optional: false
46693
+ }],
46183
46694
  "scriptRunner.run": [{
46184
46695
  name: "deviceId",
46185
46696
  form: "single",
@@ -46190,11 +46701,31 @@ Object.freeze({
46190
46701
  form: "single",
46191
46702
  optional: false
46192
46703
  }],
46704
+ "smoke.getStatus": [{
46705
+ name: "deviceId",
46706
+ form: "single",
46707
+ optional: false
46708
+ }],
46709
+ "snapshot.applyDeviceSettingsPatch": [{
46710
+ name: "deviceId",
46711
+ form: "single",
46712
+ optional: false
46713
+ }],
46193
46714
  "snapshot.getDebugState": [{
46194
46715
  name: "deviceId",
46195
46716
  form: "single",
46196
46717
  optional: false
46197
46718
  }],
46719
+ "snapshot.getDeviceLiveContribution": [{
46720
+ name: "deviceId",
46721
+ form: "single",
46722
+ optional: false
46723
+ }],
46724
+ "snapshot.getDeviceSettingsContribution": [{
46725
+ name: "deviceId",
46726
+ form: "single",
46727
+ optional: false
46728
+ }],
46198
46729
  "snapshot.getSnapshot": [{
46199
46730
  name: "deviceId",
46200
46731
  form: "single",
@@ -46211,6 +46742,11 @@ Object.freeze({
46211
46742
  form: "array",
46212
46743
  optional: false
46213
46744
  }],
46745
+ "snapshot.getStatus": [{
46746
+ name: "deviceId",
46747
+ form: "single",
46748
+ optional: false
46749
+ }],
46214
46750
  "snapshot.invalidateCache": [{
46215
46751
  name: "deviceId",
46216
46752
  form: "single",
@@ -46221,6 +46757,11 @@ Object.freeze({
46221
46757
  form: "single",
46222
46758
  optional: false
46223
46759
  }],
46760
+ "streamBroker.applyDeviceSettingsPatch": [{
46761
+ name: "deviceId",
46762
+ form: "single",
46763
+ optional: false
46764
+ }],
46224
46765
  "streamBroker.assignProfile": [{
46225
46766
  name: "deviceId",
46226
46767
  form: "single",
@@ -46236,6 +46777,16 @@ Object.freeze({
46236
46777
  form: "single",
46237
46778
  optional: false
46238
46779
  }],
46780
+ "streamBroker.getDeviceLiveContribution": [{
46781
+ name: "deviceId",
46782
+ form: "single",
46783
+ optional: false
46784
+ }],
46785
+ "streamBroker.getDeviceSettingsContribution": [{
46786
+ name: "deviceId",
46787
+ form: "single",
46788
+ optional: false
46789
+ }],
46239
46790
  "streamBroker.getStreamWithCodec": [{
46240
46791
  name: "deviceId",
46241
46792
  form: "single",
@@ -46296,16 +46847,46 @@ Object.freeze({
46296
46847
  form: "single",
46297
46848
  optional: false
46298
46849
  }],
46850
+ "streamParams.getStatus": [{
46851
+ name: "deviceId",
46852
+ form: "single",
46853
+ optional: false
46854
+ }],
46299
46855
  "streamParams.setProfile": [{
46300
46856
  name: "deviceId",
46301
46857
  form: "single",
46302
46858
  optional: false
46303
46859
  }],
46860
+ "switch.getStatus": [{
46861
+ name: "deviceId",
46862
+ form: "single",
46863
+ optional: false
46864
+ }],
46304
46865
  "switch.setState": [{
46305
46866
  name: "deviceId",
46306
46867
  form: "single",
46307
46868
  optional: false
46308
46869
  }],
46870
+ "tamper.getStatus": [{
46871
+ name: "deviceId",
46872
+ form: "single",
46873
+ optional: false
46874
+ }],
46875
+ "temperatureSensor.getStatus": [{
46876
+ name: "deviceId",
46877
+ form: "single",
46878
+ optional: false
46879
+ }],
46880
+ "update.getStatus": [{
46881
+ name: "deviceId",
46882
+ form: "single",
46883
+ optional: false
46884
+ }],
46885
+ "vacuumControl.getStatus": [{
46886
+ name: "deviceId",
46887
+ form: "single",
46888
+ optional: false
46889
+ }],
46309
46890
  "vacuumControl.locate": [{
46310
46891
  name: "deviceId",
46311
46892
  form: "single",
@@ -46341,6 +46922,11 @@ Object.freeze({
46341
46922
  form: "single",
46342
46923
  optional: false
46343
46924
  }],
46925
+ "valve.getStatus": [{
46926
+ name: "deviceId",
46927
+ form: "single",
46928
+ optional: false
46929
+ }],
46344
46930
  "valve.open": [{
46345
46931
  name: "deviceId",
46346
46932
  form: "single",
@@ -46356,6 +46942,11 @@ Object.freeze({
46356
46942
  form: "single",
46357
46943
  optional: false
46358
46944
  }],
46945
+ "vibration.getStatus": [{
46946
+ name: "deviceId",
46947
+ form: "single",
46948
+ optional: false
46949
+ }],
46359
46950
  "videoclips.dialClipStream": [{
46360
46951
  name: "deviceId",
46361
46952
  form: "single",
@@ -46391,6 +46982,11 @@ Object.freeze({
46391
46982
  form: "single",
46392
46983
  optional: false
46393
46984
  }],
46985
+ "waterHeater.getStatus": [{
46986
+ name: "deviceId",
46987
+ form: "single",
46988
+ optional: false
46989
+ }],
46394
46990
  "waterHeater.setAway": [{
46395
46991
  name: "deviceId",
46396
46992
  form: "single",
@@ -46406,6 +47002,11 @@ Object.freeze({
46406
47002
  form: "single",
46407
47003
  optional: false
46408
47004
  }],
47005
+ "weather.getStatus": [{
47006
+ name: "deviceId",
47007
+ form: "single",
47008
+ optional: false
47009
+ }],
46409
47010
  "webrtcSession.addIceCandidate": [{
46410
47011
  name: "deviceId",
46411
47012
  form: "single",
@@ -47143,7 +47744,7 @@ var PreviewTextField = string().max(200);
47143
47744
  * Whether the notification's preview is a STILL or a short animation.
47144
47745
  *
47145
47746
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
47146
- * rule)"* — the other rules already offer an animated preview, and a ten-hour
47747
+ * rule)"* [sent as a gif or video (as for the other rules)] — the other rules already offer an animated preview, and a ten-hour
47147
47748
  * night reads better as three seconds of motion than as one frame of it. Both
47148
47749
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
47149
47750
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -47281,6 +47882,7 @@ TimelapseRuleInputSchema.extend({
47281
47882
  createdAt: number(),
47282
47883
  updatedAt: number()
47283
47884
  });
47885
+ MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
47284
47886
  object({
47285
47887
  /**
47286
47888
  * Fraction of the box's own size added on EACH side before cutting.
@@ -51497,7 +52099,7 @@ var ExportAlexaAddon = class extends BaseAddon {
51497
52099
  * One reconcile pass over every exposed device shortly after boot. This is
51498
52100
  * what heals a shape that drifted while the addon was DOWN (or under an
51499
52101
  * older build that had no signature at all) with no operator click — the
51500
- * doorbell wrapper bound on 615 is exactly that case. Deliberately late
52102
+ * doorbell bound on 615 after it was exposed is exactly that case. Deliberately late
51501
52103
  * enough that device providers have registered their caps: a device whose
51502
52104
  * bindings have not landed yet builds a shape we must not push.
51503
52105
  */