@camstack/addon-export-hap 1.2.142 → 1.2.144

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.
@@ -48,7 +48,7 @@ node_fs_promises = __toESM(node_fs_promises, 1);
48
48
  let node_events = require("node:events");
49
49
  let node_dgram = require("node:dgram");
50
50
  let node_os = require("node:os");
51
- //#region ../types/dist/event-category-C5xZWqz6.mjs
51
+ //#region ../types/dist/event-category-BGmz_oxW.mjs
52
52
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
53
53
  EventCategory["SystemBoot"] = "system.boot";
54
54
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -581,6 +581,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
581
581
  */
582
582
  EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
583
583
  EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
584
+ /**
585
+ * A camera provider's RAW onboard motion push (D673): the `MotionOnMotionChanged`
586
+ * data shape without `triggeredBy`, built by `providerMotionEvent`. Its one
587
+ * consumer is the orchestrator's camera-sensors bridge, which re-emits the
588
+ * camera's EFFECTIVE onboard motion as `MotionOnMotionChanged` (a pass-through
589
+ * for a camera with no attached sensor). Providers must not emit
590
+ * `MotionOnMotionChanged` themselves; `scripts/check-onboard-motion-emitters.ts`
591
+ * keeps it so (camera-grid is a named, deferred exception).
592
+ */
584
593
  EventCategory["ProviderMotion"] = "provider.motion";
585
594
  EventCategory["ProviderDetection"] = "provider.detection";
586
595
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
@@ -5419,7 +5428,7 @@ var ZodIssueCode = {
5419
5428
  var ZodFirstPartyTypeKind;
5420
5429
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5421
5430
  //#endregion
5422
- //#region ../types/dist/sleep-gU6hqv4_.mjs
5431
+ //#region ../types/dist/sleep-CDCONfkt.mjs
5423
5432
  /**
5424
5433
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5425
5434
  * window to float samples (D455).
@@ -7517,7 +7526,7 @@ function errMsg$16(err) {
7517
7526
  return String(err);
7518
7527
  }
7519
7528
  //#endregion
7520
- //#region ../types/dist/composition-Dq42XWp0.mjs
7529
+ //#region ../types/dist/composition-Bzr7GwgP.mjs
7521
7530
  var AUDIO_ENCODER_BY_CODEC = {
7522
7531
  opus: "libopus",
7523
7532
  aac: "aac",
@@ -8166,21 +8175,20 @@ function portable(target) {
8166
8175
  }
8167
8176
  portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
8168
8177
  /**
8169
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
8170
- * valutatore puro del suo FRONTE.
8178
+ * THE table "which boolean of this device type counts as HIGH", and the
8179
+ * pure evaluator of its EDGE.
8171
8180
  *
8172
- * Viveva dentro il builtin virtual-doorbell
8173
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
8174
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
8175
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
8176
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
8177
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
8178
- * SPOSTA qui e il doorbell la ri-esporta.
8181
+ * ONE table for everyone who asks "is this device HIGH?": the
8182
+ * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
8183
+ * sensor markers, the orchestrator's camera-sensors bridge and
8184
+ * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
8185
+ * A second copy would diverge at the first cap added, and the symptom — "the
8186
+ * sensor rings the doorbell but does not record" — is exactly D62.
8179
8187
  *
8180
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
8181
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
8182
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
8183
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
8188
+ * ⚠ NOT `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), and the
8189
+ * two must not be unified: that one answers "what is the state WORD for a
8190
+ * rule" (and includes `presence`, `cover`, `alarm-panel`), this one "which
8191
+ * boolean's EDGE counts". Deliberately different vocabularies.
8184
8192
  */
8185
8193
  /**
8186
8194
  * Known binary / switch source caps → the boolean slice field whose
@@ -8201,6 +8209,8 @@ var SOURCE_CAP_ACTIVE_FIELD = {
8201
8209
  vibration: "detected",
8202
8210
  tamper: "tampered"
8203
8211
  };
8212
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
8213
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
8204
8214
  /**
8205
8215
  * Error types for the safe expression engine. Two distinct classes so callers
8206
8216
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -9857,22 +9867,22 @@ var CLASS_MAP_MACRO_TARGETS = [
9857
9867
  "package"
9858
9868
  ];
9859
9869
  /**
9860
- * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
9861
- * un operatore può selezionare.
9870
+ * The FIRST-LEVEL macro classes: those an object detector emits and that
9871
+ * an operator can select.
9862
9872
  *
9863
- * Sono le tre offerte dallo step `object-detection`
9873
+ * They are the three offered by the `object-detection` step
9864
9874
  * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
9865
- * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
9866
- * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
9867
- * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
9868
- * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
9875
+ * `enabledMacroClasses`). `package` is in {@link CLASS_MAP_MACRO_TARGETS} and in
9876
+ * `MACRO_LABELS` — it is a true macro — but NOT here: it belongs to the step
9877
+ * `package-detection`, whose enablement is driven by zones, and offering the
9878
+ * same word twice has already made an operator turn on the COCO proxy
9869
9879
  * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
9870
9880
  *
9871
- * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
9872
- * dello step e una seconda volta come union `FirstLevelMacro`
9873
- * (`types/detection.ts`); una terza copia per il trigger di registrazione
9874
- * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
9875
- * successiva.
9881
+ * ONE list. Before today the same three were hand-written in the step's offer
9882
+ * and a second time as the union `FirstLevelMacro`
9883
+ * (`types/detection.ts`); a third copy for the recording trigger
9884
+ * (`RecordingTriggers.objectClasses`) would have made the later divergence
9885
+ * invisible.
9876
9886
  */
9877
9887
  var FIRST_LEVEL_MACRO_CLASSES = [
9878
9888
  "person",
@@ -9937,17 +9947,17 @@ var RecordingStorageModeSchema = _enum([
9937
9947
  "on-device-decision"
9938
9948
  ]);
9939
9949
  /**
9940
- * Le macro classi che possono aprire una finestra di registrazione — le stesse
9941
- * tre offerte dallo step `object-detection`, da UNA lista
9950
+ * The macro classes that can open a recording window — the same
9951
+ * three offered by the `object-detection` step, from ONE list
9942
9952
  * ({@link FIRST_LEVEL_MACRO_CLASSES}).
9943
9953
  */
9944
9954
  var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
9945
9955
  /**
9946
- * True quando `values` non ripete un elemento.
9956
+ * True when `values` does not repeat an element.
9947
9957
  *
9948
- * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
9949
- * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
9950
- * conterebbe due volte le sue finestre in `segmentMissedByMs`.
9958
+ * A duplicate is not harmless: every entry of `objectClasses` / `sensorDeviceIds`
9959
+ * becomes a SOURCE in `bandTriggerSources`, and the same source twice
9960
+ * would count its windows twice in `segmentMissedByMs`.
9951
9961
  */
9952
9962
  var noDuplicates = (values) => new Set(values).size === values.length;
9953
9963
  /** Which detectors trigger an `events`-mode band. */
@@ -9955,34 +9965,34 @@ var RecordingTriggersSchema = object({
9955
9965
  motion: boolean().optional(),
9956
9966
  audioThresholdDbfs: number().optional(),
9957
9967
  /**
9958
- * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
9959
- * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
9960
- * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
9961
- * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
9968
+ * The macro classes whose detection opens a window. ABSENT = the source
9969
+ * is not listened to; an EMPTY array is rejected, because "events band, object
9970
+ * trigger on, no class" is the same shape "enabled and records
9971
+ * nothing, forever" that `eventsBandCanEverDemand` is written against.
9962
9972
  *
9963
- * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
9964
- * quelle che hanno attraversato `enabledMacroClasses`, i
9965
- * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
9966
- * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
9967
- * finestre — vedi `recorder/object-trigger.ts`.
9973
+ * The signal read is ALREADY FILTERED: only `source: 'pipeline'` detections, i.e.
9974
+ * those that went through `enabledMacroClasses`, the
9975
+ * `minConfidence<Macro>` and the full-frame guard. The camera's onboard AI
9976
+ * (`source: 'onboard'`) goes through none of those gates and does NOT open
9977
+ * windows — see `recorder/object-trigger.ts`.
9968
9978
  */
9969
9979
  objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
9970
9980
  /**
9971
- * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
9972
- * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
9981
+ * The LINKED devices whose RISING EDGE opens a window. Absent = the source
9982
+ * is not listened to; an empty array is rejected for the same reason as
9973
9983
  * `objectClasses`.
9974
9984
  *
9975
- * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
9976
- * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
9977
- * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
9978
- * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9979
- * device (D12) — mai un elenco globale di cap.
9985
+ * These are SOURCE device ids, not cameras: the band names them, so the
9986
+ * hot path (`DeviceStateChanged`, at bus rate across the whole fleet)
9987
+ * does no RPC. The OFFER from which the operator picks them is another question, and
9988
+ * is resolved with `deviceManager.getLinkedDevices` + `getBindingsBatch` per
9989
+ * device (D12) — never a global list of caps.
9980
9990
  *
9981
- * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
9982
- * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
9983
- * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
9984
- * il livello: un contatto trovato già aperto al riavvio del runner non fa
9985
- * registrare.
9991
+ * What "high" means depends on the device TYPE and is not decided here:
9992
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) is THE table,
9993
+ * the same one as the composite doorbell (D671). And it is the EDGE, not
9994
+ * the level: a contact found already open at runner restart does not
9995
+ * record.
9986
9996
  */
9987
9997
  sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
9988
9998
  });
@@ -11125,7 +11135,7 @@ var DecoderSessionConfigSchema = object({
11125
11135
  * - **VALUE** — at which level, for which scope, until when. That stays ONE
11126
11136
  * thing: the logging settings document on the `system` cap. Two authorities
11127
11137
  * over the values is the exact defect
11128
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
11138
+ * `docs/design/plans/2026-08-26-per-component-logging.md` was written to
11129
11139
  * remove; re-introducing it from the cure side would be grotesque.
11130
11140
  *
11131
11141
  * Nothing in this file reads a clock, an env var or a store. The registry is
@@ -12924,7 +12934,7 @@ var BrokerStatusEnum = _enum([
12924
12934
  "unreachable",
12925
12935
  "error"
12926
12936
  ]);
12927
- var BrokerInfoSchema$1 = object({
12937
+ var BrokerInfoSchema = object({
12928
12938
  /** Stable broker id. Persisted; survives addon restarts. */
12929
12939
  id: string(),
12930
12940
  /** Addon id of the provider that OWNS this broker.
@@ -13057,7 +13067,7 @@ var GetStateInputSchema = object({
13057
13067
  * HA: entity_id (returns the cached entity state). */
13058
13068
  key: string()
13059
13069
  });
13060
- method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, BrokerInfoSchema$1.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
13070
+ method(ListInputSchema, array(BrokerInfoSchema)), method(GetInputSchema, BrokerInfoSchema.nullable()), method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }), method(AddInputSchema, AddResultSchema, {
13061
13071
  kind: "mutation",
13062
13072
  auth: "admin"
13063
13073
  }), method(RemoveInputSchema, _void(), {
@@ -14666,7 +14676,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
14666
14676
  *
14667
14677
  * ⚠ An earlier version of this comment blamed Zod stripping, and that
14668
14678
  * was wrong — corrected 2026-08-26 after the hop map
14669
- * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
14679
+ * (`docs/design/2026-08-26-topic-hop-map.md`) traced the path.
14670
14680
  * There is **no Zod parse at all** between the door and the engine: the
14671
14681
  * dispatcher forwards the payload verbatim and UDS carries it whole. A
14672
14682
  * field declared here reaches `SqliteSettingsBackend` either way.
@@ -15503,7 +15513,17 @@ var SiteZoneSchema = object({
15503
15513
  var ProviderStatusSchema = object({
15504
15514
  connected: boolean(),
15505
15515
  deviceCount: number(),
15506
- error: string().optional()
15516
+ error: string().optional(),
15517
+ /**
15518
+ * `'orchestrator'`: this provider writes NO camera's `motion` runtime slice
15519
+ * — the orchestrator is its only writer (D696). Set by `BaseDeviceProvider`,
15520
+ * which every provider bundles, so only a provider BUILT from D696-amended
15521
+ * code says it. ABSENT means an older provider that may still write it (raw
15522
+ * VMD writes, a boot seed), and the orchestrator's provider gate keeps that
15523
+ * provider's cameras unwritten (`camera-sensors/motion-slice-provider-gate.ts`)
15524
+ * — the provider twin of the runner's `getLocalLoad().motionSliceWriter`.
15525
+ */
15526
+ motionSliceWriter: literal("orchestrator").optional()
15507
15527
  });
15508
15528
  object({
15509
15529
  externalId: string(),
@@ -16301,28 +16321,10 @@ method(object({
16301
16321
  deviceIds: array(number()).readonly(),
16302
16322
  caps: array(string()).readonly().optional()
16303
16323
  }), record(string(), record(string(), unknown().nullable())));
16304
- object({
16305
- /** Ms epoch of the last press. null = never observed since this provider started. */
16306
- lastPressedAt: number().nullable(),
16307
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
16308
- pressCountSinceStart: number()
16309
- });
16310
- object({
16311
- deviceId: number(),
16312
- timestamp: number()
16313
- });
16314
- /**
16315
- * The `doorbell-source` extension slot's editable value (D648): the camera's
16316
- * virtual-doorbell trigger devices, exactly what the wrapper's
16317
- * `doorbellSources` persists (as `{ deviceId }` records).
16318
- */
16319
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
16324
+ var ExtensionSlotIdSchema = _enum(["linked-devices"]);
16320
16325
  DeviceType.Camera;
16321
- DeviceType.Button, DeviceType.Camera;
16322
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
16323
- DeviceType.Camera, DeviceType.Camera;
16324
16326
  /** A slot's editable value, discriminated on `slot`. */
16325
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
16327
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
16326
16328
  DeviceType.Camera;
16327
16329
  var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
16328
16330
  state: literal("unreachable"),
@@ -16359,10 +16361,11 @@ var ExtensionSlotSourceSchema = object({
16359
16361
  });
16360
16362
  /**
16361
16363
  * A DIFFERENT authority that gates whether the slot's sources do anything —
16362
- * reported read-only, named, never written through this contract (D62). The
16363
- * virtual doorbell's sources ring only while the wrapper is bound to the
16364
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
16365
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
16364
+ * reported read-only, named, never written through this contract (D62) — e.g.
16365
+ * a slot whose sources act only while a wrapper is bound to the host, a
16366
+ * binding that belongs to `deviceManager.setWrapperActive`. No slot reports
16367
+ * one today (`linked-devices` answers `null`). `unknown` means the gate could
16368
+ * not be read; it is not "inactive" (D315).
16366
16369
  */
16367
16370
  var ExtensionSlotActivationSchema = object({
16368
16371
  state: _enum([
@@ -16378,7 +16381,7 @@ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
16378
16381
  /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
16379
16382
  slotRef: string(),
16380
16383
  slot: ExtensionSlotIdSchema,
16381
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
16384
+ /** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
16382
16385
  authority: string(),
16383
16386
  activation: ExtensionSlotActivationSchema.nullable(),
16384
16387
  value: ExtensionSlotValueSchema,
@@ -16656,7 +16659,7 @@ method(object({
16656
16659
  * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
16657
16660
  * ends in a log line, and a log line is the thing the operator asked to stop
16658
16661
  * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
16659
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
16662
+ * 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
16660
16663
  * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
16661
16664
  * media blackout were both diagnosed. The counters stay; this is where they can
16662
16665
  * be READ.
@@ -18032,7 +18035,7 @@ var BrokerStatusSchema$1 = _enum([
18032
18035
  "unreachable",
18033
18036
  "tls-error"
18034
18037
  ]);
18035
- var BrokerInfoSchema = object({
18038
+ var BrokerInfoSchema$1 = object({
18036
18039
  id: string(),
18037
18040
  name: string(),
18038
18041
  url: string(),
@@ -18095,7 +18098,7 @@ var StatusSchema = object({
18095
18098
  brokerCount: number(),
18096
18099
  embeddedRunning: boolean()
18097
18100
  });
18098
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
18101
+ method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
18099
18102
  kind: "mutation",
18100
18103
  auth: "admin"
18101
18104
  }), method(IdInputSchema, _void(), {
@@ -20271,78 +20274,16 @@ var NcRuleInputSchema = object({
20271
20274
  */
20272
20275
  confirm: NcConfirmSchema.optional(),
20273
20276
  /**
20274
- * WAIT for face/plate recognition before saying anything.
20275
- *
20276
- * A notification's TEXT is frozen at enqueue and its media is re-resolved at
20277
- * send; the identity is neither. A face is confirmed after `confirmFrames`
20278
- * agreeing observations — p50 **11.4 s** after the track was first seen,
20279
- * measured on this hub — and an `immediate` rule enqueues on the first object
20280
- * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
20281
- * immediate path, and no amount of media re-resolution fixes a sentence.
20282
- *
20283
- * Only two honest answers exist, and this flag picks between them. It has
20284
- * effect ONLY on a rule that declares a recognition scope
20285
- * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
20286
- * other rule there is nothing to wait for and the flag is inert.
20287
- *
20288
- * | value | what happens |
20289
- * | --- | --- |
20290
- * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
20291
- * | 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) |
20292
- *
20293
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
20294
- * on the addon cap path, and absent has to keep meaning exactly what every
20295
- * rule authored before this field meant.
20296
- *
20297
- * The cost of `true` is stated here because the editor states it too: a rule
20298
- * that waits also inherits track-close SEMANTICS — its `zones` condition
20299
- * tests every zone the track visited and a `crossing` condition can no longer
20300
- * be satisfied, because a closed track carries no crossing.
20301
- */
20302
- waitForEnhancement: boolean().optional(),
20303
- /**
20304
- * GROUP a burst of subjects into ONE notification that grows.
20305
- *
20306
- * Seconds of quiet after the last matching subject before the burst is
20307
- * considered over. While it is open, the first subject enqueues immediately —
20308
- * **exactly as today, with no added latency** — and every real growth (a new
20309
- * subject, or a name confirmed on one already in it) REPLACES that
20310
- * notification with an updated one naming everybody. The push carries the
20311
- * group's own coalescing tag, so the phone replaces rather than stacks.
20312
- *
20313
- * `0` / absent = off, and off is today's behaviour byte for byte.
20314
- *
20315
- * ### Why an idle cutoff and not a window
20316
- *
20317
- * The measured seven-person arrival on device 590 spans 110 s with every
20318
- * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
20319
- * idle cutoff holds it as one and ends it when the arrival actually ends.
20320
- * 30 s is a widely shipped value for the same decision.
20321
- *
20322
- * ### What it replaces
20323
- *
20324
- * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
20325
- * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
20326
- * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
20327
- * track that did fire and 7 carrying a confirmed identity nobody heard about.
20328
- * A group collapses the same volume by MERGING, so the cooldown becomes a
20329
- * budget over GROUPS — which is what it always meant — and a growth is never
20330
- * throttled by the window its own first member spent.
20331
- *
20332
- * ### Interaction with {@link waitForEnhancement}
20333
- *
20334
- * They compose, and the order matters. `waitForEnhancement` defers the rule to
20335
- * TRACK CLOSE, so with both set the group is opened by the first member to
20336
- * CLOSE — already carrying its name — and grows as later members close. That
20337
- * is later, and complete. With grouping alone the group opens on the first
20338
- * object event and picks up names as they are confirmed, through the growth
20339
- * path. Neither combination fires twice for one subject.
20340
- *
20341
- * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
20342
- * on the addon cap path, so absent must keep meaning what it meant before this
20343
- * field existed.
20277
+ * IMMEDIATE GROUPED (D692): the first matching subject opens a window of this many
20278
+ * seconds; the rule sends ONE message when the window closes — early, once every
20279
+ * subject in it is final, never before {@link NC_GROUPING_MIN_SEC} — naming everybody
20280
+ * the rule matched, with one mosaic tile per subject.
20281
+ * Absent = plain immediate (one message per track, at the first match).
20282
+ * Only an `immediate` DETECTION rule may carry it (`ruleGroupingApplies`, enforced on
20283
+ * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
20284
+ * path does not run Zod defaults.
20344
20285
  */
20345
- groupIdleSec: number().int().min(0).max(600).optional()
20286
+ groupingSec: number().int().min(5).max(60).optional()
20346
20287
  });
20347
20288
  /**
20348
20289
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -20390,9 +20331,10 @@ var NcRuleClearableKeySchema = _enum([
20390
20331
  "targetUsers",
20391
20332
  "snoozeOptions",
20392
20333
  "snoozeAllowGlobal",
20393
- "waitForEnhancement",
20394
20334
  "actions",
20395
20335
  "confirm",
20336
+ "groupingSec",
20337
+ "waitForEnhancement",
20396
20338
  "groupIdleSec"
20397
20339
  ]);
20398
20340
  var NcRuleUpdateInputSchema = object({
@@ -20414,9 +20356,25 @@ var NcRuleSchema = NcRuleInputSchema.extend({
20414
20356
  */
20415
20357
  disabledTargetIds: array(string()).default([])
20416
20358
  });
20359
+ /**
20360
+ * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
20361
+ * still-true state. `backups-exports` and `alarm` are not sections — a completed
20362
+ * job is not an open state, and the alarm stays instant.
20363
+ */
20364
+ var NcStateSummarySectionIdSchema = _enum([
20365
+ "device-state",
20366
+ "streams",
20367
+ "battery",
20368
+ "consumables",
20369
+ "tanks",
20370
+ "detection-blind",
20371
+ "nodes",
20372
+ "addons",
20373
+ "updates"
20374
+ ]);
20417
20375
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
20418
20376
  var NcStateSummaryPreviewSectionSchema = object({
20419
- id: string(),
20377
+ id: NcStateSummarySectionIdSchema,
20420
20378
  status: _enum(["read", "unavailable"]),
20421
20379
  items: number().int().nonnegative(),
20422
20380
  reason: string().optional()
@@ -20426,7 +20384,7 @@ var NcStateSummaryPreviewSectionSchema = object({
20426
20384
  * a `rule` split (no `section`), one section's on a `section` split.
20427
20385
  */
20428
20386
  var NcStateSummaryPreviewRowSchema = object({
20429
- section: string().optional(),
20387
+ section: NcStateSummarySectionIdSchema.optional(),
20430
20388
  /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
20431
20389
  title: string(),
20432
20390
  text: string(),
@@ -20863,6 +20821,29 @@ object({
20863
20821
  firstAt: number(),
20864
20822
  lastAt: number()
20865
20823
  });
20824
+ var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
20825
+ var NcAlarmDelaySecSchema = number().int().min(0).max(600);
20826
+ var NcAlarmTriggeredSecSchema = number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
20827
+ var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
20828
+ var NcAlarmModeDelaysListSchema = array(object({
20829
+ mode: AlarmArmModeSchema,
20830
+ exitDelaySec: NcAlarmDelaySecSchema.optional(),
20831
+ entryDelaySec: NcAlarmDelaySecSchema.optional(),
20832
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
20833
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20834
+ var NcAlarmModeDelaysEditListSchema = array(object({
20835
+ mode: AlarmArmModeSchema,
20836
+ exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20837
+ entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
20838
+ triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
20839
+ })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20840
+ /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20841
+ var NcAlarmNonBlockingEditSchema = object({
20842
+ mode: AlarmArmModeSchema,
20843
+ deviceId: number().int().positive(),
20844
+ /** true = on the list (may be open, does not block); false = off the list (blocks). */
20845
+ nonBlocking: boolean()
20846
+ });
20866
20847
  /**
20867
20848
  * The per-mode lists, at most one per mode. Two lists for one mode have no
20868
20849
  * meaning a reader could agree on (union? the last one?), so they are refused
@@ -20871,10 +20852,10 @@ object({
20871
20852
  var NcAlarmNonBlockingListSchema = array(object({
20872
20853
  mode: AlarmArmModeSchema,
20873
20854
  deviceIds: array(number().int()).max(200)
20874
- })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
20855
+ })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
20875
20856
  /**
20876
- * The three durations the panel's state machine runs on, who hears about its
20877
- * transitions, and which openings each mode tolerates.
20857
+ * The three durations — global, and per mode since D697 — the panel's state
20858
+ * machine runs on, who hears about its transitions, and which openings each mode tolerates.
20878
20859
  *
20879
20860
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
20880
20861
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -20884,15 +20865,15 @@ var NcAlarmNonBlockingListSchema = array(object({
20884
20865
  */
20885
20866
  var NcAlarmSettingsSchema = object({
20886
20867
  /** Grace period between arming and the mode taking effect. 0 = immediate. */
20887
- exitDelaySec: number().int().min(0).max(600),
20868
+ exitDelaySec: NcAlarmDelaySecSchema,
20888
20869
  /** Grace period between a trigger and the alarm firing. 0 = immediate. */
20889
- entryDelaySec: number().int().min(0).max(600),
20870
+ entryDelaySec: NcAlarmDelaySecSchema,
20890
20871
  /**
20891
20872
  * How long `triggered` lasts before the panel re-arms itself.
20892
20873
  * **0 = until an operator disarms it** — the behaviour before this field
20893
20874
  * existed, and therefore what an untouched install keeps doing.
20894
20875
  */
20895
- triggeredDurationSec: number().int().min(0).max(3600),
20876
+ triggeredDurationSec: NcAlarmTriggeredSecSchema,
20896
20877
  /**
20897
20878
  * Deprecated by D630 — every transition is announced. Parsed, never read;
20898
20879
  * remove after one release. Optional so an older admin's patch (which still
@@ -20915,17 +20896,30 @@ var NcAlarmSettingsSchema = object({
20915
20896
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
20916
20897
  * parses as "every opening blocks" — the behaviour it was written under.
20917
20898
  */
20918
- nonBlocking: NcAlarmNonBlockingListSchema.default([])
20899
+ nonBlocking: NcAlarmNonBlockingListSchema.default([]),
20900
+ /**
20901
+ * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
20902
+ * without a field, uses the global value above — so `[]`, the default, is exactly the
20903
+ * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
20904
+ */
20905
+ modeDelays: NcAlarmModeDelaysListSchema.default([])
20919
20906
  });
20920
20907
  /**
20921
20908
  * Every field optional — a tab edits one control at a time.
20922
20909
  *
20923
- * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
20924
- * remove an inner `.default()`, so a patch that never named the field would
20925
- * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
20926
- * (the same trap {@link NcRulePatchSchema} documents).
20910
+ * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
20911
+ * `.partial()` does not remove an inner `.default()`, so a patch that never
20912
+ * named the field would parse to `[]` and wipe every mode's list on an
20913
+ * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
20914
+ * `modeDelays` means EDITS, not the stored overrides.
20927
20915
  */
20928
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
20916
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
20917
+ nonBlocking: NcAlarmNonBlockingListSchema,
20918
+ /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
20919
+ modeDelays: NcAlarmModeDelaysEditListSchema,
20920
+ /** ONE membership change; the panel applies it to the list it holds at its turn. */
20921
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema
20922
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
20929
20923
  /**
20930
20924
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
20931
20925
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -21025,6 +21019,28 @@ var NcAlarmConfigSchema = object({
21025
21019
  */
21026
21020
  live: NcAlarmLiveSchema.optional()
21027
21021
  });
21022
+ var NcAlarmSensorReadingSchema = _enum([
21023
+ "open",
21024
+ "closed",
21025
+ "unknown",
21026
+ "not-opening"
21027
+ ]);
21028
+ /** One device's state as the alarm reads it — the classifier the arm check uses. */
21029
+ var NcAlarmSensorStateSchema = object({
21030
+ deviceId: number().int().positive(),
21031
+ reading: NcAlarmSensorReadingSchema,
21032
+ /** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
21033
+ state: string().nullable(),
21034
+ /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
21035
+ unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
21036
+ /** The device manager's liveness as the centre mirrors it; null = not known. */
21037
+ online: boolean().nullable()
21038
+ });
21039
+ var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
21040
+ var NcAlarmSensorsSchema = object({
21041
+ sensors: array(NcAlarmSensorStateSchema),
21042
+ readAt: number().int().nonnegative()
21043
+ });
21028
21044
  /**
21029
21045
  * ONE rule's demand on ONE camera's clip ring.
21030
21046
  *
@@ -21115,7 +21131,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
21115
21131
  }), method(object({}), NcAlarmConfigSchema, { auth: "admin" }), method(object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
21116
21132
  kind: "mutation",
21117
21133
  auth: "admin"
21118
- });
21134
+ }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
21119
21135
  /**
21120
21136
  * Zod schemas for persisted record types.
21121
21137
  *
@@ -21462,12 +21478,26 @@ var CameraSensorRefSchema = object({
21462
21478
  function cameraSensorRefKey(ref) {
21463
21479
  return `${ref.addonId}/${ref.stableId}`;
21464
21480
  }
21465
- /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
21466
- var CameraSensorsModeSchema = _enum(["in-addition"]);
21467
- var CameraSensorsConfigSchema = object({
21468
- mode: CameraSensorsModeSchema,
21469
- sensors: array(CameraSensorRefSchema).min(1).max(8)
21470
- }).superRefine((value, ctx) => {
21481
+ /**
21482
+ * A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
21483
+ * `replace`: its motion is the camera's, and while one is READY the camera's
21484
+ * own motion no longer counts on its own. `confirm`: while one is READY the
21485
+ * camera's own motion counts only while a confirm sensor is active — with or
21486
+ * without a replace sensor (spec C2, the controller's ruling on Q1).
21487
+ */
21488
+ var CameraSensorModeSchema = _enum([
21489
+ "in-addition",
21490
+ "confirm",
21491
+ "replace"
21492
+ ]);
21493
+ /** A stored sensor with no mode — every CS-1 row — is in addition. */
21494
+ var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
21495
+ /**
21496
+ * A camera's attached sensors. A CS-1 row carried an envelope
21497
+ * `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
21498
+ * what the write stores (D672 rule 3), so it is never written again.
21499
+ */
21500
+ var CameraSensorsConfigSchema = object({ sensors: array(CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) })).min(1).max(8) }).superRefine((value, ctx) => {
21471
21501
  const seen = /* @__PURE__ */ new Set();
21472
21502
  value.sensors.forEach((ref, index) => {
21473
21503
  const key = cameraSensorRefKey(ref);
@@ -21479,39 +21509,41 @@ var CameraSensorsConfigSchema = object({
21479
21509
  seen.add(key);
21480
21510
  });
21481
21511
  });
21482
- var RefFields = {
21512
+ /** Every attachment names its mode; an older orchestrator that does not is in addition. */
21513
+ var AttachmentFields = {
21483
21514
  addonId: string(),
21484
- stableId: string()
21515
+ stableId: string(),
21516
+ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
21485
21517
  };
21486
21518
  var CameraSensorAttachmentSchema = discriminatedUnion("state", [
21487
21519
  object({
21488
- ...RefFields,
21520
+ ...AttachmentFields,
21489
21521
  state: literal("ready"),
21490
21522
  deviceId: number(),
21491
21523
  name: string(),
21492
21524
  caps: array(string()).readonly()
21493
21525
  }),
21494
21526
  object({
21495
- ...RefFields,
21527
+ ...AttachmentFields,
21496
21528
  state: literal("offline"),
21497
21529
  deviceId: number(),
21498
21530
  name: string(),
21499
21531
  reason: _enum(["offline", "disabled"])
21500
21532
  }),
21501
21533
  object({
21502
- ...RefFields,
21534
+ ...AttachmentFields,
21503
21535
  state: literal("missing"),
21504
21536
  reason: string()
21505
21537
  }),
21506
21538
  object({
21507
- ...RefFields,
21539
+ ...AttachmentFields,
21508
21540
  state: literal("unsupported"),
21509
21541
  deviceId: number(),
21510
21542
  name: string(),
21511
21543
  reason: string()
21512
21544
  }),
21513
21545
  object({
21514
- ...RefFields,
21546
+ ...AttachmentFields,
21515
21547
  state: literal("unreadable"),
21516
21548
  reason: string()
21517
21549
  })
@@ -21524,17 +21556,49 @@ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
21524
21556
  * say.
21525
21557
  */
21526
21558
  var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
21559
+ /** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
21560
+ var CameraOwnMotionSchema = _enum([
21561
+ "always",
21562
+ "when-confirmed",
21563
+ "never"
21564
+ ]);
21565
+ /** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
21566
+ var CameraSensorsNotEnforcedSchema = _enum(["provider-not-split"]);
21567
+ /**
21568
+ * The camera's OWN onboard motion (D696): the raw input the bridge combines,
21569
+ * never the effective value. The camera's `motion` slice is the effective one,
21570
+ * so a view of the camera's own row reads this.
21571
+ * - `atMs`: when the bridge received the camera's last own edge.
21572
+ * - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
21573
+ * AND it is younger than the window the bridge counts the camera's own motion
21574
+ * for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
21575
+ * sends a clear (heartbeats only) therefore reads `false` once no push came
21576
+ * within the window; a status read is a snapshot, so a live view re-asks.
21577
+ * Absent = this orchestrator has not received the camera's own motion since it
21578
+ * started (unknown, never "quiet"), or an orchestrator older than D696.
21579
+ */
21580
+ var CameraRawLevelSchema = object({
21581
+ detected: boolean(),
21582
+ atMs: number()
21583
+ });
21527
21584
  var CameraSensorsStatusSchema = discriminatedUnion("state", [
21528
- object({ state: literal("not-configured") }),
21585
+ object({
21586
+ state: literal("not-configured"),
21587
+ cameraLevel: CameraRawLevelSchema.optional()
21588
+ }),
21529
21589
  object({
21530
21590
  state: literal("unreadable"),
21531
- reason: string()
21591
+ reason: string(),
21592
+ cameraLevel: CameraRawLevelSchema.optional()
21532
21593
  }),
21533
21594
  object({
21534
21595
  state: literal("configured"),
21535
- mode: CameraSensorsModeSchema,
21596
+ cameraLevel: CameraRawLevelSchema.optional(),
21536
21597
  sensors: array(CameraSensorAttachmentSchema).readonly(),
21537
- notForwarded: CameraSensorsNotForwardedSchema.optional()
21598
+ /** Absent = an orchestrator older than CS-2: it does not say (D315). */
21599
+ cameraMotion: CameraOwnMotionSchema.optional(),
21600
+ notForwarded: CameraSensorsNotForwardedSchema.optional(),
21601
+ notEnforced: CameraSensorsNotEnforcedSchema.optional()
21538
21602
  })
21539
21603
  ]);
21540
21604
  Object.freeze({
@@ -22219,14 +22283,14 @@ var TrackSchema = object({
22219
22283
  * `derive-returned-null tile=standin`,
22220
22284
  * `native-tile tile=native no-key-frame-at-close`, …) written at close and
22221
22285
  * CLEARED by the late-keyFrame upgrade when a native tile lands after all.
22222
- * The operator-facing answer to "perché manca l'immagine?" on a track whose
22286
+ * The operator-facing answer to "perché manca l'immagine?" [why is the image missing?] on a track whose
22223
22287
  * tile is a face/plate stand-in, a raster crop, or an icon.
22224
22288
  *
22225
22289
  * **`no-key-frame-at-close` (D400).** The line used to be written ONLY when
22226
22290
  * the TILE ranked below native, so a track whose thumbnail landed natively
22227
22291
  * while its key frame missed carried no reason at all — three of the four
22228
22292
  * `MISSING_KEYFRAME` tracks in the 2026-09-07 operator audit were exactly
22229
- * that shape, and their "niente ultimo" had no cause anywhere on the row.
22293
+ * that shape, and their "niente ultimo" [no last frame] had no cause anywhere on the row.
22230
22294
  * The key frame is the head of a cascade (`keyFrame ⇒ keyFrameSmall ⇒ the
22231
22295
  * lastFrame copy source`), so its absence is now on this line whatever the
22232
22296
  * tile ended up being.
@@ -22313,7 +22377,7 @@ var MotionEventSchema = object({
22313
22377
  /**
22314
22378
  * Ms offsets from `timestamp` (the episode's own first rising edge, so the
22315
22379
  * first entry is always `0`) of every genuine off→on transition the
22316
- * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
22380
+ * episode saw — "ogni evento on si deve salvare" [every ON event must be saved] (D475). NOT one entry per
22317
22381
  * push: a firmware source that keepalives at ~1 Hz for the whole burst
22318
22382
  * (Reolink, Hikvision) produces exactly one edge; a source that reports an
22319
22383
  * explicit `false` mid-episode and then resumes before the quiet window
@@ -22837,6 +22901,7 @@ var KeyEventSchema = object({
22837
22901
  bestEventId: string(),
22838
22902
  /** Track lifetime in ms (lastSeen - firstSeen). */
22839
22903
  windowMs: number().optional(),
22904
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
22840
22905
  ...TrackFlagFields,
22841
22906
  ...TrackRetrainFields
22842
22907
  });
@@ -24725,8 +24790,10 @@ var RunnerCameraConfigSchema = object({
24725
24790
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
24726
24791
  * analyzer providers) running on decoded frames in this runner.
24727
24792
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
24728
- * analytics). The provider emits ProviderMotion
24729
- * events; the orchestrator forwards to `reportMotion`.
24793
+ * analytics). The provider emits its raw push as `ProviderMotion`
24794
+ * (D673); the orchestrator's camera-sensors bridge re-emits the camera's
24795
+ * effective motion as `MotionOnMotionChanged`, which is what reaches
24796
+ * `reportMotion`.
24730
24797
  */
24731
24798
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24732
24799
  /**
@@ -24903,7 +24970,15 @@ var RunnerLocalLoadSchema = object({
24903
24970
  hasGpu: boolean(),
24904
24971
  inferenceBackend: string().optional(),
24905
24972
  cpuCores: number().optional()
24906
- })
24973
+ }),
24974
+ /**
24975
+ * Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
24976
+ * runner never writes it. ABSENT = a runner older than D696, which still
24977
+ * writes the slice from its phase machine; the orchestrator does not write
24978
+ * while any runner node is absent-or-unanswered, so one slice never has two
24979
+ * writers.
24980
+ */
24981
+ motionSliceWriter: literal("orchestrator").optional()
24907
24982
  });
24908
24983
  /**
24909
24984
  * Aggregate runtime metrics for the runner's whole local pool. Mirrors the
@@ -27539,7 +27614,7 @@ var ClipPlaybackSchema = object({
27539
27614
  * **One authorised yes is one wake.** `next-natural` — take the clip the next
27540
27615
  * time the camera is awake for its own reasons — is deliberately not a member:
27541
27616
  * it was proposed, it is free on the battery, and the operator declined it on
27542
- * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
27617
+ * 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."* [When an export is requested the camera wakes up],
27543
27618
  * D558 "Considered and not taken").
27544
27619
  */
27545
27620
  var ClipWakeSchema = _enum(["authorised"]);
@@ -27806,7 +27881,7 @@ var videoclipsCapability = {
27806
27881
  * **It is a SECTION of the Recording tab, at the end of it — not a tab of
27807
27882
  * its own.** It shipped as a `clips` top-tab and the operator rejected the
27808
27883
  * placement: *"utilizzerei la stessa tab recordings, lì abbiamo già tutto il
27809
- * necessario, una nuova sezione alla fine per le clips"*. The Recording tab
27884
+ * necessario, una nuova sezione alla fine per le clips"* [I would use the same recordings tab, there we already have everything necessary, a new section at the end for the clips]. The Recording tab
27810
27885
  * already holds the recorder's panel, the schedule bands and the unified
27811
27886
  * retention policy; footage the camera itself holds is the same question,
27812
27887
  * asked of a different store. `order: 100` puts it after all of them with
@@ -27867,7 +27942,7 @@ var videoclipsCapability = {
27867
27942
  * It replaced `sources?: string[]`, a VIEW filter over source ids that
27868
27943
  * assumed the answer was a fan-out over everything a camera has. The
27869
27944
  * operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
27870
- * solo"* — so the list is asked of one provider at a time and there is
27945
+ * solo"* [there is only one user] — so the list is asked of one provider at a time and there is
27871
27946
  * nothing to filter out of it.
27872
27947
  */
27873
27948
  provider: string().min(1)
@@ -29957,6 +30032,17 @@ object({
29957
30032
  deviceId: number(),
29958
30033
  status: DeviceStatusSchema
29959
30034
  });
30035
+ object({
30036
+ /** Ms epoch of the last press. null = never observed since this provider started. */
30037
+ lastPressedAt: number().nullable(),
30038
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
30039
+ pressCountSinceStart: number()
30040
+ });
30041
+ object({
30042
+ deviceId: number(),
30043
+ timestamp: number()
30044
+ });
30045
+ DeviceType.Button, DeviceType.Camera;
29960
30046
  /**
29961
30047
  * Enum-state sensor — a string value picked from a finite option set.
29962
30048
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -31617,9 +31703,9 @@ var MotionStatusSchema = object({
31617
31703
  /** Ms epoch of the last detected-true observation. Null if never detected. */
31618
31704
  lastDetectedAt: number().nullable(),
31619
31705
  /**
31620
- * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
31621
- * `null` while false and on every `Sensor` device (D475) — see that
31622
- * constant's doc for the one-authority rule.
31706
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
31707
+ * orchestrator writes it, D696), `null` while false and on every `Sensor`
31708
+ * device (D475) — see that constant's doc for the one-authority rule.
31623
31709
  *
31624
31710
  * ## Reading this field still arms nothing
31625
31711
  *
@@ -31637,38 +31723,41 @@ var MotionStatusSchema = object({
31637
31723
  * `addon-export-alexa/src/motion-clear-hold.ts` and
31638
31724
  * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
31639
31725
  *
31640
- * ## Who writes it
31641
- *
31642
- * - **Cameras** — the runner's phase machine, `active → watching` on
31643
- * `cooldown_expired`, which then writes this slice with
31644
- * `detected: false` (`handlePhaseChanged` in
31645
- * `pipeline-runner/index.ts`). It produces the FALLING edge, which
31646
- * matters most for the sources that only ever push a rising one:
31647
- * Reolink emits `MotionOnMotionChanged { detected: true }` and never
31648
- * a false.
31649
- * - **Sensors** (Home Assistant binary sensors, Homematic) — the
31650
- * provider pushes the false itself, from the upstream system's own
31651
- * state change. No phase machine is involved.
31652
- *
31653
- * ### The phase machine is CANONICAL, not sole — and that is a defect
31654
- *
31655
- * An earlier revision of this docblock (mine, 2026-09-12) claimed the
31656
- * phase machine is the sole writer for a camera. It is not.
31657
- * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
31658
- * `setCapSlice(motionCapability, …)` on their own rising edge, and
31659
- * Hikvision's comment says why: it read THIS docblock, agreed the
31660
- * runner is canonical, and wrote anyway to avoid per-tick churn. So
31661
- * two authorities can disagree about one slice, which this repo
31662
- * forbids, and the doc said otherwise — which is worse than saying
31663
- * nothing, because it reads as verification.
31664
- *
31665
- * This predates D475 and is not fixed there: the fix touches every
31666
- * camera provider. Recorded in D475's Consequences. Do not restore the
31667
- * "sole writer" wording without also removing the other writers.
31726
+ * ## Who writes it (D696)
31727
+ *
31728
+ * - **Cameras** — the orchestrator, and nothing else. It writes the camera's
31729
+ * EFFECTIVE motion (`addon-pipeline-orchestrator`
31730
+ * `camera-sensors/motion-slice.ts`), alongside the effective
31731
+ * `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
31732
+ * for a split camera, an unsplit emitter's direct event, the analyzer and
31733
+ * device activity, as the camera's `motionSources` counts them. A source
31734
+ * that never sends a clear falls one window after its last `true`.
31735
+ * `lastDetectedAt` moves only on an effective rise. No camera provider
31736
+ * writes this slice (not even a boot seed), and neither does the runner's
31737
+ * phase machine: its phase is "a session is attached" for every on-motion
31738
+ * session, not motion, and it now has its own `DetectionPhaseTransition`.
31739
+ * A provider's `isDetected` answers from the hub's slice.
31740
+ * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
31741
+ * imports) — the provider pushes both edges itself, from the upstream
31742
+ * system's own state change. That is the sensor's own fact.
31743
+ *
31744
+ * `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
31745
+ * `motion` slice; each sensor writer is a named exemption.
31746
+ *
31747
+ * This replaces the D475 §4 rule ("the phase machine is the sole writer").
31748
+ * That rule was never true: Hikvision and Amcrest wrote the slice raw beside
31749
+ * it, every provider seeded it at boot, and every session left it
31750
+ * `detected: true` after its detach.
31668
31751
  */
31669
31752
  autoClearAfterMs: number().nullable()
31670
31753
  });
31671
- object({
31754
+ /**
31755
+ * Payload of `motion.onMotionChanged` event + the corresponding bus
31756
+ * event `EventCategory.MotionOnMotionChanged`. Single source of truth
31757
+ * — both the cap event surface and the bus payload type alias to this
31758
+ * schema.
31759
+ */
31760
+ var MotionOnMotionChangedDataSchema = object({
31672
31761
  deviceId: number(),
31673
31762
  detected: boolean(),
31674
31763
  timestamp: number(),
@@ -34133,7 +34222,7 @@ var ReadGopBytesResultSchema = object({
34133
34222
  /**
34134
34223
  * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
34135
34224
  * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
34136
- * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
34225
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-clip-replay-on-pipeline.md`):
34137
34226
  * a replay needs several seconds of native pixels, not one frame.
34138
34227
  *
34139
34228
  * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
@@ -36774,6 +36863,7 @@ DeviceType.Camera, method(object({
36774
36863
  detection: array(ZoneRuleSchema).readonly(),
36775
36864
  package: array(ZoneRuleSchema).readonly()
36776
36865
  });
36866
+ SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
36777
36867
  /**
36778
36868
  * Accessory device helpers — shared across drivers.
36779
36869
  *
@@ -41752,6 +41842,12 @@ Object.freeze({
41752
41842
  addonId: null,
41753
41843
  access: "view"
41754
41844
  },
41845
+ "notificationRules.getAlarmSensors": {
41846
+ capName: "notification-rules",
41847
+ capScope: "system",
41848
+ addonId: null,
41849
+ access: "view"
41850
+ },
41755
41851
  "notificationRules.getClipRetentionAsks": {
41756
41852
  capName: "notification-rules",
41757
41853
  capScope: "system",
@@ -45330,6 +45426,11 @@ Object.freeze({
45330
45426
  }
45331
45427
  });
45332
45428
  Object.freeze({
45429
+ "accessories.getStatus": [{
45430
+ name: "deviceId",
45431
+ form: "single",
45432
+ optional: false
45433
+ }],
45333
45434
  "accessories.setChildHidden": [{
45334
45435
  name: "childDeviceId",
45335
45436
  form: "single",
@@ -45349,6 +45450,11 @@ Object.freeze({
45349
45450
  form: "single",
45350
45451
  optional: false
45351
45452
  }],
45453
+ "airQualitySensor.getStatus": [{
45454
+ name: "deviceId",
45455
+ form: "single",
45456
+ optional: false
45457
+ }],
45352
45458
  "alarmPanel.arm": [{
45353
45459
  name: "deviceId",
45354
45460
  form: "single",
@@ -45359,11 +45465,36 @@ Object.freeze({
45359
45465
  form: "single",
45360
45466
  optional: false
45361
45467
  }],
45468
+ "alarmPanel.getStatus": [{
45469
+ name: "deviceId",
45470
+ form: "single",
45471
+ optional: false
45472
+ }],
45362
45473
  "alarmPanel.trigger": [{
45363
45474
  name: "deviceId",
45364
45475
  form: "single",
45365
45476
  optional: false
45366
45477
  }],
45478
+ "ambientLightSensor.getStatus": [{
45479
+ name: "deviceId",
45480
+ form: "single",
45481
+ optional: false
45482
+ }],
45483
+ "audioAnalysis.applyDeviceSettingsPatch": [{
45484
+ name: "deviceId",
45485
+ form: "single",
45486
+ optional: false
45487
+ }],
45488
+ "audioAnalysis.getDeviceLiveContribution": [{
45489
+ name: "deviceId",
45490
+ form: "single",
45491
+ optional: false
45492
+ }],
45493
+ "audioAnalysis.getDeviceSettingsContribution": [{
45494
+ name: "deviceId",
45495
+ form: "single",
45496
+ optional: false
45497
+ }],
45367
45498
  "audioAnalysis.resolveDeviceSettings": [{
45368
45499
  name: "deviceId",
45369
45500
  form: "single",
@@ -45404,16 +45535,36 @@ Object.freeze({
45404
45535
  form: "single",
45405
45536
  optional: false
45406
45537
  }],
45538
+ "automationControl.getStatus": [{
45539
+ name: "deviceId",
45540
+ form: "single",
45541
+ optional: false
45542
+ }],
45407
45543
  "automationControl.trigger": [{
45408
45544
  name: "deviceId",
45409
45545
  form: "single",
45410
45546
  optional: false
45411
45547
  }],
45548
+ "battery.getStatus": [{
45549
+ name: "deviceId",
45550
+ form: "single",
45551
+ optional: false
45552
+ }],
45412
45553
  "battery.wakeForStream": [{
45413
45554
  name: "deviceId",
45414
45555
  form: "single",
45415
45556
  optional: false
45416
45557
  }],
45558
+ "binary.getStatus": [{
45559
+ name: "deviceId",
45560
+ form: "single",
45561
+ optional: false
45562
+ }],
45563
+ "brightness.getStatus": [{
45564
+ name: "deviceId",
45565
+ form: "single",
45566
+ optional: false
45567
+ }],
45417
45568
  "brightness.setBrightness": [{
45418
45569
  name: "deviceId",
45419
45570
  form: "single",
@@ -45429,6 +45580,26 @@ Object.freeze({
45429
45580
  form: "single",
45430
45581
  optional: false
45431
45582
  }],
45583
+ "cameraCredentials.getStatus": [{
45584
+ name: "deviceId",
45585
+ form: "single",
45586
+ optional: false
45587
+ }],
45588
+ "cameraGridLayout.applyDeviceSettingsPatch": [{
45589
+ name: "deviceId",
45590
+ form: "single",
45591
+ optional: false
45592
+ }],
45593
+ "cameraGridLayout.getDeviceLiveContribution": [{
45594
+ name: "deviceId",
45595
+ form: "single",
45596
+ optional: false
45597
+ }],
45598
+ "cameraGridLayout.getDeviceSettingsContribution": [{
45599
+ name: "deviceId",
45600
+ form: "single",
45601
+ optional: false
45602
+ }],
45432
45603
  "cameraGridLayout.getLayout": [{
45433
45604
  name: "deviceId",
45434
45605
  form: "single",
@@ -45444,6 +45615,21 @@ Object.freeze({
45444
45615
  form: "single",
45445
45616
  optional: false
45446
45617
  }],
45618
+ "cameraPipelineConfig.applyDeviceSettingsPatch": [{
45619
+ name: "deviceId",
45620
+ form: "single",
45621
+ optional: false
45622
+ }],
45623
+ "cameraPipelineConfig.getDeviceLiveContribution": [{
45624
+ name: "deviceId",
45625
+ form: "single",
45626
+ optional: false
45627
+ }],
45628
+ "cameraPipelineConfig.getDeviceSettingsContribution": [{
45629
+ name: "deviceId",
45630
+ form: "single",
45631
+ optional: false
45632
+ }],
45447
45633
  "cameraStreams.getBrokerStreams": [{
45448
45634
  name: "deviceId",
45449
45635
  form: "single",
@@ -45469,6 +45655,16 @@ Object.freeze({
45469
45655
  form: "single",
45470
45656
  optional: false
45471
45657
  }],
45658
+ "carbonMonoxide.getStatus": [{
45659
+ name: "deviceId",
45660
+ form: "single",
45661
+ optional: false
45662
+ }],
45663
+ "climateControl.getStatus": [{
45664
+ name: "deviceId",
45665
+ form: "single",
45666
+ optional: false
45667
+ }],
45472
45668
  "climateControl.setFanMode": [{
45473
45669
  name: "deviceId",
45474
45670
  form: "single",
@@ -45509,16 +45705,41 @@ Object.freeze({
45509
45705
  form: "single",
45510
45706
  optional: false
45511
45707
  }],
45708
+ "color.getStatus": [{
45709
+ name: "deviceId",
45710
+ form: "single",
45711
+ optional: false
45712
+ }],
45512
45713
  "color.setColor": [{
45513
45714
  name: "deviceId",
45514
45715
  form: "single",
45515
45716
  optional: false
45516
45717
  }],
45718
+ "connectivity.getStatus": [{
45719
+ name: "deviceId",
45720
+ form: "single",
45721
+ optional: false
45722
+ }],
45723
+ "consumables.getStatus": [{
45724
+ name: "deviceId",
45725
+ form: "single",
45726
+ optional: false
45727
+ }],
45517
45728
  "consumables.reset": [{
45518
45729
  name: "deviceId",
45519
45730
  form: "single",
45520
45731
  optional: false
45521
45732
  }],
45733
+ "contact.getStatus": [{
45734
+ name: "deviceId",
45735
+ form: "single",
45736
+ optional: false
45737
+ }],
45738
+ "control.getStatus": [{
45739
+ name: "deviceId",
45740
+ form: "single",
45741
+ optional: false
45742
+ }],
45522
45743
  "control.setValue": [{
45523
45744
  name: "deviceId",
45524
45745
  form: "single",
@@ -45529,6 +45750,11 @@ Object.freeze({
45529
45750
  form: "single",
45530
45751
  optional: false
45531
45752
  }],
45753
+ "cover.getStatus": [{
45754
+ name: "deviceId",
45755
+ form: "single",
45756
+ optional: false
45757
+ }],
45532
45758
  "cover.open": [{
45533
45759
  name: "deviceId",
45534
45760
  form: "single",
@@ -45554,6 +45780,11 @@ Object.freeze({
45554
45780
  form: "single",
45555
45781
  optional: false
45556
45782
  }],
45783
+ "dayNight.getStatus": [{
45784
+ name: "deviceId",
45785
+ form: "single",
45786
+ optional: false
45787
+ }],
45557
45788
  "dayNight.setSettings": [{
45558
45789
  name: "deviceId",
45559
45790
  form: "single",
@@ -45564,11 +45795,31 @@ Object.freeze({
45564
45795
  form: "single",
45565
45796
  optional: true
45566
45797
  }],
45798
+ "detectionPipeline.applyDeviceSettingsPatch": [{
45799
+ name: "deviceId",
45800
+ form: "single",
45801
+ optional: false
45802
+ }],
45803
+ "detectionPipeline.getDeviceLiveContribution": [{
45804
+ name: "deviceId",
45805
+ form: "single",
45806
+ optional: false
45807
+ }],
45808
+ "detectionPipeline.getDeviceSettingsContribution": [{
45809
+ name: "deviceId",
45810
+ form: "single",
45811
+ optional: false
45812
+ }],
45567
45813
  "deviceAdminLink.getAdminLink": [{
45568
45814
  name: "deviceId",
45569
45815
  form: "single",
45570
45816
  optional: false
45571
45817
  }],
45818
+ "deviceAdoption.getStatus": [{
45819
+ name: "deviceId",
45820
+ form: "single",
45821
+ optional: false
45822
+ }],
45572
45823
  "deviceAdoption.release": [{
45573
45824
  name: "camDeviceId",
45574
45825
  form: "single",
@@ -45584,6 +45835,11 @@ Object.freeze({
45584
45835
  form: "single",
45585
45836
  optional: false
45586
45837
  }],
45838
+ "deviceDiscovery.getStatus": [{
45839
+ name: "deviceId",
45840
+ form: "single",
45841
+ optional: false
45842
+ }],
45587
45843
  "deviceDiscovery.listDiscovered": [{
45588
45844
  name: "deviceId",
45589
45845
  form: "single",
@@ -45603,6 +45859,21 @@ Object.freeze({
45603
45859
  form: "single",
45604
45860
  optional: false
45605
45861
  }],
45862
+ "deviceExport.applyDeviceSettingsPatch": [{
45863
+ name: "deviceId",
45864
+ form: "single",
45865
+ optional: false
45866
+ }],
45867
+ "deviceExport.getDeviceLiveContribution": [{
45868
+ name: "deviceId",
45869
+ form: "single",
45870
+ optional: false
45871
+ }],
45872
+ "deviceExport.getDeviceSettingsContribution": [{
45873
+ name: "deviceId",
45874
+ form: "single",
45875
+ optional: false
45876
+ }],
45606
45877
  "deviceExtension.listCandidates": [{
45607
45878
  name: "deviceId",
45608
45879
  form: "single",
@@ -45951,6 +46222,26 @@ Object.freeze({
45951
46222
  form: "single",
45952
46223
  optional: false
45953
46224
  }],
46225
+ "deviceStatus.getStatus": [{
46226
+ name: "deviceId",
46227
+ form: "single",
46228
+ optional: false
46229
+ }],
46230
+ "doorbell.getStatus": [{
46231
+ name: "deviceId",
46232
+ form: "single",
46233
+ optional: false
46234
+ }],
46235
+ "enumSensor.getStatus": [{
46236
+ name: "deviceId",
46237
+ form: "single",
46238
+ optional: false
46239
+ }],
46240
+ "eventEmitter.getStatus": [{
46241
+ name: "deviceId",
46242
+ form: "single",
46243
+ optional: false
46244
+ }],
45954
46245
  "faceGallery.getFaceByTrack": [{
45955
46246
  name: "deviceId",
45956
46247
  form: "single",
@@ -45965,6 +46256,11 @@ Object.freeze({
45965
46256
  form: "array",
45966
46257
  optional: true
45967
46258
  }],
46259
+ "fanControl.getStatus": [{
46260
+ name: "deviceId",
46261
+ form: "single",
46262
+ optional: false
46263
+ }],
45968
46264
  "fanControl.setDirection": [{
45969
46265
  name: "deviceId",
45970
46266
  form: "single",
@@ -45985,6 +46281,26 @@ Object.freeze({
45985
46281
  form: "single",
45986
46282
  optional: false
45987
46283
  }],
46284
+ "featureProbe.getStatus": [{
46285
+ name: "deviceId",
46286
+ form: "single",
46287
+ optional: false
46288
+ }],
46289
+ "flood.getStatus": [{
46290
+ name: "deviceId",
46291
+ form: "single",
46292
+ optional: false
46293
+ }],
46294
+ "gas.getStatus": [{
46295
+ name: "deviceId",
46296
+ form: "single",
46297
+ optional: false
46298
+ }],
46299
+ "humidifier.getStatus": [{
46300
+ name: "deviceId",
46301
+ form: "single",
46302
+ optional: false
46303
+ }],
45988
46304
  "humidifier.setMode": [{
45989
46305
  name: "deviceId",
45990
46306
  form: "single",
@@ -46000,11 +46316,26 @@ Object.freeze({
46000
46316
  form: "single",
46001
46317
  optional: false
46002
46318
  }],
46319
+ "humiditySensor.getStatus": [{
46320
+ name: "deviceId",
46321
+ form: "single",
46322
+ optional: false
46323
+ }],
46324
+ "image.getStatus": [{
46325
+ name: "deviceId",
46326
+ form: "single",
46327
+ optional: false
46328
+ }],
46003
46329
  "imageSettings.getOptions": [{
46004
46330
  name: "deviceId",
46005
46331
  form: "single",
46006
46332
  optional: false
46007
46333
  }],
46334
+ "imageSettings.getStatus": [{
46335
+ name: "deviceId",
46336
+ form: "single",
46337
+ optional: false
46338
+ }],
46008
46339
  "imageSettings.setSettings": [{
46009
46340
  name: "deviceId",
46010
46341
  form: "single",
@@ -46015,6 +46346,11 @@ Object.freeze({
46015
46346
  form: "single",
46016
46347
  optional: false
46017
46348
  }],
46349
+ "intercom.getStatus": [{
46350
+ name: "deviceId",
46351
+ form: "single",
46352
+ optional: false
46353
+ }],
46018
46354
  "intercom.handleAnswer": [{
46019
46355
  name: "deviceId",
46020
46356
  form: "single",
@@ -46045,6 +46381,11 @@ Object.freeze({
46045
46381
  form: "single",
46046
46382
  optional: false
46047
46383
  }],
46384
+ "lawnMowerControl.getStatus": [{
46385
+ name: "deviceId",
46386
+ form: "single",
46387
+ optional: false
46388
+ }],
46048
46389
  "lawnMowerControl.pause": [{
46049
46390
  name: "deviceId",
46050
46391
  form: "single",
@@ -46055,6 +46396,11 @@ Object.freeze({
46055
46396
  form: "single",
46056
46397
  optional: false
46057
46398
  }],
46399
+ "lockControl.getStatus": [{
46400
+ name: "deviceId",
46401
+ form: "single",
46402
+ optional: false
46403
+ }],
46058
46404
  "lockControl.lock": [{
46059
46405
  name: "deviceId",
46060
46406
  form: "single",
@@ -46070,6 +46416,11 @@ Object.freeze({
46070
46416
  form: "single",
46071
46417
  optional: false
46072
46418
  }],
46419
+ "mediaPlayer.getStatus": [{
46420
+ name: "deviceId",
46421
+ form: "single",
46422
+ optional: false
46423
+ }],
46073
46424
  "mediaPlayer.next": [{
46074
46425
  name: "deviceId",
46075
46426
  form: "single",
@@ -46130,6 +46481,11 @@ Object.freeze({
46130
46481
  form: "single",
46131
46482
  optional: false
46132
46483
  }],
46484
+ "motion.getStatus": [{
46485
+ name: "deviceId",
46486
+ form: "single",
46487
+ optional: false
46488
+ }],
46133
46489
  "motion.isDetected": [{
46134
46490
  name: "deviceId",
46135
46491
  form: "single",
@@ -46140,11 +46496,31 @@ Object.freeze({
46140
46496
  form: "single",
46141
46497
  optional: false
46142
46498
  }],
46499
+ "motionDetection.applyDeviceSettingsPatch": [{
46500
+ name: "deviceId",
46501
+ form: "single",
46502
+ optional: false
46503
+ }],
46504
+ "motionDetection.getDeviceLiveContribution": [{
46505
+ name: "deviceId",
46506
+ form: "single",
46507
+ optional: false
46508
+ }],
46509
+ "motionDetection.getDeviceSettingsContribution": [{
46510
+ name: "deviceId",
46511
+ form: "single",
46512
+ optional: false
46513
+ }],
46143
46514
  "motionDetection.removeCamera": [{
46144
46515
  name: "deviceId",
46145
46516
  form: "single",
46146
46517
  optional: false
46147
46518
  }],
46519
+ "motionTrigger.getStatus": [{
46520
+ name: "deviceId",
46521
+ form: "single",
46522
+ optional: false
46523
+ }],
46148
46524
  "motionTrigger.setMotionTrigger": [{
46149
46525
  name: "deviceId",
46150
46526
  form: "single",
@@ -46155,6 +46531,11 @@ Object.freeze({
46155
46531
  form: "single",
46156
46532
  optional: false
46157
46533
  }],
46534
+ "motionZones.getStatus": [{
46535
+ name: "deviceId",
46536
+ form: "single",
46537
+ optional: false
46538
+ }],
46158
46539
  "motionZones.setZone": [{
46159
46540
  name: "deviceId",
46160
46541
  form: "single",
@@ -46165,6 +46546,11 @@ Object.freeze({
46165
46546
  form: "single",
46166
46547
  optional: false
46167
46548
  }],
46549
+ "nativeObjectDetection.getStatus": [{
46550
+ name: "deviceId",
46551
+ form: "single",
46552
+ optional: false
46553
+ }],
46168
46554
  "nativeObjectDetection.setEnabled": [{
46169
46555
  name: "deviceId",
46170
46556
  form: "single",
@@ -46180,6 +46566,11 @@ Object.freeze({
46180
46566
  form: "single",
46181
46567
  optional: false
46182
46568
  }],
46569
+ "navigation.getStatus": [{
46570
+ name: "deviceId",
46571
+ form: "single",
46572
+ optional: false
46573
+ }],
46183
46574
  "navigation.goToPoint": [{
46184
46575
  name: "deviceId",
46185
46576
  form: "single",
@@ -46225,6 +46616,11 @@ Object.freeze({
46225
46616
  form: "single",
46226
46617
  optional: false
46227
46618
  }],
46619
+ "networkLink.getStatus": [{
46620
+ name: "deviceId",
46621
+ form: "single",
46622
+ optional: false
46623
+ }],
46228
46624
  "networkQuality.getDeviceStats": [{
46229
46625
  name: "deviceId",
46230
46626
  form: "single",
@@ -46235,6 +46631,11 @@ Object.freeze({
46235
46631
  form: "single",
46236
46632
  optional: false
46237
46633
  }],
46634
+ "notificationRules.getAlarmSensors": [{
46635
+ name: "deviceIds",
46636
+ form: "array",
46637
+ optional: false
46638
+ }],
46238
46639
  "notificationRules.setDeviceMuted": [{
46239
46640
  name: "deviceId",
46240
46641
  form: "single",
@@ -46245,11 +46646,26 @@ Object.freeze({
46245
46646
  form: "single",
46246
46647
  optional: false
46247
46648
  }],
46649
+ "notifier.getStatus": [{
46650
+ name: "deviceId",
46651
+ form: "single",
46652
+ optional: false
46653
+ }],
46248
46654
  "notifier.send": [{
46249
46655
  name: "deviceId",
46250
46656
  form: "single",
46251
46657
  optional: false
46252
46658
  }],
46659
+ "numericSensor.getStatus": [{
46660
+ name: "deviceId",
46661
+ form: "single",
46662
+ optional: false
46663
+ }],
46664
+ "osd.getStatus": [{
46665
+ name: "deviceId",
46666
+ form: "single",
46667
+ optional: false
46668
+ }],
46253
46669
  "osd.setOverlay": [{
46254
46670
  name: "deviceId",
46255
46671
  form: "single",
@@ -46309,6 +46725,11 @@ Object.freeze({
46309
46725
  form: "single",
46310
46726
  optional: false
46311
46727
  }],
46728
+ "petFeeder.getStatus": [{
46729
+ name: "deviceId",
46730
+ form: "single",
46731
+ optional: false
46732
+ }],
46312
46733
  "petFeeder.markFoodReplenished": [{
46313
46734
  name: "deviceId",
46314
46735
  form: "single",
@@ -46344,6 +46765,11 @@ Object.freeze({
46344
46765
  form: "single",
46345
46766
  optional: false
46346
46767
  }],
46768
+ "pipelineAnalytics.applyDeviceSettingsPatch": [{
46769
+ name: "deviceId",
46770
+ form: "single",
46771
+ optional: false
46772
+ }],
46347
46773
  "pipelineAnalytics.clearTrackRelation": [{
46348
46774
  name: "deviceId",
46349
46775
  form: "single",
@@ -46384,6 +46810,16 @@ Object.freeze({
46384
46810
  form: "single",
46385
46811
  optional: false
46386
46812
  }],
46813
+ "pipelineAnalytics.getDeviceLiveContribution": [{
46814
+ name: "deviceId",
46815
+ form: "single",
46816
+ optional: false
46817
+ }],
46818
+ "pipelineAnalytics.getDeviceSettingsContribution": [{
46819
+ name: "deviceId",
46820
+ form: "single",
46821
+ optional: false
46822
+ }],
46387
46823
  "pipelineAnalytics.getEventDensity": [{
46388
46824
  name: "deviceId",
46389
46825
  form: "single",
@@ -46599,6 +47035,11 @@ Object.freeze({
46599
47035
  form: "single",
46600
47036
  optional: true
46601
47037
  }],
47038
+ "pipelineOrchestrator.applyDeviceSettingsPatch": [{
47039
+ name: "deviceId",
47040
+ form: "single",
47041
+ optional: false
47042
+ }],
46602
47043
  "pipelineOrchestrator.assignAudio": [{
46603
47044
  name: "deviceId",
46604
47045
  form: "single",
@@ -46644,6 +47085,16 @@ Object.freeze({
46644
47085
  form: "single",
46645
47086
  optional: false
46646
47087
  }],
47088
+ "pipelineOrchestrator.getDeviceLiveContribution": [{
47089
+ name: "deviceId",
47090
+ form: "single",
47091
+ optional: false
47092
+ }],
47093
+ "pipelineOrchestrator.getDeviceSettingsContribution": [{
47094
+ name: "deviceId",
47095
+ form: "single",
47096
+ optional: false
47097
+ }],
46647
47098
  "pipelineOrchestrator.getPipelineAssignment": [{
46648
47099
  name: "deviceId",
46649
47100
  form: "single",
@@ -46764,11 +47215,31 @@ Object.freeze({
46764
47215
  form: "single",
46765
47216
  optional: true
46766
47217
  }],
47218
+ "powerMeter.getStatus": [{
47219
+ name: "deviceId",
47220
+ form: "single",
47221
+ optional: false
47222
+ }],
47223
+ "presence.getStatus": [{
47224
+ name: "deviceId",
47225
+ form: "single",
47226
+ optional: false
47227
+ }],
47228
+ "pressureSensor.getStatus": [{
47229
+ name: "deviceId",
47230
+ form: "single",
47231
+ optional: false
47232
+ }],
46767
47233
  "privacyMask.getOptions": [{
46768
47234
  name: "deviceId",
46769
47235
  form: "single",
46770
47236
  optional: false
46771
47237
  }],
47238
+ "privacyMask.getStatus": [{
47239
+ name: "deviceId",
47240
+ form: "single",
47241
+ optional: false
47242
+ }],
46772
47243
  "privacyMask.setAudioEnabled": [{
46773
47244
  name: "deviceId",
46774
47245
  form: "single",
@@ -46819,6 +47290,11 @@ Object.freeze({
46819
47290
  form: "single",
46820
47291
  optional: false
46821
47292
  }],
47293
+ "ptz.getStatus": [{
47294
+ name: "deviceId",
47295
+ form: "single",
47296
+ optional: false
47297
+ }],
46822
47298
  "ptz.goHome": [{
46823
47299
  name: "deviceId",
46824
47300
  form: "single",
@@ -46909,6 +47385,11 @@ Object.freeze({
46909
47385
  form: "single",
46910
47386
  optional: false
46911
47387
  }],
47388
+ "recordingArchive.applyDeviceSettingsPatch": [{
47389
+ name: "deviceId",
47390
+ form: "single",
47391
+ optional: false
47392
+ }],
46912
47393
  "recordingArchive.deleteFootprint": [{
46913
47394
  name: "deviceId",
46914
47395
  form: "single",
@@ -46929,6 +47410,21 @@ Object.freeze({
46929
47410
  form: "single",
46930
47411
  optional: false
46931
47412
  }],
47413
+ "recordingArchive.getDeviceLiveContribution": [{
47414
+ name: "deviceId",
47415
+ form: "single",
47416
+ optional: false
47417
+ }],
47418
+ "recordingArchive.getDeviceSettingsContribution": [{
47419
+ name: "deviceId",
47420
+ form: "single",
47421
+ optional: false
47422
+ }],
47423
+ "recordingArchive.getStatus": [{
47424
+ name: "deviceId",
47425
+ form: "single",
47426
+ optional: false
47427
+ }],
46932
47428
  "recordingArchive.listOpsLog": [{
46933
47429
  name: "deviceId",
46934
47430
  form: "single",
@@ -47014,6 +47510,11 @@ Object.freeze({
47014
47510
  form: "single",
47015
47511
  optional: false
47016
47512
  }],
47513
+ "recordingOnboard.getStatus": [{
47514
+ name: "deviceId",
47515
+ form: "single",
47516
+ optional: false
47517
+ }],
47017
47518
  "recordingOnboard.setSettings": [{
47018
47519
  name: "deviceId",
47019
47520
  form: "single",
@@ -47044,6 +47545,11 @@ Object.freeze({
47044
47545
  form: "single",
47045
47546
  optional: false
47046
47547
  }],
47548
+ "sceneMonitor.getStatus": [{
47549
+ name: "deviceId",
47550
+ form: "single",
47551
+ optional: false
47552
+ }],
47047
47553
  "sceneMonitor.listScenes": [{
47048
47554
  name: "deviceId",
47049
47555
  form: "single",
@@ -47069,6 +47575,11 @@ Object.freeze({
47069
47575
  form: "single",
47070
47576
  optional: false
47071
47577
  }],
47578
+ "scriptRunner.getStatus": [{
47579
+ name: "deviceId",
47580
+ form: "single",
47581
+ optional: false
47582
+ }],
47072
47583
  "scriptRunner.run": [{
47073
47584
  name: "deviceId",
47074
47585
  form: "single",
@@ -47079,11 +47590,31 @@ Object.freeze({
47079
47590
  form: "single",
47080
47591
  optional: false
47081
47592
  }],
47593
+ "smoke.getStatus": [{
47594
+ name: "deviceId",
47595
+ form: "single",
47596
+ optional: false
47597
+ }],
47598
+ "snapshot.applyDeviceSettingsPatch": [{
47599
+ name: "deviceId",
47600
+ form: "single",
47601
+ optional: false
47602
+ }],
47082
47603
  "snapshot.getDebugState": [{
47083
47604
  name: "deviceId",
47084
47605
  form: "single",
47085
47606
  optional: false
47086
47607
  }],
47608
+ "snapshot.getDeviceLiveContribution": [{
47609
+ name: "deviceId",
47610
+ form: "single",
47611
+ optional: false
47612
+ }],
47613
+ "snapshot.getDeviceSettingsContribution": [{
47614
+ name: "deviceId",
47615
+ form: "single",
47616
+ optional: false
47617
+ }],
47087
47618
  "snapshot.getSnapshot": [{
47088
47619
  name: "deviceId",
47089
47620
  form: "single",
@@ -47100,6 +47631,11 @@ Object.freeze({
47100
47631
  form: "array",
47101
47632
  optional: false
47102
47633
  }],
47634
+ "snapshot.getStatus": [{
47635
+ name: "deviceId",
47636
+ form: "single",
47637
+ optional: false
47638
+ }],
47103
47639
  "snapshot.invalidateCache": [{
47104
47640
  name: "deviceId",
47105
47641
  form: "single",
@@ -47110,6 +47646,11 @@ Object.freeze({
47110
47646
  form: "single",
47111
47647
  optional: false
47112
47648
  }],
47649
+ "streamBroker.applyDeviceSettingsPatch": [{
47650
+ name: "deviceId",
47651
+ form: "single",
47652
+ optional: false
47653
+ }],
47113
47654
  "streamBroker.assignProfile": [{
47114
47655
  name: "deviceId",
47115
47656
  form: "single",
@@ -47125,6 +47666,16 @@ Object.freeze({
47125
47666
  form: "single",
47126
47667
  optional: false
47127
47668
  }],
47669
+ "streamBroker.getDeviceLiveContribution": [{
47670
+ name: "deviceId",
47671
+ form: "single",
47672
+ optional: false
47673
+ }],
47674
+ "streamBroker.getDeviceSettingsContribution": [{
47675
+ name: "deviceId",
47676
+ form: "single",
47677
+ optional: false
47678
+ }],
47128
47679
  "streamBroker.getStreamWithCodec": [{
47129
47680
  name: "deviceId",
47130
47681
  form: "single",
@@ -47185,16 +47736,46 @@ Object.freeze({
47185
47736
  form: "single",
47186
47737
  optional: false
47187
47738
  }],
47739
+ "streamParams.getStatus": [{
47740
+ name: "deviceId",
47741
+ form: "single",
47742
+ optional: false
47743
+ }],
47188
47744
  "streamParams.setProfile": [{
47189
47745
  name: "deviceId",
47190
47746
  form: "single",
47191
47747
  optional: false
47192
47748
  }],
47749
+ "switch.getStatus": [{
47750
+ name: "deviceId",
47751
+ form: "single",
47752
+ optional: false
47753
+ }],
47193
47754
  "switch.setState": [{
47194
47755
  name: "deviceId",
47195
47756
  form: "single",
47196
47757
  optional: false
47197
47758
  }],
47759
+ "tamper.getStatus": [{
47760
+ name: "deviceId",
47761
+ form: "single",
47762
+ optional: false
47763
+ }],
47764
+ "temperatureSensor.getStatus": [{
47765
+ name: "deviceId",
47766
+ form: "single",
47767
+ optional: false
47768
+ }],
47769
+ "update.getStatus": [{
47770
+ name: "deviceId",
47771
+ form: "single",
47772
+ optional: false
47773
+ }],
47774
+ "vacuumControl.getStatus": [{
47775
+ name: "deviceId",
47776
+ form: "single",
47777
+ optional: false
47778
+ }],
47198
47779
  "vacuumControl.locate": [{
47199
47780
  name: "deviceId",
47200
47781
  form: "single",
@@ -47230,6 +47811,11 @@ Object.freeze({
47230
47811
  form: "single",
47231
47812
  optional: false
47232
47813
  }],
47814
+ "valve.getStatus": [{
47815
+ name: "deviceId",
47816
+ form: "single",
47817
+ optional: false
47818
+ }],
47233
47819
  "valve.open": [{
47234
47820
  name: "deviceId",
47235
47821
  form: "single",
@@ -47245,6 +47831,11 @@ Object.freeze({
47245
47831
  form: "single",
47246
47832
  optional: false
47247
47833
  }],
47834
+ "vibration.getStatus": [{
47835
+ name: "deviceId",
47836
+ form: "single",
47837
+ optional: false
47838
+ }],
47248
47839
  "videoclips.dialClipStream": [{
47249
47840
  name: "deviceId",
47250
47841
  form: "single",
@@ -47280,6 +47871,11 @@ Object.freeze({
47280
47871
  form: "single",
47281
47872
  optional: false
47282
47873
  }],
47874
+ "waterHeater.getStatus": [{
47875
+ name: "deviceId",
47876
+ form: "single",
47877
+ optional: false
47878
+ }],
47283
47879
  "waterHeater.setAway": [{
47284
47880
  name: "deviceId",
47285
47881
  form: "single",
@@ -47295,6 +47891,11 @@ Object.freeze({
47295
47891
  form: "single",
47296
47892
  optional: false
47297
47893
  }],
47894
+ "weather.getStatus": [{
47895
+ name: "deviceId",
47896
+ form: "single",
47897
+ optional: false
47898
+ }],
47298
47899
  "webrtcSession.addIceCandidate": [{
47299
47900
  name: "deviceId",
47300
47901
  form: "single",
@@ -48032,7 +48633,7 @@ var PreviewTextField = string().max(200);
48032
48633
  * Whether the notification's preview is a STILL or a short animation.
48033
48634
  *
48034
48635
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
48035
- * rule)"* — the other rules already offer an animated preview, and a ten-hour
48636
+ * rule)"* [sent as a gif or video (as for the other rules)] — the other rules already offer an animated preview, and a ten-hour
48036
48637
  * night reads better as three seconds of motion than as one frame of it. Both
48037
48638
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
48038
48639
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -48170,6 +48771,7 @@ TimelapseRuleInputSchema.extend({
48170
48771
  createdAt: number(),
48171
48772
  updatedAt: number()
48172
48773
  });
48774
+ MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
48173
48775
  object({
48174
48776
  /**
48175
48777
  * Fraction of the box's own size added on EACH side before cutting.