@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.
@@ -1,5 +1,5 @@
1
1
  import * as crypto$1 from "node:crypto";
2
- //#region ../types/dist/event-category-C5xZWqz6.mjs
2
+ //#region ../types/dist/event-category-BGmz_oxW.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -532,6 +532,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
532
532
  */
533
533
  EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
534
534
  EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
535
+ /**
536
+ * A camera provider's RAW onboard motion push (D673): the `MotionOnMotionChanged`
537
+ * data shape without `triggeredBy`, built by `providerMotionEvent`. Its one
538
+ * consumer is the orchestrator's camera-sensors bridge, which re-emits the
539
+ * camera's EFFECTIVE onboard motion as `MotionOnMotionChanged` (a pass-through
540
+ * for a camera with no attached sensor). Providers must not emit
541
+ * `MotionOnMotionChanged` themselves; `scripts/check-onboard-motion-emitters.ts`
542
+ * keeps it so (camera-grid is a named, deferred exception).
543
+ */
535
544
  EventCategory["ProviderMotion"] = "provider.motion";
536
545
  EventCategory["ProviderDetection"] = "provider.detection";
537
546
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
@@ -5370,7 +5379,7 @@ var ZodIssueCode = {
5370
5379
  var ZodFirstPartyTypeKind;
5371
5380
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5372
5381
  //#endregion
5373
- //#region ../types/dist/sleep-gU6hqv4_.mjs
5382
+ //#region ../types/dist/sleep-CDCONfkt.mjs
5374
5383
  /**
5375
5384
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5376
5385
  * window to float samples (D455).
@@ -7431,7 +7440,7 @@ function errMsg(err) {
7431
7440
  return String(err);
7432
7441
  }
7433
7442
  //#endregion
7434
- //#region ../types/dist/composition-Dq42XWp0.mjs
7443
+ //#region ../types/dist/composition-Bzr7GwgP.mjs
7435
7444
  /**
7436
7445
  * The ffmpeg build every CamStack node downloads, per platform and arch.
7437
7446
  *
@@ -7485,21 +7494,20 @@ function portable(target) {
7485
7494
  }
7486
7495
  portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7487
7496
  /**
7488
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7489
- * valutatore puro del suo FRONTE.
7497
+ * THE table "which boolean of this device type counts as HIGH", and the
7498
+ * pure evaluator of its EDGE.
7490
7499
  *
7491
- * Viveva dentro il builtin virtual-doorbell
7492
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
7493
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
7494
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
7495
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
7496
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
7497
- * SPOSTA qui e il doorbell la ri-esporta.
7500
+ * ONE table for everyone who asks "is this device HIGH?": the
7501
+ * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
7502
+ * sensor markers, the orchestrator's camera-sensors bridge and
7503
+ * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
7504
+ * A second copy would diverge at the first cap added, and the symptom — "the
7505
+ * sensor rings the doorbell but does not record" — is exactly D62.
7498
7506
  *
7499
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
7500
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
7501
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
7502
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
7507
+ * ⚠ NOT `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), and the
7508
+ * two must not be unified: that one answers "what is the state WORD for a
7509
+ * rule" (and includes `presence`, `cover`, `alarm-panel`), this one "which
7510
+ * boolean's EDGE counts". Deliberately different vocabularies.
7503
7511
  */
7504
7512
  /**
7505
7513
  * Known binary / switch source caps → the boolean slice field whose
@@ -7520,6 +7528,8 @@ var SOURCE_CAP_ACTIVE_FIELD = {
7520
7528
  vibration: "detected",
7521
7529
  tamper: "tampered"
7522
7530
  };
7531
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
7532
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
7523
7533
  /**
7524
7534
  * Error types for the safe expression engine. Two distinct classes so callers
7525
7535
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -9275,22 +9285,22 @@ var CLASS_MAP_MACRO_TARGETS = [
9275
9285
  "package"
9276
9286
  ];
9277
9287
  /**
9278
- * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
9279
- * un operatore può selezionare.
9288
+ * The FIRST-LEVEL macro classes: those an object detector emits and that
9289
+ * an operator can select.
9280
9290
  *
9281
- * Sono le tre offerte dallo step `object-detection`
9291
+ * They are the three offered by the `object-detection` step
9282
9292
  * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
9283
- * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
9284
- * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
9285
- * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
9286
- * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
9293
+ * `enabledMacroClasses`). `package` is in {@link CLASS_MAP_MACRO_TARGETS} and in
9294
+ * `MACRO_LABELS` — it is a true macro — but NOT here: it belongs to the step
9295
+ * `package-detection`, whose enablement is driven by zones, and offering the
9296
+ * same word twice has already made an operator turn on the COCO proxy
9287
9297
  * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
9288
9298
  *
9289
- * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
9290
- * dello step e una seconda volta come union `FirstLevelMacro`
9291
- * (`types/detection.ts`); una terza copia per il trigger di registrazione
9292
- * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
9293
- * successiva.
9299
+ * ONE list. Before today the same three were hand-written in the step's offer
9300
+ * and a second time as the union `FirstLevelMacro`
9301
+ * (`types/detection.ts`); a third copy for the recording trigger
9302
+ * (`RecordingTriggers.objectClasses`) would have made the later divergence
9303
+ * invisible.
9294
9304
  */
9295
9305
  var FIRST_LEVEL_MACRO_CLASSES = [
9296
9306
  "person",
@@ -9355,17 +9365,17 @@ var RecordingStorageModeSchema = _enum([
9355
9365
  "on-device-decision"
9356
9366
  ]);
9357
9367
  /**
9358
- * Le macro classi che possono aprire una finestra di registrazione — le stesse
9359
- * tre offerte dallo step `object-detection`, da UNA lista
9368
+ * The macro classes that can open a recording window — the same
9369
+ * three offered by the `object-detection` step, from ONE list
9360
9370
  * ({@link FIRST_LEVEL_MACRO_CLASSES}).
9361
9371
  */
9362
9372
  var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
9363
9373
  /**
9364
- * True quando `values` non ripete un elemento.
9374
+ * True when `values` does not repeat an element.
9365
9375
  *
9366
- * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
9367
- * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
9368
- * conterebbe due volte le sue finestre in `segmentMissedByMs`.
9376
+ * A duplicate is not harmless: every entry of `objectClasses` / `sensorDeviceIds`
9377
+ * becomes a SOURCE in `bandTriggerSources`, and the same source twice
9378
+ * would count its windows twice in `segmentMissedByMs`.
9369
9379
  */
9370
9380
  var noDuplicates = (values) => new Set(values).size === values.length;
9371
9381
  /** Which detectors trigger an `events`-mode band. */
@@ -9373,34 +9383,34 @@ var RecordingTriggersSchema = object({
9373
9383
  motion: boolean().optional(),
9374
9384
  audioThresholdDbfs: number().optional(),
9375
9385
  /**
9376
- * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
9377
- * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
9378
- * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
9379
- * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
9386
+ * The macro classes whose detection opens a window. ABSENT = the source
9387
+ * is not listened to; an EMPTY array is rejected, because "events band, object
9388
+ * trigger on, no class" is the same shape "enabled and records
9389
+ * nothing, forever" that `eventsBandCanEverDemand` is written against.
9380
9390
  *
9381
- * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
9382
- * quelle che hanno attraversato `enabledMacroClasses`, i
9383
- * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
9384
- * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
9385
- * finestre — vedi `recorder/object-trigger.ts`.
9391
+ * The signal read is ALREADY FILTERED: only `source: 'pipeline'` detections, i.e.
9392
+ * those that went through `enabledMacroClasses`, the
9393
+ * `minConfidence<Macro>` and the full-frame guard. The camera's onboard AI
9394
+ * (`source: 'onboard'`) goes through none of those gates and does NOT open
9395
+ * windows — see `recorder/object-trigger.ts`.
9386
9396
  */
9387
9397
  objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
9388
9398
  /**
9389
- * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
9390
- * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
9399
+ * The LINKED devices whose RISING EDGE opens a window. Absent = the source
9400
+ * is not listened to; an empty array is rejected for the same reason as
9391
9401
  * `objectClasses`.
9392
9402
  *
9393
- * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
9394
- * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
9395
- * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
9396
- * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9397
- * device (D12) — mai un elenco globale di cap.
9403
+ * These are SOURCE device ids, not cameras: the band names them, so the
9404
+ * hot path (`DeviceStateChanged`, at bus rate across the whole fleet)
9405
+ * does no RPC. The OFFER from which the operator picks them is another question, and
9406
+ * is resolved with `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9407
+ * device (D12) — never a global list of caps.
9398
9408
  *
9399
- * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
9400
- * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
9401
- * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
9402
- * il livello: un contatto trovato già aperto al riavvio del runner non fa
9403
- * registrare.
9409
+ * What "high" means depends on the device TYPE and is not decided here:
9410
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) is THE table,
9411
+ * the same one as the composite doorbell (D671). And it is the EDGE, not
9412
+ * the level: a contact found already open at runner restart does not
9413
+ * record.
9404
9414
  */
9405
9415
  sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
9406
9416
  });
@@ -10477,7 +10487,7 @@ var DecoderSessionConfigSchema = object({
10477
10487
  * - **VALUE** — at which level, for which scope, until when. That stays ONE
10478
10488
  * thing: the logging settings document on the `system` cap. Two authorities
10479
10489
  * over the values is the exact defect
10480
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
10490
+ * `docs/design/plans/2026-08-26-per-component-logging.md` was written to
10481
10491
  * remove; re-introducing it from the cure side would be grotesque.
10482
10492
  *
10483
10493
  * Nothing in this file reads a clock, an env var or a store. The registry is
@@ -12302,7 +12312,7 @@ var BrokerStatusEnum = _enum([
12302
12312
  "unreachable",
12303
12313
  "error"
12304
12314
  ]);
12305
- var BrokerInfoSchema$1 = object({
12315
+ var BrokerInfoSchema = object({
12306
12316
  /** Stable broker id. Persisted; survives addon restarts. */
12307
12317
  id: string(),
12308
12318
  /** Addon id of the provider that OWNS this broker.
@@ -12435,7 +12445,7 @@ var GetStateInputSchema = object({
12435
12445
  * HA: entity_id (returns the cached entity state). */
12436
12446
  key: string()
12437
12447
  });
12438
- method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, BrokerInfoSchema$1.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12448
+ method(ListInputSchema, array(BrokerInfoSchema)), method(GetInputSchema, BrokerInfoSchema.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
12439
12449
  kind: "mutation",
12440
12450
  auth: "admin"
12441
12451
  }), method(RemoveInputSchema, _void(), {
@@ -14044,7 +14054,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
14044
14054
  *
14045
14055
  * ⚠ An earlier version of this comment blamed Zod stripping, and that
14046
14056
  * was wrong — corrected 2026-08-26 after the hop map
14047
- * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
14057
+ * (`docs/design/2026-08-26-topic-hop-map.md`) traced the path.
14048
14058
  * There is **no Zod parse at all** between the door and the engine: the
14049
14059
  * dispatcher forwards the payload verbatim and UDS carries it whole. A
14050
14060
  * field declared here reaches `SqliteSettingsBackend` either way.
@@ -14825,7 +14835,17 @@ var SiteZoneSchema = object({
14825
14835
  var ProviderStatusSchema = object({
14826
14836
  connected: boolean(),
14827
14837
  deviceCount: number(),
14828
- error: string().optional()
14838
+ error: string().optional(),
14839
+ /**
14840
+ * `'orchestrator'`: this provider writes NO camera's `motion` runtime slice
14841
+ * — the orchestrator is its only writer (D696). Set by `BaseDeviceProvider`,
14842
+ * which every provider bundles, so only a provider BUILT from D696-amended
14843
+ * code says it. ABSENT means an older provider that may still write it (raw
14844
+ * VMD writes, a boot seed), and the orchestrator's provider gate keeps that
14845
+ * provider's cameras unwritten (`camera-sensors/motion-slice-provider-gate.ts`)
14846
+ * — the provider twin of the runner's `getLocalLoad().motionSliceWriter`.
14847
+ */
14848
+ motionSliceWriter: literal("orchestrator").optional()
14829
14849
  });
14830
14850
  object({
14831
14851
  externalId: string(),
@@ -15623,28 +15643,10 @@ method(object({
15623
15643
  deviceIds: array(number()).readonly(),
15624
15644
  caps: array(string()).readonly().optional()
15625
15645
  }), record(string(), record(string(), unknown().nullable())));
15626
- object({
15627
- /** Ms epoch of the last press. null = never observed since this provider started. */
15628
- lastPressedAt: number().nullable(),
15629
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
15630
- pressCountSinceStart: number()
15631
- });
15632
- object({
15633
- deviceId: number(),
15634
- timestamp: number()
15635
- });
15636
- /**
15637
- * The `doorbell-source` extension slot's editable value (D648): the camera's
15638
- * virtual-doorbell trigger devices, exactly what the wrapper's
15639
- * `doorbellSources` persists (as `{ deviceId }` records).
15640
- */
15641
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
15646
+ var ExtensionSlotIdSchema = _enum(["linked-devices"]);
15642
15647
  DeviceType.Camera;
15643
- DeviceType.Button, DeviceType.Camera;
15644
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
15645
- DeviceType.Camera, DeviceType.Camera;
15646
15648
  /** A slot's editable value, discriminated on `slot`. */
15647
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15649
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15648
15650
  DeviceType.Camera;
15649
15651
  var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
15650
15652
  state: literal("unreachable"),
@@ -15681,10 +15683,11 @@ var ExtensionSlotSourceSchema = object({
15681
15683
  });
15682
15684
  /**
15683
15685
  * A DIFFERENT authority that gates whether the slot's sources do anything —
15684
- * reported read-only, named, never written through this contract (D62). The
15685
- * virtual doorbell's sources ring only while the wrapper is bound to the
15686
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
15687
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
15686
+ * reported read-only, named, never written through this contract (D62) — e.g.
15687
+ * a slot whose sources act only while a wrapper is bound to the host, a
15688
+ * binding that belongs to `deviceManager.setWrapperActive`. No slot reports
15689
+ * one today (`linked-devices` answers `null`). `unknown` means the gate could
15690
+ * not be read; it is not "inactive" (D315).
15688
15691
  */
15689
15692
  var ExtensionSlotActivationSchema = object({
15690
15693
  state: _enum([
@@ -15700,7 +15703,7 @@ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
15700
15703
  /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
15701
15704
  slotRef: string(),
15702
15705
  slot: ExtensionSlotIdSchema,
15703
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
15706
+ /** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
15704
15707
  authority: string(),
15705
15708
  activation: ExtensionSlotActivationSchema.nullable(),
15706
15709
  value: ExtensionSlotValueSchema,
@@ -15978,7 +15981,7 @@ method(object({
15978
15981
  * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
15979
15982
  * ends in a log line, and a log line is the thing the operator asked to stop
15980
15983
  * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
15981
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
15984
+ * 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
15982
15985
  * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
15983
15986
  * media blackout were both diagnosed. The counters stay; this is where they can
15984
15987
  * be READ.
@@ -17354,7 +17357,7 @@ var BrokerStatusSchema$1 = _enum([
17354
17357
  "unreachable",
17355
17358
  "tls-error"
17356
17359
  ]);
17357
- var BrokerInfoSchema = object({
17360
+ var BrokerInfoSchema$1 = object({
17358
17361
  id: string(),
17359
17362
  name: string(),
17360
17363
  url: string(),
@@ -17417,7 +17420,7 @@ var StatusSchema = object({
17417
17420
  brokerCount: number(),
17418
17421
  embeddedRunning: boolean()
17419
17422
  });
17420
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17423
+ method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
17421
17424
  kind: "mutation",
17422
17425
  auth: "admin"
17423
17426
  }), method(IdInputSchema, _void(), {
@@ -19593,78 +19596,16 @@ var NcRuleInputSchema = object({
19593
19596
  */
19594
19597
  confirm: NcConfirmSchema.optional(),
19595
19598
  /**
19596
- * WAIT for face/plate recognition before saying anything.
19597
- *
19598
- * A notification's TEXT is frozen at enqueue and its media is re-resolved at
19599
- * send; the identity is neither. A face is confirmed after `confirmFrames`
19600
- * agreeing observations — p50 **11.4 s** after the track was first seen,
19601
- * measured on this hub — and an `immediate` rule enqueues on the first object
19602
- * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
19603
- * immediate path, and no amount of media re-resolution fixes a sentence.
19604
- *
19605
- * Only two honest answers exist, and this flag picks between them. It has
19606
- * effect ONLY on a rule that declares a recognition scope
19607
- * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
19608
- * other rule there is nothing to wait for and the flag is inert.
19609
- *
19610
- * | value | what happens |
19611
- * | --- | --- |
19612
- * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
19613
- * | 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) |
19614
- *
19615
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19616
- * on the addon cap path, and absent has to keep meaning exactly what every
19617
- * rule authored before this field meant.
19618
- *
19619
- * The cost of `true` is stated here because the editor states it too: a rule
19620
- * that waits also inherits track-close SEMANTICS — its `zones` condition
19621
- * tests every zone the track visited and a `crossing` condition can no longer
19622
- * be satisfied, because a closed track carries no crossing.
19623
- */
19624
- waitForEnhancement: boolean().optional(),
19625
- /**
19626
- * GROUP a burst of subjects into ONE notification that grows.
19627
- *
19628
- * Seconds of quiet after the last matching subject before the burst is
19629
- * considered over. While it is open, the first subject enqueues immediately —
19630
- * **exactly as today, with no added latency** — and every real growth (a new
19631
- * subject, or a name confirmed on one already in it) REPLACES that
19632
- * notification with an updated one naming everybody. The push carries the
19633
- * group's own coalescing tag, so the phone replaces rather than stacks.
19634
- *
19635
- * `0` / absent = off, and off is today's behaviour byte for byte.
19636
- *
19637
- * ### Why an idle cutoff and not a window
19638
- *
19639
- * The measured seven-person arrival on device 590 spans 110 s with every
19640
- * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
19641
- * idle cutoff holds it as one and ends it when the arrival actually ends.
19642
- * 30 s is a widely shipped value for the same decision.
19643
- *
19644
- * ### What it replaces
19645
- *
19646
- * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
19647
- * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
19648
- * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
19649
- * track that did fire and 7 carrying a confirmed identity nobody heard about.
19650
- * A group collapses the same volume by MERGING, so the cooldown becomes a
19651
- * budget over GROUPS — which is what it always meant — and a growth is never
19652
- * throttled by the window its own first member spent.
19653
- *
19654
- * ### Interaction with {@link waitForEnhancement}
19655
- *
19656
- * They compose, and the order matters. `waitForEnhancement` defers the rule to
19657
- * TRACK CLOSE, so with both set the group is opened by the first member to
19658
- * CLOSE — already carrying its name — and grows as later members close. That
19659
- * is later, and complete. With grouping alone the group opens on the first
19660
- * object event and picks up names as they are confirmed, through the growth
19661
- * path. Neither combination fires twice for one subject.
19662
- *
19663
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
19664
- * on the addon cap path, so absent must keep meaning what it meant before this
19665
- * field existed.
19599
+ * IMMEDIATE GROUPED (D692): the first matching subject opens a window of this many
19600
+ * seconds; the rule sends ONE message when the window closes — early, once every
19601
+ * subject in it is final, never before {@link NC_GROUPING_MIN_SEC} — naming everybody
19602
+ * the rule matched, with one mosaic tile per subject.
19603
+ * Absent = plain immediate (one message per track, at the first match).
19604
+ * Only an `immediate` DETECTION rule may carry it (`ruleGroupingApplies`, enforced on
19605
+ * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
19606
+ * path does not run Zod defaults.
19666
19607
  */
19667
- groupIdleSec: number().int().min(0).max(600).optional()
19608
+ groupingSec: number().int().min(5).max(60).optional()
19668
19609
  });
19669
19610
  /**
19670
19611
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -19712,9 +19653,10 @@ var NcRuleClearableKeySchema = _enum([
19712
19653
  "targetUsers",
19713
19654
  "snoozeOptions",
19714
19655
  "snoozeAllowGlobal",
19715
- "waitForEnhancement",
19716
19656
  "actions",
19717
19657
  "confirm",
19658
+ "groupingSec",
19659
+ "waitForEnhancement",
19718
19660
  "groupIdleSec"
19719
19661
  ]);
19720
19662
  var NcRuleUpdateInputSchema = object({
@@ -19736,9 +19678,25 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19736
19678
  */
19737
19679
  disabledTargetIds: array(string()).default([])
19738
19680
  });
19681
+ /**
19682
+ * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19683
+ * still-true state. `backups-exports` and `alarm` are not sections — a completed
19684
+ * job is not an open state, and the alarm stays instant.
19685
+ */
19686
+ var NcStateSummarySectionIdSchema = _enum([
19687
+ "device-state",
19688
+ "streams",
19689
+ "battery",
19690
+ "consumables",
19691
+ "tanks",
19692
+ "detection-blind",
19693
+ "nodes",
19694
+ "addons",
19695
+ "updates"
19696
+ ]);
19739
19697
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
19740
19698
  var NcStateSummaryPreviewSectionSchema = object({
19741
- id: string(),
19699
+ id: NcStateSummarySectionIdSchema,
19742
19700
  status: _enum(["read", "unavailable"]),
19743
19701
  items: number().int().nonnegative(),
19744
19702
  reason: string().optional()
@@ -19748,7 +19706,7 @@ var NcStateSummaryPreviewSectionSchema = object({
19748
19706
  * a `rule` split (no `section`), one section's on a `section` split.
19749
19707
  */
19750
19708
  var NcStateSummaryPreviewRowSchema = object({
19751
- section: string().optional(),
19709
+ section: NcStateSummarySectionIdSchema.optional(),
19752
19710
  /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
19753
19711
  title: string(),
19754
19712
  text: string(),
@@ -20185,6 +20143,29 @@ object({
20185
20143
  firstAt: number(),
20186
20144
  lastAt: number()
20187
20145
  });
20146
+ var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
20147
+ var NcAlarmDelaySecSchema = number().int().min(0).max(600);
20148
+ var NcAlarmTriggeredSecSchema = number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
20149
+ var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
20150
+ var NcAlarmModeDelaysListSchema = array(object({
20151
+ mode: AlarmArmModeSchema,
20152
+ exitDelaySec: NcAlarmDelaySecSchema.optional(),
20153
+ entryDelaySec: NcAlarmDelaySecSchema.optional(),
20154
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
20155
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20156
+ var NcAlarmModeDelaysEditListSchema = array(object({
20157
+ mode: AlarmArmModeSchema,
20158
+ exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20159
+ entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20160
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
20161
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20162
+ /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20163
+ var NcAlarmNonBlockingEditSchema = object({
20164
+ mode: AlarmArmModeSchema,
20165
+ deviceId: number().int().positive(),
20166
+ /** true = on the list (may be open, does not block); false = off the list (blocks). */
20167
+ nonBlocking: boolean()
20168
+ });
20188
20169
  /**
20189
20170
  * The per-mode lists, at most one per mode. Two lists for one mode have no
20190
20171
  * meaning a reader could agree on (union? the last one?), so they are refused
@@ -20193,10 +20174,10 @@ object({
20193
20174
  var NcAlarmNonBlockingListSchema = array(object({
20194
20175
  mode: AlarmArmModeSchema,
20195
20176
  deviceIds: array(number().int()).max(200)
20196
- })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
20177
+ })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
20197
20178
  /**
20198
- * The three durations the panel's state machine runs on, who hears about its
20199
- * transitions, and which openings each mode tolerates.
20179
+ * The three durations — global, and per mode since D697 — the panel's state
20180
+ * machine runs on, who hears about its transitions, and which openings each mode tolerates.
20200
20181
  *
20201
20182
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
20202
20183
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -20206,15 +20187,15 @@ var NcAlarmNonBlockingListSchema = array(object({
20206
20187
  */
20207
20188
  var NcAlarmSettingsSchema = object({
20208
20189
  /** Grace period between arming and the mode taking effect. 0 = immediate. */
20209
- exitDelaySec: number().int().min(0).max(600),
20190
+ exitDelaySec: NcAlarmDelaySecSchema,
20210
20191
  /** Grace period between a trigger and the alarm firing. 0 = immediate. */
20211
- entryDelaySec: number().int().min(0).max(600),
20192
+ entryDelaySec: NcAlarmDelaySecSchema,
20212
20193
  /**
20213
20194
  * How long `triggered` lasts before the panel re-arms itself.
20214
20195
  * **0 = until an operator disarms it** — the behaviour before this field
20215
20196
  * existed, and therefore what an untouched install keeps doing.
20216
20197
  */
20217
- triggeredDurationSec: number().int().min(0).max(3600),
20198
+ triggeredDurationSec: NcAlarmTriggeredSecSchema,
20218
20199
  /**
20219
20200
  * Deprecated by D630 — every transition is announced. Parsed, never read;
20220
20201
  * remove after one release. Optional so an older admin's patch (which still
@@ -20237,17 +20218,30 @@ var NcAlarmSettingsSchema = object({
20237
20218
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
20238
20219
  * parses as "every opening blocks" — the behaviour it was written under.
20239
20220
  */
20240
- nonBlocking: NcAlarmNonBlockingListSchema.default([])
20221
+ nonBlocking: NcAlarmNonBlockingListSchema.default([]),
20222
+ /**
20223
+ * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
20224
+ * without a field, uses the global value above — so `[]`, the default, is exactly the
20225
+ * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
20226
+ */
20227
+ modeDelays: NcAlarmModeDelaysListSchema.default([])
20241
20228
  });
20242
20229
  /**
20243
20230
  * Every field optional — a tab edits one control at a time.
20244
20231
  *
20245
- * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
20246
- * remove an inner `.default()`, so a patch that never named the field would
20247
- * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
20248
- * (the same trap {@link NcRulePatchSchema} documents).
20232
+ * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
20233
+ * `.partial()` does not remove an inner `.default()`, so a patch that never
20234
+ * named the field would parse to `[]` and wipe every mode's list on an
20235
+ * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
20236
+ * `modeDelays` means EDITS, not the stored overrides.
20249
20237
  */
20250
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
20238
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
20239
+ nonBlocking: NcAlarmNonBlockingListSchema,
20240
+ /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
20241
+ modeDelays: NcAlarmModeDelaysEditListSchema,
20242
+ /** ONE membership change; the panel applies it to the list it holds at its turn. */
20243
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema
20244
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
20251
20245
  /**
20252
20246
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
20253
20247
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -20347,6 +20341,28 @@ var NcAlarmConfigSchema = object({
20347
20341
  */
20348
20342
  live: NcAlarmLiveSchema.optional()
20349
20343
  });
20344
+ var NcAlarmSensorReadingSchema = _enum([
20345
+ "open",
20346
+ "closed",
20347
+ "unknown",
20348
+ "not-opening"
20349
+ ]);
20350
+ /** One device's state as the alarm reads it — the classifier the arm check uses. */
20351
+ var NcAlarmSensorStateSchema = object({
20352
+ deviceId: number().int().positive(),
20353
+ reading: NcAlarmSensorReadingSchema,
20354
+ /** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
20355
+ state: string().nullable(),
20356
+ /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
20357
+ unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
20358
+ /** The device manager's liveness as the centre mirrors it; null = not known. */
20359
+ online: boolean().nullable()
20360
+ });
20361
+ var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
20362
+ var NcAlarmSensorsSchema = object({
20363
+ sensors: array(NcAlarmSensorStateSchema),
20364
+ readAt: number().int().nonnegative()
20365
+ });
20350
20366
  /**
20351
20367
  * ONE rule's demand on ONE camera's clip ring.
20352
20368
  *
@@ -20437,7 +20453,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
20437
20453
  }), method(object({}), NcAlarmConfigSchema, { auth: "admin" }), method(object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
20438
20454
  kind: "mutation",
20439
20455
  auth: "admin"
20440
- });
20456
+ }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
20441
20457
  /**
20442
20458
  * Zod schemas for persisted record types.
20443
20459
  *
@@ -20800,12 +20816,26 @@ var CameraSensorRefSchema = object({
20800
20816
  function cameraSensorRefKey(ref) {
20801
20817
  return `${ref.addonId}/${ref.stableId}`;
20802
20818
  }
20803
- /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20804
- var CameraSensorsModeSchema = _enum(["in-addition"]);
20805
- var CameraSensorsConfigSchema = object({
20806
- mode: CameraSensorsModeSchema,
20807
- sensors: array(CameraSensorRefSchema).min(1).max(8)
20808
- }).superRefine((value, ctx) => {
20819
+ /**
20820
+ * A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
20821
+ * `replace`: its motion is the camera's, and while one is READY the camera's
20822
+ * own motion no longer counts on its own. `confirm`: while one is READY the
20823
+ * camera's own motion counts only while a confirm sensor is active — with or
20824
+ * without a replace sensor (spec C2, the controller's ruling on Q1).
20825
+ */
20826
+ var CameraSensorModeSchema = _enum([
20827
+ "in-addition",
20828
+ "confirm",
20829
+ "replace"
20830
+ ]);
20831
+ /** A stored sensor with no mode — every CS-1 row — is in addition. */
20832
+ var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
20833
+ /**
20834
+ * A camera's attached sensors. A CS-1 row carried an envelope
20835
+ * `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
20836
+ * what the write stores (D672 rule 3), so it is never written again.
20837
+ */
20838
+ var CameraSensorsConfigSchema = object({ sensors: array(CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) })).min(1).max(8) }).superRefine((value, ctx) => {
20809
20839
  const seen = /* @__PURE__ */ new Set();
20810
20840
  value.sensors.forEach((ref, index) => {
20811
20841
  const key = cameraSensorRefKey(ref);
@@ -20817,39 +20847,41 @@ var CameraSensorsConfigSchema = object({
20817
20847
  seen.add(key);
20818
20848
  });
20819
20849
  });
20820
- var RefFields = {
20850
+ /** Every attachment names its mode; an older orchestrator that does not is in addition. */
20851
+ var AttachmentFields = {
20821
20852
  addonId: string(),
20822
- stableId: string()
20853
+ stableId: string(),
20854
+ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
20823
20855
  };
20824
20856
  var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20825
20857
  object({
20826
- ...RefFields,
20858
+ ...AttachmentFields,
20827
20859
  state: literal("ready"),
20828
20860
  deviceId: number(),
20829
20861
  name: string(),
20830
20862
  caps: array(string()).readonly()
20831
20863
  }),
20832
20864
  object({
20833
- ...RefFields,
20865
+ ...AttachmentFields,
20834
20866
  state: literal("offline"),
20835
20867
  deviceId: number(),
20836
20868
  name: string(),
20837
20869
  reason: _enum(["offline", "disabled"])
20838
20870
  }),
20839
20871
  object({
20840
- ...RefFields,
20872
+ ...AttachmentFields,
20841
20873
  state: literal("missing"),
20842
20874
  reason: string()
20843
20875
  }),
20844
20876
  object({
20845
- ...RefFields,
20877
+ ...AttachmentFields,
20846
20878
  state: literal("unsupported"),
20847
20879
  deviceId: number(),
20848
20880
  name: string(),
20849
20881
  reason: string()
20850
20882
  }),
20851
20883
  object({
20852
- ...RefFields,
20884
+ ...AttachmentFields,
20853
20885
  state: literal("unreadable"),
20854
20886
  reason: string()
20855
20887
  })
@@ -20862,17 +20894,49 @@ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20862
20894
  * say.
20863
20895
  */
20864
20896
  var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20897
+ /** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
20898
+ var CameraOwnMotionSchema = _enum([
20899
+ "always",
20900
+ "when-confirmed",
20901
+ "never"
20902
+ ]);
20903
+ /** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
20904
+ var CameraSensorsNotEnforcedSchema = _enum(["provider-not-split"]);
20905
+ /**
20906
+ * The camera's OWN onboard motion (D696): the raw input the bridge combines,
20907
+ * never the effective value. The camera's `motion` slice is the effective one,
20908
+ * so a view of the camera's own row reads this.
20909
+ * - `atMs`: when the bridge received the camera's last own edge.
20910
+ * - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
20911
+ * AND it is younger than the window the bridge counts the camera's own motion
20912
+ * for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
20913
+ * sends a clear (heartbeats only) therefore reads `false` once no push came
20914
+ * within the window; a status read is a snapshot, so a live view re-asks.
20915
+ * Absent = this orchestrator has not received the camera's own motion since it
20916
+ * started (unknown, never "quiet"), or an orchestrator older than D696.
20917
+ */
20918
+ var CameraRawLevelSchema = object({
20919
+ detected: boolean(),
20920
+ atMs: number()
20921
+ });
20865
20922
  var CameraSensorsStatusSchema = discriminatedUnion("state", [
20866
- object({ state: literal("not-configured") }),
20923
+ object({
20924
+ state: literal("not-configured"),
20925
+ cameraLevel: CameraRawLevelSchema.optional()
20926
+ }),
20867
20927
  object({
20868
20928
  state: literal("unreadable"),
20869
- reason: string()
20929
+ reason: string(),
20930
+ cameraLevel: CameraRawLevelSchema.optional()
20870
20931
  }),
20871
20932
  object({
20872
20933
  state: literal("configured"),
20873
- mode: CameraSensorsModeSchema,
20934
+ cameraLevel: CameraRawLevelSchema.optional(),
20874
20935
  sensors: array(CameraSensorAttachmentSchema).readonly(),
20875
- notForwarded: CameraSensorsNotForwardedSchema.optional()
20936
+ /** Absent = an orchestrator older than CS-2: it does not say (D315). */
20937
+ cameraMotion: CameraOwnMotionSchema.optional(),
20938
+ notForwarded: CameraSensorsNotForwardedSchema.optional(),
20939
+ notEnforced: CameraSensorsNotEnforcedSchema.optional()
20876
20940
  })
20877
20941
  ]);
20878
20942
  Object.freeze({
@@ -21557,14 +21621,14 @@ var TrackSchema = object({
21557
21621
  * `derive-returned-null tile=standin`,
21558
21622
  * `native-tile tile=native no-key-frame-at-close`, …) written at close and
21559
21623
  * CLEARED by the late-keyFrame upgrade when a native tile lands after all.
21560
- * The operator-facing answer to "perché manca l'immagine?" on a track whose
21624
+ * The operator-facing answer to "perché manca l'immagine?" [why is the image missing?] on a track whose
21561
21625
  * tile is a face/plate stand-in, a raster crop, or an icon.
21562
21626
  *
21563
21627
  * **`no-key-frame-at-close` (D400).** The line used to be written ONLY when
21564
21628
  * the TILE ranked below native, so a track whose thumbnail landed natively
21565
21629
  * while its key frame missed carried no reason at all — three of the four
21566
21630
  * `MISSING_KEYFRAME` tracks in the 2026-09-07 operator audit were exactly
21567
- * that shape, and their "niente ultimo" had no cause anywhere on the row.
21631
+ * that shape, and their "niente ultimo" [no last frame] had no cause anywhere on the row.
21568
21632
  * The key frame is the head of a cascade (`keyFrame ⇒ keyFrameSmall ⇒ the
21569
21633
  * lastFrame copy source`), so its absence is now on this line whatever the
21570
21634
  * tile ended up being.
@@ -21651,7 +21715,7 @@ var MotionEventSchema = object({
21651
21715
  /**
21652
21716
  * Ms offsets from `timestamp` (the episode's own first rising edge, so the
21653
21717
  * first entry is always `0`) of every genuine off→on transition the
21654
- * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
21718
+ * episode saw — "ogni evento on si deve salvare" [every ON event must be saved] (D475). NOT one entry per
21655
21719
  * push: a firmware source that keepalives at ~1 Hz for the whole burst
21656
21720
  * (Reolink, Hikvision) produces exactly one edge; a source that reports an
21657
21721
  * explicit `false` mid-episode and then resumes before the quiet window
@@ -22175,6 +22239,7 @@ var KeyEventSchema = object({
22175
22239
  bestEventId: string(),
22176
22240
  /** Track lifetime in ms (lastSeen - firstSeen). */
22177
22241
  windowMs: number().optional(),
22242
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
22178
22243
  ...TrackFlagFields,
22179
22244
  ...TrackRetrainFields
22180
22245
  });
@@ -24063,8 +24128,10 @@ var RunnerCameraConfigSchema = object({
24063
24128
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
24064
24129
  * analyzer providers) running on decoded frames in this runner.
24065
24130
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
24066
- * analytics). The provider emits ProviderMotion
24067
- * events; the orchestrator forwards to `reportMotion`.
24131
+ * analytics). The provider emits its raw push as `ProviderMotion`
24132
+ * (D673); the orchestrator's camera-sensors bridge re-emits the camera's
24133
+ * effective motion as `MotionOnMotionChanged`, which is what reaches
24134
+ * `reportMotion`.
24068
24135
  */
24069
24136
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24070
24137
  /**
@@ -24241,7 +24308,15 @@ var RunnerLocalLoadSchema = object({
24241
24308
  hasGpu: boolean(),
24242
24309
  inferenceBackend: string().optional(),
24243
24310
  cpuCores: number().optional()
24244
- })
24311
+ }),
24312
+ /**
24313
+ * Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
24314
+ * runner never writes it. ABSENT = a runner older than D696, which still
24315
+ * writes the slice from its phase machine; the orchestrator does not write
24316
+ * while any runner node is absent-or-unanswered, so one slice never has two
24317
+ * writers.
24318
+ */
24319
+ motionSliceWriter: literal("orchestrator").optional()
24245
24320
  });
24246
24321
  /**
24247
24322
  * Aggregate runtime metrics for the runner's whole local pool. Mirrors the
@@ -26877,7 +26952,7 @@ var ClipPlaybackSchema = object({
26877
26952
  * **One authorised yes is one wake.** `next-natural` — take the clip the next
26878
26953
  * time the camera is awake for its own reasons — is deliberately not a member:
26879
26954
  * it was proposed, it is free on the battery, and the operator declined it on
26880
- * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
26955
+ * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."* [When an export is requested the camera wakes up],
26881
26956
  * D558 "Considered and not taken").
26882
26957
  */
26883
26958
  var ClipWakeSchema = _enum(["authorised"]);
@@ -27138,7 +27213,7 @@ DeviceType.Camera, method(object({
27138
27213
  * It replaced `sources?: string[]`, a VIEW filter over source ids that
27139
27214
  * assumed the answer was a fan-out over everything a camera has. The
27140
27215
  * operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
27141
- * solo"* — so the list is asked of one provider at a time and there is
27216
+ * solo"* [there is only one user] — so the list is asked of one provider at a time and there is
27142
27217
  * nothing to filter out of it.
27143
27218
  */
27144
27219
  provider: string().min(1)
@@ -29082,6 +29157,17 @@ object({
29082
29157
  deviceId: number(),
29083
29158
  status: DeviceStatusSchema
29084
29159
  });
29160
+ object({
29161
+ /** Ms epoch of the last press. null = never observed since this provider started. */
29162
+ lastPressedAt: number().nullable(),
29163
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
29164
+ pressCountSinceStart: number()
29165
+ });
29166
+ object({
29167
+ deviceId: number(),
29168
+ timestamp: number()
29169
+ });
29170
+ DeviceType.Button, DeviceType.Camera;
29085
29171
  /**
29086
29172
  * Enum-state sensor — a string value picked from a finite option set.
29087
29173
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -30726,9 +30812,9 @@ object({
30726
30812
  /** Ms epoch of the last detected-true observation. Null if never detected. */
30727
30813
  lastDetectedAt: number().nullable(),
30728
30814
  /**
30729
- * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
30730
- * `null` while false and on every `Sensor` device (D475) — see that
30731
- * constant's doc for the one-authority rule.
30815
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
30816
+ * orchestrator writes it, D696), `null` while false and on every `Sensor`
30817
+ * device (D475) — see that constant's doc for the one-authority rule.
30732
30818
  *
30733
30819
  * ## Reading this field still arms nothing
30734
30820
  *
@@ -30746,38 +30832,41 @@ object({
30746
30832
  * `addon-export-alexa/src/motion-clear-hold.ts` and
30747
30833
  * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
30748
30834
  *
30749
- * ## Who writes it
30750
- *
30751
- * - **Cameras** — the runner's phase machine, `active → watching` on
30752
- * `cooldown_expired`, which then writes this slice with
30753
- * `detected: false` (`handlePhaseChanged` in
30754
- * `pipeline-runner/index.ts`). It produces the FALLING edge, which
30755
- * matters most for the sources that only ever push a rising one:
30756
- * Reolink emits `MotionOnMotionChanged { detected: true }` and never
30757
- * a false.
30758
- * - **Sensors** (Home Assistant binary sensors, Homematic) — the
30759
- * provider pushes the false itself, from the upstream system's own
30760
- * state change. No phase machine is involved.
30761
- *
30762
- * ### The phase machine is CANONICAL, not sole — and that is a defect
30763
- *
30764
- * An earlier revision of this docblock (mine, 2026-09-12) claimed the
30765
- * phase machine is the sole writer for a camera. It is not.
30766
- * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
30767
- * `setCapSlice(motionCapability, …)` on their own rising edge, and
30768
- * Hikvision's comment says why: it read THIS docblock, agreed the
30769
- * runner is canonical, and wrote anyway to avoid per-tick churn. So
30770
- * two authorities can disagree about one slice, which this repo
30771
- * forbids, and the doc said otherwise — which is worse than saying
30772
- * nothing, because it reads as verification.
30773
- *
30774
- * This predates D475 and is not fixed there: the fix touches every
30775
- * camera provider. Recorded in D475's Consequences. Do not restore the
30776
- * "sole writer" wording without also removing the other writers.
30835
+ * ## Who writes it (D696)
30836
+ *
30837
+ * - **Cameras** — the orchestrator, and nothing else. It writes the camera's
30838
+ * EFFECTIVE motion (`addon-pipeline-orchestrator`
30839
+ * `camera-sensors/motion-slice.ts`), alongside the effective
30840
+ * `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
30841
+ * for a split camera, an unsplit emitter's direct event, the analyzer and
30842
+ * device activity, as the camera's `motionSources` counts them. A source
30843
+ * that never sends a clear falls one window after its last `true`.
30844
+ * `lastDetectedAt` moves only on an effective rise. No camera provider
30845
+ * writes this slice (not even a boot seed), and neither does the runner's
30846
+ * phase machine: its phase is "a session is attached" for every on-motion
30847
+ * session, not motion, and it now has its own `DetectionPhaseTransition`.
30848
+ * A provider's `isDetected` answers from the hub's slice.
30849
+ * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
30850
+ * imports) — the provider pushes both edges itself, from the upstream
30851
+ * system's own state change. That is the sensor's own fact.
30852
+ *
30853
+ * `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
30854
+ * `motion` slice; each sensor writer is a named exemption.
30855
+ *
30856
+ * This replaces the D475 §4 rule ("the phase machine is the sole writer").
30857
+ * That rule was never true: Hikvision and Amcrest wrote the slice raw beside
30858
+ * it, every provider seeded it at boot, and every session left it
30859
+ * `detected: true` after its detach.
30777
30860
  */
30778
30861
  autoClearAfterMs: number().nullable()
30779
30862
  });
30780
- object({
30863
+ /**
30864
+ * Payload of `motion.onMotionChanged` event + the corresponding bus
30865
+ * event `EventCategory.MotionOnMotionChanged`. Single source of truth
30866
+ * — both the cap event surface and the bus payload type alias to this
30867
+ * schema.
30868
+ */
30869
+ var MotionOnMotionChangedDataSchema = object({
30781
30870
  deviceId: number(),
30782
30871
  detected: boolean(),
30783
30872
  timestamp: number(),
@@ -33242,7 +33331,7 @@ var ReadGopBytesResultSchema = object({
33242
33331
  /**
33243
33332
  * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
33244
33333
  * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
33245
- * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
33334
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-clip-replay-on-pipeline.md`):
33246
33335
  * a replay needs several seconds of native pixels, not one frame.
33247
33336
  *
33248
33337
  * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
@@ -35858,6 +35947,7 @@ DeviceType.Camera, method(object({
35858
35947
  detection: array(ZoneRuleSchema).readonly(),
35859
35948
  package: array(ZoneRuleSchema).readonly()
35860
35949
  });
35950
+ SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
35861
35951
  /**
35862
35952
  * Accessory device helpers — shared across drivers.
35863
35953
  *
@@ -40836,6 +40926,12 @@ Object.freeze({
40836
40926
  addonId: null,
40837
40927
  access: "view"
40838
40928
  },
40929
+ "notificationRules.getAlarmSensors": {
40930
+ capName: "notification-rules",
40931
+ capScope: "system",
40932
+ addonId: null,
40933
+ access: "view"
40934
+ },
40839
40935
  "notificationRules.getClipRetentionAsks": {
40840
40936
  capName: "notification-rules",
40841
40937
  capScope: "system",
@@ -44414,6 +44510,11 @@ Object.freeze({
44414
44510
  }
44415
44511
  });
44416
44512
  Object.freeze({
44513
+ "accessories.getStatus": [{
44514
+ name: "deviceId",
44515
+ form: "single",
44516
+ optional: false
44517
+ }],
44417
44518
  "accessories.setChildHidden": [{
44418
44519
  name: "childDeviceId",
44419
44520
  form: "single",
@@ -44433,6 +44534,11 @@ Object.freeze({
44433
44534
  form: "single",
44434
44535
  optional: false
44435
44536
  }],
44537
+ "airQualitySensor.getStatus": [{
44538
+ name: "deviceId",
44539
+ form: "single",
44540
+ optional: false
44541
+ }],
44436
44542
  "alarmPanel.arm": [{
44437
44543
  name: "deviceId",
44438
44544
  form: "single",
@@ -44443,11 +44549,36 @@ Object.freeze({
44443
44549
  form: "single",
44444
44550
  optional: false
44445
44551
  }],
44552
+ "alarmPanel.getStatus": [{
44553
+ name: "deviceId",
44554
+ form: "single",
44555
+ optional: false
44556
+ }],
44446
44557
  "alarmPanel.trigger": [{
44447
44558
  name: "deviceId",
44448
44559
  form: "single",
44449
44560
  optional: false
44450
44561
  }],
44562
+ "ambientLightSensor.getStatus": [{
44563
+ name: "deviceId",
44564
+ form: "single",
44565
+ optional: false
44566
+ }],
44567
+ "audioAnalysis.applyDeviceSettingsPatch": [{
44568
+ name: "deviceId",
44569
+ form: "single",
44570
+ optional: false
44571
+ }],
44572
+ "audioAnalysis.getDeviceLiveContribution": [{
44573
+ name: "deviceId",
44574
+ form: "single",
44575
+ optional: false
44576
+ }],
44577
+ "audioAnalysis.getDeviceSettingsContribution": [{
44578
+ name: "deviceId",
44579
+ form: "single",
44580
+ optional: false
44581
+ }],
44451
44582
  "audioAnalysis.resolveDeviceSettings": [{
44452
44583
  name: "deviceId",
44453
44584
  form: "single",
@@ -44488,16 +44619,36 @@ Object.freeze({
44488
44619
  form: "single",
44489
44620
  optional: false
44490
44621
  }],
44622
+ "automationControl.getStatus": [{
44623
+ name: "deviceId",
44624
+ form: "single",
44625
+ optional: false
44626
+ }],
44491
44627
  "automationControl.trigger": [{
44492
44628
  name: "deviceId",
44493
44629
  form: "single",
44494
44630
  optional: false
44495
44631
  }],
44632
+ "battery.getStatus": [{
44633
+ name: "deviceId",
44634
+ form: "single",
44635
+ optional: false
44636
+ }],
44496
44637
  "battery.wakeForStream": [{
44497
44638
  name: "deviceId",
44498
44639
  form: "single",
44499
44640
  optional: false
44500
44641
  }],
44642
+ "binary.getStatus": [{
44643
+ name: "deviceId",
44644
+ form: "single",
44645
+ optional: false
44646
+ }],
44647
+ "brightness.getStatus": [{
44648
+ name: "deviceId",
44649
+ form: "single",
44650
+ optional: false
44651
+ }],
44501
44652
  "brightness.setBrightness": [{
44502
44653
  name: "deviceId",
44503
44654
  form: "single",
@@ -44513,6 +44664,26 @@ Object.freeze({
44513
44664
  form: "single",
44514
44665
  optional: false
44515
44666
  }],
44667
+ "cameraCredentials.getStatus": [{
44668
+ name: "deviceId",
44669
+ form: "single",
44670
+ optional: false
44671
+ }],
44672
+ "cameraGridLayout.applyDeviceSettingsPatch": [{
44673
+ name: "deviceId",
44674
+ form: "single",
44675
+ optional: false
44676
+ }],
44677
+ "cameraGridLayout.getDeviceLiveContribution": [{
44678
+ name: "deviceId",
44679
+ form: "single",
44680
+ optional: false
44681
+ }],
44682
+ "cameraGridLayout.getDeviceSettingsContribution": [{
44683
+ name: "deviceId",
44684
+ form: "single",
44685
+ optional: false
44686
+ }],
44516
44687
  "cameraGridLayout.getLayout": [{
44517
44688
  name: "deviceId",
44518
44689
  form: "single",
@@ -44528,6 +44699,21 @@ Object.freeze({
44528
44699
  form: "single",
44529
44700
  optional: false
44530
44701
  }],
44702
+ "cameraPipelineConfig.applyDeviceSettingsPatch": [{
44703
+ name: "deviceId",
44704
+ form: "single",
44705
+ optional: false
44706
+ }],
44707
+ "cameraPipelineConfig.getDeviceLiveContribution": [{
44708
+ name: "deviceId",
44709
+ form: "single",
44710
+ optional: false
44711
+ }],
44712
+ "cameraPipelineConfig.getDeviceSettingsContribution": [{
44713
+ name: "deviceId",
44714
+ form: "single",
44715
+ optional: false
44716
+ }],
44531
44717
  "cameraStreams.getBrokerStreams": [{
44532
44718
  name: "deviceId",
44533
44719
  form: "single",
@@ -44553,6 +44739,16 @@ Object.freeze({
44553
44739
  form: "single",
44554
44740
  optional: false
44555
44741
  }],
44742
+ "carbonMonoxide.getStatus": [{
44743
+ name: "deviceId",
44744
+ form: "single",
44745
+ optional: false
44746
+ }],
44747
+ "climateControl.getStatus": [{
44748
+ name: "deviceId",
44749
+ form: "single",
44750
+ optional: false
44751
+ }],
44556
44752
  "climateControl.setFanMode": [{
44557
44753
  name: "deviceId",
44558
44754
  form: "single",
@@ -44593,16 +44789,41 @@ Object.freeze({
44593
44789
  form: "single",
44594
44790
  optional: false
44595
44791
  }],
44792
+ "color.getStatus": [{
44793
+ name: "deviceId",
44794
+ form: "single",
44795
+ optional: false
44796
+ }],
44596
44797
  "color.setColor": [{
44597
44798
  name: "deviceId",
44598
44799
  form: "single",
44599
44800
  optional: false
44600
44801
  }],
44802
+ "connectivity.getStatus": [{
44803
+ name: "deviceId",
44804
+ form: "single",
44805
+ optional: false
44806
+ }],
44807
+ "consumables.getStatus": [{
44808
+ name: "deviceId",
44809
+ form: "single",
44810
+ optional: false
44811
+ }],
44601
44812
  "consumables.reset": [{
44602
44813
  name: "deviceId",
44603
44814
  form: "single",
44604
44815
  optional: false
44605
44816
  }],
44817
+ "contact.getStatus": [{
44818
+ name: "deviceId",
44819
+ form: "single",
44820
+ optional: false
44821
+ }],
44822
+ "control.getStatus": [{
44823
+ name: "deviceId",
44824
+ form: "single",
44825
+ optional: false
44826
+ }],
44606
44827
  "control.setValue": [{
44607
44828
  name: "deviceId",
44608
44829
  form: "single",
@@ -44613,6 +44834,11 @@ Object.freeze({
44613
44834
  form: "single",
44614
44835
  optional: false
44615
44836
  }],
44837
+ "cover.getStatus": [{
44838
+ name: "deviceId",
44839
+ form: "single",
44840
+ optional: false
44841
+ }],
44616
44842
  "cover.open": [{
44617
44843
  name: "deviceId",
44618
44844
  form: "single",
@@ -44638,6 +44864,11 @@ Object.freeze({
44638
44864
  form: "single",
44639
44865
  optional: false
44640
44866
  }],
44867
+ "dayNight.getStatus": [{
44868
+ name: "deviceId",
44869
+ form: "single",
44870
+ optional: false
44871
+ }],
44641
44872
  "dayNight.setSettings": [{
44642
44873
  name: "deviceId",
44643
44874
  form: "single",
@@ -44648,11 +44879,31 @@ Object.freeze({
44648
44879
  form: "single",
44649
44880
  optional: true
44650
44881
  }],
44882
+ "detectionPipeline.applyDeviceSettingsPatch": [{
44883
+ name: "deviceId",
44884
+ form: "single",
44885
+ optional: false
44886
+ }],
44887
+ "detectionPipeline.getDeviceLiveContribution": [{
44888
+ name: "deviceId",
44889
+ form: "single",
44890
+ optional: false
44891
+ }],
44892
+ "detectionPipeline.getDeviceSettingsContribution": [{
44893
+ name: "deviceId",
44894
+ form: "single",
44895
+ optional: false
44896
+ }],
44651
44897
  "deviceAdminLink.getAdminLink": [{
44652
44898
  name: "deviceId",
44653
44899
  form: "single",
44654
44900
  optional: false
44655
44901
  }],
44902
+ "deviceAdoption.getStatus": [{
44903
+ name: "deviceId",
44904
+ form: "single",
44905
+ optional: false
44906
+ }],
44656
44907
  "deviceAdoption.release": [{
44657
44908
  name: "camDeviceId",
44658
44909
  form: "single",
@@ -44668,6 +44919,11 @@ Object.freeze({
44668
44919
  form: "single",
44669
44920
  optional: false
44670
44921
  }],
44922
+ "deviceDiscovery.getStatus": [{
44923
+ name: "deviceId",
44924
+ form: "single",
44925
+ optional: false
44926
+ }],
44671
44927
  "deviceDiscovery.listDiscovered": [{
44672
44928
  name: "deviceId",
44673
44929
  form: "single",
@@ -44687,6 +44943,21 @@ Object.freeze({
44687
44943
  form: "single",
44688
44944
  optional: false
44689
44945
  }],
44946
+ "deviceExport.applyDeviceSettingsPatch": [{
44947
+ name: "deviceId",
44948
+ form: "single",
44949
+ optional: false
44950
+ }],
44951
+ "deviceExport.getDeviceLiveContribution": [{
44952
+ name: "deviceId",
44953
+ form: "single",
44954
+ optional: false
44955
+ }],
44956
+ "deviceExport.getDeviceSettingsContribution": [{
44957
+ name: "deviceId",
44958
+ form: "single",
44959
+ optional: false
44960
+ }],
44690
44961
  "deviceExtension.listCandidates": [{
44691
44962
  name: "deviceId",
44692
44963
  form: "single",
@@ -45035,6 +45306,26 @@ Object.freeze({
45035
45306
  form: "single",
45036
45307
  optional: false
45037
45308
  }],
45309
+ "deviceStatus.getStatus": [{
45310
+ name: "deviceId",
45311
+ form: "single",
45312
+ optional: false
45313
+ }],
45314
+ "doorbell.getStatus": [{
45315
+ name: "deviceId",
45316
+ form: "single",
45317
+ optional: false
45318
+ }],
45319
+ "enumSensor.getStatus": [{
45320
+ name: "deviceId",
45321
+ form: "single",
45322
+ optional: false
45323
+ }],
45324
+ "eventEmitter.getStatus": [{
45325
+ name: "deviceId",
45326
+ form: "single",
45327
+ optional: false
45328
+ }],
45038
45329
  "faceGallery.getFaceByTrack": [{
45039
45330
  name: "deviceId",
45040
45331
  form: "single",
@@ -45049,6 +45340,11 @@ Object.freeze({
45049
45340
  form: "array",
45050
45341
  optional: true
45051
45342
  }],
45343
+ "fanControl.getStatus": [{
45344
+ name: "deviceId",
45345
+ form: "single",
45346
+ optional: false
45347
+ }],
45052
45348
  "fanControl.setDirection": [{
45053
45349
  name: "deviceId",
45054
45350
  form: "single",
@@ -45069,6 +45365,26 @@ Object.freeze({
45069
45365
  form: "single",
45070
45366
  optional: false
45071
45367
  }],
45368
+ "featureProbe.getStatus": [{
45369
+ name: "deviceId",
45370
+ form: "single",
45371
+ optional: false
45372
+ }],
45373
+ "flood.getStatus": [{
45374
+ name: "deviceId",
45375
+ form: "single",
45376
+ optional: false
45377
+ }],
45378
+ "gas.getStatus": [{
45379
+ name: "deviceId",
45380
+ form: "single",
45381
+ optional: false
45382
+ }],
45383
+ "humidifier.getStatus": [{
45384
+ name: "deviceId",
45385
+ form: "single",
45386
+ optional: false
45387
+ }],
45072
45388
  "humidifier.setMode": [{
45073
45389
  name: "deviceId",
45074
45390
  form: "single",
@@ -45084,11 +45400,26 @@ Object.freeze({
45084
45400
  form: "single",
45085
45401
  optional: false
45086
45402
  }],
45403
+ "humiditySensor.getStatus": [{
45404
+ name: "deviceId",
45405
+ form: "single",
45406
+ optional: false
45407
+ }],
45408
+ "image.getStatus": [{
45409
+ name: "deviceId",
45410
+ form: "single",
45411
+ optional: false
45412
+ }],
45087
45413
  "imageSettings.getOptions": [{
45088
45414
  name: "deviceId",
45089
45415
  form: "single",
45090
45416
  optional: false
45091
45417
  }],
45418
+ "imageSettings.getStatus": [{
45419
+ name: "deviceId",
45420
+ form: "single",
45421
+ optional: false
45422
+ }],
45092
45423
  "imageSettings.setSettings": [{
45093
45424
  name: "deviceId",
45094
45425
  form: "single",
@@ -45099,6 +45430,11 @@ Object.freeze({
45099
45430
  form: "single",
45100
45431
  optional: false
45101
45432
  }],
45433
+ "intercom.getStatus": [{
45434
+ name: "deviceId",
45435
+ form: "single",
45436
+ optional: false
45437
+ }],
45102
45438
  "intercom.handleAnswer": [{
45103
45439
  name: "deviceId",
45104
45440
  form: "single",
@@ -45129,6 +45465,11 @@ Object.freeze({
45129
45465
  form: "single",
45130
45466
  optional: false
45131
45467
  }],
45468
+ "lawnMowerControl.getStatus": [{
45469
+ name: "deviceId",
45470
+ form: "single",
45471
+ optional: false
45472
+ }],
45132
45473
  "lawnMowerControl.pause": [{
45133
45474
  name: "deviceId",
45134
45475
  form: "single",
@@ -45139,6 +45480,11 @@ Object.freeze({
45139
45480
  form: "single",
45140
45481
  optional: false
45141
45482
  }],
45483
+ "lockControl.getStatus": [{
45484
+ name: "deviceId",
45485
+ form: "single",
45486
+ optional: false
45487
+ }],
45142
45488
  "lockControl.lock": [{
45143
45489
  name: "deviceId",
45144
45490
  form: "single",
@@ -45154,6 +45500,11 @@ Object.freeze({
45154
45500
  form: "single",
45155
45501
  optional: false
45156
45502
  }],
45503
+ "mediaPlayer.getStatus": [{
45504
+ name: "deviceId",
45505
+ form: "single",
45506
+ optional: false
45507
+ }],
45157
45508
  "mediaPlayer.next": [{
45158
45509
  name: "deviceId",
45159
45510
  form: "single",
@@ -45214,6 +45565,11 @@ Object.freeze({
45214
45565
  form: "single",
45215
45566
  optional: false
45216
45567
  }],
45568
+ "motion.getStatus": [{
45569
+ name: "deviceId",
45570
+ form: "single",
45571
+ optional: false
45572
+ }],
45217
45573
  "motion.isDetected": [{
45218
45574
  name: "deviceId",
45219
45575
  form: "single",
@@ -45224,11 +45580,31 @@ Object.freeze({
45224
45580
  form: "single",
45225
45581
  optional: false
45226
45582
  }],
45583
+ "motionDetection.applyDeviceSettingsPatch": [{
45584
+ name: "deviceId",
45585
+ form: "single",
45586
+ optional: false
45587
+ }],
45588
+ "motionDetection.getDeviceLiveContribution": [{
45589
+ name: "deviceId",
45590
+ form: "single",
45591
+ optional: false
45592
+ }],
45593
+ "motionDetection.getDeviceSettingsContribution": [{
45594
+ name: "deviceId",
45595
+ form: "single",
45596
+ optional: false
45597
+ }],
45227
45598
  "motionDetection.removeCamera": [{
45228
45599
  name: "deviceId",
45229
45600
  form: "single",
45230
45601
  optional: false
45231
45602
  }],
45603
+ "motionTrigger.getStatus": [{
45604
+ name: "deviceId",
45605
+ form: "single",
45606
+ optional: false
45607
+ }],
45232
45608
  "motionTrigger.setMotionTrigger": [{
45233
45609
  name: "deviceId",
45234
45610
  form: "single",
@@ -45239,6 +45615,11 @@ Object.freeze({
45239
45615
  form: "single",
45240
45616
  optional: false
45241
45617
  }],
45618
+ "motionZones.getStatus": [{
45619
+ name: "deviceId",
45620
+ form: "single",
45621
+ optional: false
45622
+ }],
45242
45623
  "motionZones.setZone": [{
45243
45624
  name: "deviceId",
45244
45625
  form: "single",
@@ -45249,6 +45630,11 @@ Object.freeze({
45249
45630
  form: "single",
45250
45631
  optional: false
45251
45632
  }],
45633
+ "nativeObjectDetection.getStatus": [{
45634
+ name: "deviceId",
45635
+ form: "single",
45636
+ optional: false
45637
+ }],
45252
45638
  "nativeObjectDetection.setEnabled": [{
45253
45639
  name: "deviceId",
45254
45640
  form: "single",
@@ -45264,6 +45650,11 @@ Object.freeze({
45264
45650
  form: "single",
45265
45651
  optional: false
45266
45652
  }],
45653
+ "navigation.getStatus": [{
45654
+ name: "deviceId",
45655
+ form: "single",
45656
+ optional: false
45657
+ }],
45267
45658
  "navigation.goToPoint": [{
45268
45659
  name: "deviceId",
45269
45660
  form: "single",
@@ -45309,6 +45700,11 @@ Object.freeze({
45309
45700
  form: "single",
45310
45701
  optional: false
45311
45702
  }],
45703
+ "networkLink.getStatus": [{
45704
+ name: "deviceId",
45705
+ form: "single",
45706
+ optional: false
45707
+ }],
45312
45708
  "networkQuality.getDeviceStats": [{
45313
45709
  name: "deviceId",
45314
45710
  form: "single",
@@ -45319,6 +45715,11 @@ Object.freeze({
45319
45715
  form: "single",
45320
45716
  optional: false
45321
45717
  }],
45718
+ "notificationRules.getAlarmSensors": [{
45719
+ name: "deviceIds",
45720
+ form: "array",
45721
+ optional: false
45722
+ }],
45322
45723
  "notificationRules.setDeviceMuted": [{
45323
45724
  name: "deviceId",
45324
45725
  form: "single",
@@ -45329,11 +45730,26 @@ Object.freeze({
45329
45730
  form: "single",
45330
45731
  optional: false
45331
45732
  }],
45733
+ "notifier.getStatus": [{
45734
+ name: "deviceId",
45735
+ form: "single",
45736
+ optional: false
45737
+ }],
45332
45738
  "notifier.send": [{
45333
45739
  name: "deviceId",
45334
45740
  form: "single",
45335
45741
  optional: false
45336
45742
  }],
45743
+ "numericSensor.getStatus": [{
45744
+ name: "deviceId",
45745
+ form: "single",
45746
+ optional: false
45747
+ }],
45748
+ "osd.getStatus": [{
45749
+ name: "deviceId",
45750
+ form: "single",
45751
+ optional: false
45752
+ }],
45337
45753
  "osd.setOverlay": [{
45338
45754
  name: "deviceId",
45339
45755
  form: "single",
@@ -45393,6 +45809,11 @@ Object.freeze({
45393
45809
  form: "single",
45394
45810
  optional: false
45395
45811
  }],
45812
+ "petFeeder.getStatus": [{
45813
+ name: "deviceId",
45814
+ form: "single",
45815
+ optional: false
45816
+ }],
45396
45817
  "petFeeder.markFoodReplenished": [{
45397
45818
  name: "deviceId",
45398
45819
  form: "single",
@@ -45428,6 +45849,11 @@ Object.freeze({
45428
45849
  form: "single",
45429
45850
  optional: false
45430
45851
  }],
45852
+ "pipelineAnalytics.applyDeviceSettingsPatch": [{
45853
+ name: "deviceId",
45854
+ form: "single",
45855
+ optional: false
45856
+ }],
45431
45857
  "pipelineAnalytics.clearTrackRelation": [{
45432
45858
  name: "deviceId",
45433
45859
  form: "single",
@@ -45468,6 +45894,16 @@ Object.freeze({
45468
45894
  form: "single",
45469
45895
  optional: false
45470
45896
  }],
45897
+ "pipelineAnalytics.getDeviceLiveContribution": [{
45898
+ name: "deviceId",
45899
+ form: "single",
45900
+ optional: false
45901
+ }],
45902
+ "pipelineAnalytics.getDeviceSettingsContribution": [{
45903
+ name: "deviceId",
45904
+ form: "single",
45905
+ optional: false
45906
+ }],
45471
45907
  "pipelineAnalytics.getEventDensity": [{
45472
45908
  name: "deviceId",
45473
45909
  form: "single",
@@ -45683,6 +46119,11 @@ Object.freeze({
45683
46119
  form: "single",
45684
46120
  optional: true
45685
46121
  }],
46122
+ "pipelineOrchestrator.applyDeviceSettingsPatch": [{
46123
+ name: "deviceId",
46124
+ form: "single",
46125
+ optional: false
46126
+ }],
45686
46127
  "pipelineOrchestrator.assignAudio": [{
45687
46128
  name: "deviceId",
45688
46129
  form: "single",
@@ -45728,6 +46169,16 @@ Object.freeze({
45728
46169
  form: "single",
45729
46170
  optional: false
45730
46171
  }],
46172
+ "pipelineOrchestrator.getDeviceLiveContribution": [{
46173
+ name: "deviceId",
46174
+ form: "single",
46175
+ optional: false
46176
+ }],
46177
+ "pipelineOrchestrator.getDeviceSettingsContribution": [{
46178
+ name: "deviceId",
46179
+ form: "single",
46180
+ optional: false
46181
+ }],
45731
46182
  "pipelineOrchestrator.getPipelineAssignment": [{
45732
46183
  name: "deviceId",
45733
46184
  form: "single",
@@ -45848,11 +46299,31 @@ Object.freeze({
45848
46299
  form: "single",
45849
46300
  optional: true
45850
46301
  }],
46302
+ "powerMeter.getStatus": [{
46303
+ name: "deviceId",
46304
+ form: "single",
46305
+ optional: false
46306
+ }],
46307
+ "presence.getStatus": [{
46308
+ name: "deviceId",
46309
+ form: "single",
46310
+ optional: false
46311
+ }],
46312
+ "pressureSensor.getStatus": [{
46313
+ name: "deviceId",
46314
+ form: "single",
46315
+ optional: false
46316
+ }],
45851
46317
  "privacyMask.getOptions": [{
45852
46318
  name: "deviceId",
45853
46319
  form: "single",
45854
46320
  optional: false
45855
46321
  }],
46322
+ "privacyMask.getStatus": [{
46323
+ name: "deviceId",
46324
+ form: "single",
46325
+ optional: false
46326
+ }],
45856
46327
  "privacyMask.setAudioEnabled": [{
45857
46328
  name: "deviceId",
45858
46329
  form: "single",
@@ -45903,6 +46374,11 @@ Object.freeze({
45903
46374
  form: "single",
45904
46375
  optional: false
45905
46376
  }],
46377
+ "ptz.getStatus": [{
46378
+ name: "deviceId",
46379
+ form: "single",
46380
+ optional: false
46381
+ }],
45906
46382
  "ptz.goHome": [{
45907
46383
  name: "deviceId",
45908
46384
  form: "single",
@@ -45993,6 +46469,11 @@ Object.freeze({
45993
46469
  form: "single",
45994
46470
  optional: false
45995
46471
  }],
46472
+ "recordingArchive.applyDeviceSettingsPatch": [{
46473
+ name: "deviceId",
46474
+ form: "single",
46475
+ optional: false
46476
+ }],
45996
46477
  "recordingArchive.deleteFootprint": [{
45997
46478
  name: "deviceId",
45998
46479
  form: "single",
@@ -46013,6 +46494,21 @@ Object.freeze({
46013
46494
  form: "single",
46014
46495
  optional: false
46015
46496
  }],
46497
+ "recordingArchive.getDeviceLiveContribution": [{
46498
+ name: "deviceId",
46499
+ form: "single",
46500
+ optional: false
46501
+ }],
46502
+ "recordingArchive.getDeviceSettingsContribution": [{
46503
+ name: "deviceId",
46504
+ form: "single",
46505
+ optional: false
46506
+ }],
46507
+ "recordingArchive.getStatus": [{
46508
+ name: "deviceId",
46509
+ form: "single",
46510
+ optional: false
46511
+ }],
46016
46512
  "recordingArchive.listOpsLog": [{
46017
46513
  name: "deviceId",
46018
46514
  form: "single",
@@ -46098,6 +46594,11 @@ Object.freeze({
46098
46594
  form: "single",
46099
46595
  optional: false
46100
46596
  }],
46597
+ "recordingOnboard.getStatus": [{
46598
+ name: "deviceId",
46599
+ form: "single",
46600
+ optional: false
46601
+ }],
46101
46602
  "recordingOnboard.setSettings": [{
46102
46603
  name: "deviceId",
46103
46604
  form: "single",
@@ -46128,6 +46629,11 @@ Object.freeze({
46128
46629
  form: "single",
46129
46630
  optional: false
46130
46631
  }],
46632
+ "sceneMonitor.getStatus": [{
46633
+ name: "deviceId",
46634
+ form: "single",
46635
+ optional: false
46636
+ }],
46131
46637
  "sceneMonitor.listScenes": [{
46132
46638
  name: "deviceId",
46133
46639
  form: "single",
@@ -46153,6 +46659,11 @@ Object.freeze({
46153
46659
  form: "single",
46154
46660
  optional: false
46155
46661
  }],
46662
+ "scriptRunner.getStatus": [{
46663
+ name: "deviceId",
46664
+ form: "single",
46665
+ optional: false
46666
+ }],
46156
46667
  "scriptRunner.run": [{
46157
46668
  name: "deviceId",
46158
46669
  form: "single",
@@ -46163,11 +46674,31 @@ Object.freeze({
46163
46674
  form: "single",
46164
46675
  optional: false
46165
46676
  }],
46677
+ "smoke.getStatus": [{
46678
+ name: "deviceId",
46679
+ form: "single",
46680
+ optional: false
46681
+ }],
46682
+ "snapshot.applyDeviceSettingsPatch": [{
46683
+ name: "deviceId",
46684
+ form: "single",
46685
+ optional: false
46686
+ }],
46166
46687
  "snapshot.getDebugState": [{
46167
46688
  name: "deviceId",
46168
46689
  form: "single",
46169
46690
  optional: false
46170
46691
  }],
46692
+ "snapshot.getDeviceLiveContribution": [{
46693
+ name: "deviceId",
46694
+ form: "single",
46695
+ optional: false
46696
+ }],
46697
+ "snapshot.getDeviceSettingsContribution": [{
46698
+ name: "deviceId",
46699
+ form: "single",
46700
+ optional: false
46701
+ }],
46171
46702
  "snapshot.getSnapshot": [{
46172
46703
  name: "deviceId",
46173
46704
  form: "single",
@@ -46184,6 +46715,11 @@ Object.freeze({
46184
46715
  form: "array",
46185
46716
  optional: false
46186
46717
  }],
46718
+ "snapshot.getStatus": [{
46719
+ name: "deviceId",
46720
+ form: "single",
46721
+ optional: false
46722
+ }],
46187
46723
  "snapshot.invalidateCache": [{
46188
46724
  name: "deviceId",
46189
46725
  form: "single",
@@ -46194,6 +46730,11 @@ Object.freeze({
46194
46730
  form: "single",
46195
46731
  optional: false
46196
46732
  }],
46733
+ "streamBroker.applyDeviceSettingsPatch": [{
46734
+ name: "deviceId",
46735
+ form: "single",
46736
+ optional: false
46737
+ }],
46197
46738
  "streamBroker.assignProfile": [{
46198
46739
  name: "deviceId",
46199
46740
  form: "single",
@@ -46209,6 +46750,16 @@ Object.freeze({
46209
46750
  form: "single",
46210
46751
  optional: false
46211
46752
  }],
46753
+ "streamBroker.getDeviceLiveContribution": [{
46754
+ name: "deviceId",
46755
+ form: "single",
46756
+ optional: false
46757
+ }],
46758
+ "streamBroker.getDeviceSettingsContribution": [{
46759
+ name: "deviceId",
46760
+ form: "single",
46761
+ optional: false
46762
+ }],
46212
46763
  "streamBroker.getStreamWithCodec": [{
46213
46764
  name: "deviceId",
46214
46765
  form: "single",
@@ -46269,16 +46820,46 @@ Object.freeze({
46269
46820
  form: "single",
46270
46821
  optional: false
46271
46822
  }],
46823
+ "streamParams.getStatus": [{
46824
+ name: "deviceId",
46825
+ form: "single",
46826
+ optional: false
46827
+ }],
46272
46828
  "streamParams.setProfile": [{
46273
46829
  name: "deviceId",
46274
46830
  form: "single",
46275
46831
  optional: false
46276
46832
  }],
46833
+ "switch.getStatus": [{
46834
+ name: "deviceId",
46835
+ form: "single",
46836
+ optional: false
46837
+ }],
46277
46838
  "switch.setState": [{
46278
46839
  name: "deviceId",
46279
46840
  form: "single",
46280
46841
  optional: false
46281
46842
  }],
46843
+ "tamper.getStatus": [{
46844
+ name: "deviceId",
46845
+ form: "single",
46846
+ optional: false
46847
+ }],
46848
+ "temperatureSensor.getStatus": [{
46849
+ name: "deviceId",
46850
+ form: "single",
46851
+ optional: false
46852
+ }],
46853
+ "update.getStatus": [{
46854
+ name: "deviceId",
46855
+ form: "single",
46856
+ optional: false
46857
+ }],
46858
+ "vacuumControl.getStatus": [{
46859
+ name: "deviceId",
46860
+ form: "single",
46861
+ optional: false
46862
+ }],
46282
46863
  "vacuumControl.locate": [{
46283
46864
  name: "deviceId",
46284
46865
  form: "single",
@@ -46314,6 +46895,11 @@ Object.freeze({
46314
46895
  form: "single",
46315
46896
  optional: false
46316
46897
  }],
46898
+ "valve.getStatus": [{
46899
+ name: "deviceId",
46900
+ form: "single",
46901
+ optional: false
46902
+ }],
46317
46903
  "valve.open": [{
46318
46904
  name: "deviceId",
46319
46905
  form: "single",
@@ -46329,6 +46915,11 @@ Object.freeze({
46329
46915
  form: "single",
46330
46916
  optional: false
46331
46917
  }],
46918
+ "vibration.getStatus": [{
46919
+ name: "deviceId",
46920
+ form: "single",
46921
+ optional: false
46922
+ }],
46332
46923
  "videoclips.dialClipStream": [{
46333
46924
  name: "deviceId",
46334
46925
  form: "single",
@@ -46364,6 +46955,11 @@ Object.freeze({
46364
46955
  form: "single",
46365
46956
  optional: false
46366
46957
  }],
46958
+ "waterHeater.getStatus": [{
46959
+ name: "deviceId",
46960
+ form: "single",
46961
+ optional: false
46962
+ }],
46367
46963
  "waterHeater.setAway": [{
46368
46964
  name: "deviceId",
46369
46965
  form: "single",
@@ -46379,6 +46975,11 @@ Object.freeze({
46379
46975
  form: "single",
46380
46976
  optional: false
46381
46977
  }],
46978
+ "weather.getStatus": [{
46979
+ name: "deviceId",
46980
+ form: "single",
46981
+ optional: false
46982
+ }],
46382
46983
  "webrtcSession.addIceCandidate": [{
46383
46984
  name: "deviceId",
46384
46985
  form: "single",
@@ -47116,7 +47717,7 @@ var PreviewTextField = string().max(200);
47116
47717
  * Whether the notification's preview is a STILL or a short animation.
47117
47718
  *
47118
47719
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
47119
- * rule)"* — the other rules already offer an animated preview, and a ten-hour
47720
+ * rule)"* [sent as a gif or video (as for the other rules)] — the other rules already offer an animated preview, and a ten-hour
47120
47721
  * night reads better as three seconds of motion than as one frame of it. Both
47121
47722
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
47122
47723
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -47254,6 +47855,7 @@ TimelapseRuleInputSchema.extend({
47254
47855
  createdAt: number(),
47255
47856
  updatedAt: number()
47256
47857
  });
47858
+ MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
47257
47859
  object({
47258
47860
  /**
47259
47861
  * Fraction of the box's own size added on EACH side before cutting.
@@ -51470,7 +52072,7 @@ var ExportAlexaAddon = class extends BaseAddon {
51470
52072
  * One reconcile pass over every exposed device shortly after boot. This is
51471
52073
  * what heals a shape that drifted while the addon was DOWN (or under an
51472
52074
  * older build that had no signature at all) with no operator click — the
51473
- * doorbell wrapper bound on 615 is exactly that case. Deliberately late
52075
+ * doorbell bound on 615 after it was exposed is exactly that case. Deliberately late
51474
52076
  * enough that device providers have registered their caps: a device whose
51475
52077
  * bindings have not landed yet builds a shape we must not push.
51476
52078
  */