@camstack/addon-provider-dreame 0.2.30 → 0.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +572 -61
  2. package/dist/addon.mjs +572 -61
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -58031,6 +58031,66 @@ var OpsLogQueryInputSchema = object({
58031
58031
  /** Max rows returned, newest-first. */
58032
58032
  limit: number().int().min(1).max(1e3).optional()
58033
58033
  });
58034
+ var LabelDefinitionSchema = object({
58035
+ id: string(),
58036
+ name: string(),
58037
+ category: string().optional(),
58038
+ description: string().optional(),
58039
+ icon: string().optional()
58040
+ });
58041
+ /** Detection-macro targets a catalog `classMap` may resolve to. */
58042
+ var CLASS_MAP_MACRO_TARGETS = [
58043
+ "person",
58044
+ "vehicle",
58045
+ "animal",
58046
+ "package"
58047
+ ];
58048
+ /**
58049
+ * Le macro classi di PRIMO LIVELLO: quelle che un object detector emette e che
58050
+ * un operatore può selezionare.
58051
+ *
58052
+ * Sono le tre offerte dallo step `object-detection`
58053
+ * (`addon-pipeline/src/detection-pipeline/registry/step-definitions.ts`,
58054
+ * `enabledMacroClasses`). `package` sta in {@link CLASS_MAP_MACRO_TARGETS} e in
58055
+ * `MACRO_LABELS` — è una macro vera — ma NON qui: appartiene allo step
58056
+ * `package-detection`, la cui abilitazione è guidata dalle zone, e offrire la
58057
+ * stessa parola due volte ha già fatto accendere a un operatore il proxy COCO
58058
+ * (suitcase/backpack/handbag) lasciando spento il detector dedicato.
58059
+ *
58060
+ * UNA lista. Prima di oggi le stesse tre erano scritte a mano nell'offerta
58061
+ * dello step e una seconda volta come union `FirstLevelMacro`
58062
+ * (`types/detection.ts`); una terza copia per il trigger di registrazione
58063
+ * (`RecordingTriggers.objectClasses`) avrebbe reso invisibile la divergenza
58064
+ * successiva.
58065
+ */
58066
+ var FIRST_LEVEL_MACRO_CLASSES = [
58067
+ "person",
58068
+ "vehicle",
58069
+ "animal"
58070
+ ];
58071
+ /**
58072
+ * Wire schema for a per-model CATALOG classMap override
58073
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
58074
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
58075
+ * detection pipeline executor actually routes.
58076
+ *
58077
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
58078
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
58079
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
58080
+ * enum) — the two used to share the name `ClassMapDefinition`/
58081
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
58082
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
58083
+ * are not: it is two different concepts colliding on a name. Keep this type
58084
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
58085
+ * would either narrow every `ClassMapDefinition` consumer to the four
58086
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
58087
+ * schema exists for (see the "rejects a classMap whose target is not a
58088
+ * detection macro" test in `model-catalog-schema.test.ts`).
58089
+ */
58090
+ var DetectionCatalogClassMapSchema = object({
58091
+ mapping: record(string(), _enum(CLASS_MAP_MACRO_TARGETS)),
58092
+ preserveOriginal: boolean()
58093
+ });
58034
58094
  /**
58035
58095
  * Numeric day-of-week: 0 = Sunday … 6 = Saturday (matches `Date.getDay`).
58036
58096
  * Named `RecordingWeekday` to avoid collision with the string-union
@@ -58053,10 +58113,55 @@ var RecordingStorageModeSchema = _enum([
58053
58113
  "events",
58054
58114
  "continuous"
58055
58115
  ]);
58116
+ /**
58117
+ * Le macro classi che possono aprire una finestra di registrazione — le stesse
58118
+ * tre offerte dallo step `object-detection`, da UNA lista
58119
+ * ({@link FIRST_LEVEL_MACRO_CLASSES}).
58120
+ */
58121
+ var RecordingObjectTriggerClassSchema = _enum(FIRST_LEVEL_MACRO_CLASSES);
58122
+ /**
58123
+ * True quando `values` non ripete un elemento.
58124
+ *
58125
+ * Un duplicato non è innocuo: ogni voce di `objectClasses` / `sensorDeviceIds`
58126
+ * diventa una SORGENTE in `bandTriggerSources`, e la stessa sorgente due volte
58127
+ * conterebbe due volte le sue finestre in `segmentMissedByMs`.
58128
+ */
58129
+ var noDuplicates = (values) => new Set(values).size === values.length;
58056
58130
  /** Which detectors trigger an `events`-mode band. */
58057
58131
  var RecordingTriggersSchema = object({
58058
58132
  motion: boolean().optional(),
58059
- audioThresholdDbfs: number().optional()
58133
+ audioThresholdDbfs: number().optional(),
58134
+ /**
58135
+ * Le macro classi la cui detection apre una finestra. ASSENTE = la sorgente
58136
+ * non è ascoltata; un array VUOTO è rifiutato, perché "banda events, trigger
58137
+ * object acceso, nessuna classe" è la stessa forma "abilitata e non registra
58138
+ * nulla, per sempre" contro cui è scritto `eventsBandCanEverDemand`.
58139
+ *
58140
+ * Il segnale letto è GIÀ FILTRATO: solo detection `source: 'pipeline'`, cioè
58141
+ * quelle che hanno attraversato `enabledMacroClasses`, i
58142
+ * `minConfidence<Macro>` e il full-frame guard. L'AI a bordo camera
58143
+ * (`source: 'onboard'`) non attraversa nessuno di quei gate e NON apre
58144
+ * finestre — vedi `recorder/object-trigger.ts`.
58145
+ */
58146
+ objectClasses: array(RecordingObjectTriggerClassSchema).min(1).refine(noDuplicates, { message: "objectClasses must not repeat a class" }).optional(),
58147
+ /**
58148
+ * I device LINKED il cui FRONTE ALTO apre una finestra. Assente = la sorgente
58149
+ * non è ascoltata; un array vuoto è rifiutato per la stessa ragione di
58150
+ * `objectClasses`.
58151
+ *
58152
+ * Sono id di device SORGENTE, non camere: la banda li nomina, quindi il
58153
+ * percorso caldo (`DeviceStateChanged`, a ritmo di bus su tutta la flotta)
58154
+ * non fa RPC. L'OFFERTA da cui l'operatore li sceglie è un'altra domanda, e
58155
+ * si risolve con `deviceManager.getLinkedDevices` + `getBindingsBatch` per
58156
+ * device (D12) — mai un elenco globale di cap.
58157
+ *
58158
+ * Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
58159
+ * `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
58160
+ * la stessa che il virtual-doorbell usa dal 2026-08-05. Ed è il FRONTE, non
58161
+ * il livello: un contatto trovato già aperto al riavvio del runner non fa
58162
+ * registrare.
58163
+ */
58164
+ sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
58060
58165
  });
58061
58166
  /**
58062
58167
  * Mode of a single recording band — the recorder per-band vocabulary.
@@ -58508,41 +58613,6 @@ var DecoderSessionConfigSchema = object({
58508
58613
  */
58509
58614
  debug: boolean().optional()
58510
58615
  });
58511
- var LabelDefinitionSchema = object({
58512
- id: string(),
58513
- name: string(),
58514
- category: string().optional(),
58515
- description: string().optional(),
58516
- icon: string().optional()
58517
- });
58518
- /**
58519
- * Wire schema for a per-model CATALOG classMap override
58520
- * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
58521
- * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
58522
- * detection pipeline executor actually routes.
58523
- *
58524
- * This is deliberately a DIFFERENT, narrower shape than the general-purpose
58525
- * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
58526
- * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
58527
- * enum) — the two used to share the name `ClassMapDefinition`/
58528
- * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
58529
- * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
58530
- * are not: it is two different concepts colliding on a name. Keep this type
58531
- * under its own name rather than reusing `ClassMapDefinition` — reusing it
58532
- * would either narrow every `ClassMapDefinition` consumer to the four
58533
- * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
58534
- * schema exists for (see the "rejects a classMap whose target is not a
58535
- * detection macro" test in `model-catalog-schema.test.ts`).
58536
- */
58537
- var DetectionCatalogClassMapSchema = object({
58538
- mapping: record(string(), _enum([
58539
- "person",
58540
- "vehicle",
58541
- "animal",
58542
- "package"
58543
- ])),
58544
- preserveOriginal: boolean()
58545
- });
58546
58616
  var MODEL_FORMATS = [
58547
58617
  "onnx",
58548
58618
  "coreml",
@@ -66304,7 +66374,23 @@ var NcHistoryEntrySchema = object({
66304
66374
  updatedAt: number(),
66305
66375
  /** Failure detail — present on a `dead` row. */
66306
66376
  error: string().optional(),
66307
- subject: NcHistorySubjectSchema
66377
+ subject: NcHistorySubjectSchema,
66378
+ /**
66379
+ * Ids of the artefacts (still, then gif, then clip) this row's successful
66380
+ * delivery indexed in the artefact library — a REFERENCE, never the bytes
66381
+ * (an artefact is often megabytes; this row is durable JSON rewritten on
66382
+ * every delivery attempt). Absent on a row still pending/dead, a row
66383
+ * delivered before this field shipped, or a wiring with no artefact index.
66384
+ *
66385
+ * Resolve one to a fetchable URL with `resolveArtifactUrl` — an id
66386
+ * outlives any one URL's TTL, so a caller mints a fresh link on demand
66387
+ * rather than trusting one frozen at delivery time. `resolveArtifactUrl`
66388
+ * also answers `null` for an id whose artefact has since expired past the
66389
+ * retained shelf's own age bound — the degrade a caller (the Home
66390
+ * Assistant export) must render as "no image right now", never as a
66391
+ * broken link.
66392
+ */
66393
+ artifactIds: array(string().min(1)).optional()
66308
66394
  });
66309
66395
  /**
66310
66396
  * Query filter for `getHistory` (spec §4.2). Every field is a narrowing
@@ -66531,7 +66617,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
66531
66617
  }), method(object({}), object({
66532
66618
  catalog: array(NcConditionDescriptorSchema),
66533
66619
  taxonomy: NcTaxonomySchema.optional()
66534
- })), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
66620
+ })), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
66535
66621
  kind: "mutation",
66536
66622
  caller: "required"
66537
66623
  }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
@@ -71659,7 +71745,7 @@ var lifecycleJobSchema = object({
71659
71745
  * `useAddonsOnAddonLogs`, etc. flow through the same codegen pipeline
71660
71746
  * as every other cap.
71661
71747
  */
71662
- var LogLevelSchema$1 = _enum([
71748
+ var LogLevelSchema$2 = _enum([
71663
71749
  "debug",
71664
71750
  "info",
71665
71751
  "warn",
@@ -71866,7 +71952,7 @@ var CustomActionInputSchema = object({
71866
71952
  method(_void(), array(AddonListItemSchema).readonly()), method(object({
71867
71953
  addonId: string(),
71868
71954
  limit: number().min(1).max(500).default(100),
71869
- level: LogLevelSchema$1.optional()
71955
+ level: LogLevelSchema$2.optional()
71870
71956
  }), array(LogQueryEntrySchema)), method(_void(), array(InstalledPackageSchema).readonly()), method(object({
71871
71957
  packageName: string(),
71872
71958
  version: string().optional()
@@ -71964,7 +72050,7 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
71964
72050
  auth: "admin"
71965
72051
  }), method(object({
71966
72052
  addonId: string(),
71967
- level: LogLevelSchema$1.optional()
72053
+ level: LogLevelSchema$2.optional()
71968
72054
  }), LogStreamEntrySchema, { kind: "subscription" });
71969
72055
  /**
71970
72056
  * Multi-metric air-quality slice. Covers CO₂, total VOCs, particulate
@@ -73713,6 +73799,35 @@ var FaceFilterEnum = _enum([
73713
73799
  "identified",
73714
73800
  "all"
73715
73801
  ]);
73802
+ /**
73803
+ * What a `listRecentFaces` page is ORDERED BY.
73804
+ *
73805
+ * - `timestamp` — when the face was seen. The historical (and default) order.
73806
+ * - `suggestionConfidence` — {@link FaceInfo.suggestedMatchScore}, the peak
73807
+ * cosine of the face's SUGGESTED identity. This is the "review by certainty"
73808
+ * order: it puts the suggestions an operator can confirm with one tap at the
73809
+ * top, and it is the reason this enum exists — a client that ranked a capped
73810
+ * page client-side was ranking the newest N, never the most certain N.
73811
+ *
73812
+ * A row with NO suggestion (`suggestedMatchScore` absent — a legacy row, an
73813
+ * auto-assigned face, or a face below the suggestion band) has no certainty to
73814
+ * compare. Under `suggestionConfidence` it sorts **LAST, in BOTH directions**
73815
+ * — flipping the direction reorders the rows that HAVE a certainty and never
73816
+ * floods the page with the ones that do not. `addon-post-analysis`'s
73817
+ * `store/face-sort.ts` is the single implementation, tiebreaks newest-first
73818
+ * then by faceId, and is what makes this a total order instead of the
73819
+ * backend's NULL-collation accident.
73820
+ */
73821
+ var FaceSortFieldEnum = _enum(["timestamp", "suggestionConfidence"]);
73822
+ var FaceSortDirectionEnum = _enum(["asc", "desc"]);
73823
+ /** One suggested group of look-alike UNASSIGNED faces. Ids only — an embedding
73824
+ * never leaves the server. */
73825
+ var FaceClusterSchema = object({
73826
+ faceIds: array(string()).readonly(),
73827
+ representativeFaceId: string(),
73828
+ size: number().int(),
73829
+ cohesion: number()
73830
+ });
73716
73831
  var MediaFileLiteSchema$1 = object({
73717
73832
  key: string(),
73718
73833
  kind: string(),
@@ -73759,24 +73874,72 @@ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly
73759
73874
  kind: "mutation",
73760
73875
  auth: "admin"
73761
73876
  }), method(object({
73762
- /** Restrict to one camera. Absent keeps the cluster-wide gallery view. */
73877
+ /**
73878
+ * Restrict to ONE camera. Absent keeps the cluster-wide gallery view.
73879
+ *
73880
+ * The legacy single-camera form, kept verbatim for every caller that
73881
+ * already sends it. A caller that wants a SET sends {@link deviceIds}
73882
+ * instead — never both: `deviceIds` is the authority whenever it is
73883
+ * present, and this field is then ignored rather than unioned, so
73884
+ * there is exactly one answer to "which cameras did I ask for".
73885
+ */
73763
73886
  deviceId: number().int().optional(),
73887
+ /**
73888
+ * Restrict to a SET of cameras — the review UI's camera filter, which
73889
+ * until now had to fetch the cluster-wide page and drop rows in the
73890
+ * client (so the `limit` it asked for was spent on cameras it was
73891
+ * about to discard).
73892
+ *
73893
+ * An **empty array reads NOTHING** — `[]` is an empty page, never
73894
+ * "every camera". A request for no devices is a request, not an
73895
+ * omission; same contract as `deviceManager.listFleet` and
73896
+ * `pipelineAnalytics.listRecentTracks`.
73897
+ *
73898
+ * Absent (`undefined`) is the omission, and keeps the cluster-wide view.
73899
+ */
73900
+ deviceIds: array(number().int()).optional(),
73764
73901
  limit: number().int().positive().optional(),
73765
73902
  filter: FaceFilterEnum.optional(),
73766
73903
  /**
73767
- * Inline the base64 crop on every row. Default `true` — the existing
73768
- * behaviour, kept so no caller breaks.
73904
+ * Window lower bound on {@link FaceInfo.timestamp}, INCLUSIVE.
73905
+ * Absent means no lower bound.
73906
+ */
73907
+ since: number().int().optional(),
73908
+ /**
73909
+ * Window upper bound on {@link FaceInfo.timestamp}, INCLUSIVE.
73910
+ * Absent means no upper bound.
73911
+ */
73912
+ until: number().int().optional(),
73913
+ /**
73914
+ * Order the page by time or by suggestion certainty. Default
73915
+ * `'timestamp'` — the historical order, unchanged for every caller
73916
+ * that does not ask.
73769
73917
  *
73770
- * Set `false` once the caller renders {@link FaceInfo.cropUrl}: that
73771
- * drops ~2.87 MiB per 500-row page to a few KiB of metadata and lets
73772
- * the browser cache the images.
73918
+ * See {@link FaceSortFieldEnum} for what a row with no suggestion
73919
+ * does under `'suggestionConfidence'`.
73773
73920
  *
73774
- * **This is an INPUT field, so it does not reach the addon until the
73775
- * next train.** The hub router validates cap inputs against its own
73776
- * compiled Zod, which strips a key it does not know — verified today
73777
- * on the OUTPUT side, where an additive field DOES arrive immediately
73778
- * (`Track.hasFace`). Until the train ships, sending `false` is
73779
- * harmless and simply keeps the crops inline.
73921
+ * Cost note: `'timestamp'` is served by the `(deviceId, timestamp)`
73922
+ * index and stops reading as soon as `limit` rows have PASSED the
73923
+ * filter. `'suggestionConfidence'` cannot stop early — the most
73924
+ * certain row may be the oldest — so it walks the window. Narrow it
73925
+ * with {@link since} / {@link until}.
73926
+ */
73927
+ sortBy: FaceSortFieldEnum.optional(),
73928
+ /** Sort direction for {@link sortBy}. Default `'desc'`. */
73929
+ sortDirection: FaceSortDirectionEnum.optional(),
73930
+ /**
73931
+ * Inline the base64 crop on every row.
73932
+ *
73933
+ * Default `false` since the 2026-08-25 inversion — see
73934
+ * `include-crops-default.ts`, which is the ONE place that resolves
73935
+ * this for every gallery, and which records why the inline shape had
73936
+ * to become the one you ASK for (60 457 ms → UDS timeout at 500 rows).
73937
+ * The doc here used to still say `true`; it was wrong, and a leftover
73938
+ * that describes the old design reads as permission to rely on it.
73939
+ *
73940
+ * Nothing loses its image: the row carries {@link FaceInfo.cropUrl},
73941
+ * which the browser fetches off the `event-media` plane in parallel,
73942
+ * cached and ETagged.
73780
73943
  */
73781
73944
  includeCrops: boolean().optional()
73782
73945
  }).optional(), array(FaceInfoSchema).readonly()), method(object({
@@ -73812,13 +73975,39 @@ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly
73812
73975
  }), method(object({
73813
73976
  threshold: number().min(0).max(1).optional(),
73814
73977
  minClusterSize: number().int().min(2).optional(),
73815
- limit: number().int().positive().optional()
73816
- }).optional(), array(object({
73817
- faceIds: array(string()).readonly(),
73818
- representativeFaceId: string(),
73819
- size: number().int(),
73820
- cohesion: number()
73821
- })).readonly());
73978
+ /**
73979
+ * Cap on the number of CLUSTERS returned. Renamed from `limit`,
73980
+ * which read as though it bounded the work — it never did.
73981
+ *
73982
+ * Wins over {@link limit} when both are sent.
73983
+ */
73984
+ maxClusters: number().int().positive().optional(),
73985
+ /**
73986
+ * @deprecated Ambiguous name for {@link maxClusters} — it cuts the
73987
+ * RESULT, not the scan. Kept so existing callers keep working; send
73988
+ * `maxClusters` (and, if you care about cost, {@link maxFacesScanned}).
73989
+ */
73990
+ limit: number().int().positive().optional(),
73991
+ /**
73992
+ * Cap on the number of unassigned faces READ AND CLUSTERED — the
73993
+ * POOL, not the result.
73994
+ *
73995
+ * This is the knob {@link maxClusters} was mistaken for. Clustering
73996
+ * used to read every unassigned face on the hub no matter what the
73997
+ * caller asked for, because the only bound cut the finished clusters
73998
+ * afterwards; a UI showing a window of 100 paid for a scan of the
73999
+ * whole corpus, on an addon whose disk is under contention.
74000
+ *
74001
+ * The pool is the NEWEST matching faces first — the same order the
74002
+ * gallery shows — so a bound here shortens the horizon, it does not
74003
+ * sample it randomly.
74004
+ *
74005
+ * Default: 1 000 (`FACE_SWEEP_PAGE`, exactly one store page). Chosen
74006
+ * so the live corpus — 372 face rows — is unaffected while the
74007
+ * unbounded scan can never come back as the table grows.
74008
+ */
74009
+ maxFacesScanned: number().int().positive().optional()
74010
+ }).optional(), array(FaceClusterSchema).readonly());
73822
74011
  /**
73823
74012
  * Fan-control cap. Models HA `fan.*` entity-specific surfaces:
73824
74013
  * speed percentage, preset modes, ceiling-fan direction, and
@@ -77576,6 +77765,39 @@ var ReadGopBytesResultSchema = object({
77576
77765
  /** Media ms the returned fragment covers. */
77577
77766
  gopDurMs: number()
77578
77767
  });
77768
+ /**
77769
+ * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
77770
+ * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
77771
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
77772
+ * a replay needs several seconds of native pixels, not one frame.
77773
+ *
77774
+ * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
77775
+ * is `false` when the returned bytes were cut short by the read's own safety
77776
+ * byte cap before covering `[fromMs, toMs)` — a truncation, reported, not a
77777
+ * silently shorter answer. `spans-multiple-segments` is a REFUSAL, not a
77778
+ * degradation: a window whose end falls past the covering segment would need
77779
+ * bytes stitched from a second segment file (its own `ftyp`+`moov`), which is
77780
+ * not one standalone-demuxable stream — the caller's answer is to request a
77781
+ * shorter window or one aligned to a single segment, not to receive spliced
77782
+ * bytes nothing has proven decodable.
77783
+ */
77784
+ var ReadWindowBytesResultSchema = discriminatedUnion("kind", [object({
77785
+ kind: literal("ok"),
77786
+ data: _instanceof(Uint8Array),
77787
+ /** Absolute epoch ms of the returned bytes' first sample — at or before
77788
+ * the requested `fromMs` (anchored on the nearest keyframe). */
77789
+ gopStartMs: number(),
77790
+ /** Media ms the returned bytes cover, from `gopStartMs`. */
77791
+ gopDurMs: number(),
77792
+ /** `false` ⇒ the safety byte cap cut the read short before it reached
77793
+ * the requested `toMs`; the caller got fewer frames than asked for. */
77794
+ reachesRequestedEnd: boolean()
77795
+ }), object({
77796
+ kind: literal("spans-multiple-segments"),
77797
+ /** Where the covering segment's own footage runs out — informational,
77798
+ * not a retry hint (retrying the same window would refuse again). */
77799
+ segmentEndMs: number()
77800
+ })]);
77579
77801
  method(object({
77580
77802
  deviceId: number(),
77581
77803
  fromMs: number(),
@@ -77626,6 +77848,15 @@ method(object({
77626
77848
  }), ReadGopBytesResultSchema, {
77627
77849
  kind: "query",
77628
77850
  auth: "admin"
77851
+ }), method(object({
77852
+ deviceId: number(),
77853
+ profile: string(),
77854
+ startMs: number(),
77855
+ fromMs: number(),
77856
+ toMs: number()
77857
+ }), ReadWindowBytesResultSchema, {
77858
+ kind: "query",
77859
+ auth: "admin"
77629
77860
  }), method(object({
77630
77861
  deviceId: number(),
77631
77862
  config: RecordingConfigSchema
@@ -78718,6 +78949,211 @@ var SetSiteLocationInputSchema = object({
78718
78949
  latitude: number().min(-90).max(90),
78719
78950
  longitude: number().min(-180).max(180)
78720
78951
  }).nullable();
78952
+ /**
78953
+ * One `(procedure, user-agent, ip, principal)` tuple of the HTTP request
78954
+ * census. `principal` is the DERIVED identity (`apocaliss92 (admin)`,
78955
+ * `scoped:1a2b3c4d (scoped-token)`, `anonymous`) that the tRPC error log
78956
+ * already prints - never a token, never an `Authorization` header.
78957
+ */
78958
+ var RequestCensusGroupSchema = object({
78959
+ procedure: string(),
78960
+ userAgent: string(),
78961
+ ip: string(),
78962
+ principal: string(),
78963
+ calls: number(),
78964
+ perMin: number()
78965
+ });
78966
+ /**
78967
+ * A procedure's TOTAL over the window, across every caller.
78968
+ *
78969
+ * This block, not the group list, is what answers "did these calls arrive over
78970
+ * HTTP at all". A total far BELOW what a store-side census counted over the
78971
+ * same window excludes the HTTP plane, which is a result, not a failure.
78972
+ */
78973
+ var RequestCensusProcedureSchema = object({
78974
+ procedure: string(),
78975
+ calls: number(),
78976
+ perMin: number()
78977
+ });
78978
+ /**
78979
+ * The census as an operator sees it.
78980
+ *
78981
+ * `persisted` is the honest answer to "will this survive the restart I am
78982
+ * about to do": the arm deadline is written to `system-settings` so a window
78983
+ * armed now can measure the NEXT boot, and a write that failed must not look
78984
+ * like one that succeeded.
78985
+ */
78986
+ var RequestCensusStatusSchema = object({
78987
+ armed: boolean(),
78988
+ /** How long the current - or just-closed - window collected, in ms. */
78989
+ elapsedMs: number(),
78990
+ /** The window actually armed, after the server clamped the request. */
78991
+ windowMs: number(),
78992
+ /** Epoch ms the window closes at. 0 when disarmed. */
78993
+ armedUntilMs: number(),
78994
+ httpRequests: number(),
78995
+ batchedRequests: number(),
78996
+ /**
78997
+ * Procedure invocations. Higher than `httpRequests` whenever tRPC batching
78998
+ * is in play (`?batch=1` carries several procedures in one request); this is
78999
+ * the number comparable with a store-side call count.
79000
+ */
79001
+ procedureCalls: number(),
79002
+ /**
79003
+ * tRPC WebSocket connections opened during the window. NOT calls - the WS
79004
+ * transport resolves one context per connection - but the number that says
79005
+ * whether a plane this census cannot see was busy while HTTP was quiet.
79006
+ */
79007
+ wsConnections: number(),
79008
+ distinctGroups: number(),
79009
+ /** Calls counted in the totals whose group attribution was shed at the
79010
+ * cardinality bound. */
79011
+ unattributedCalls: number(),
79012
+ procedures: array(RequestCensusProcedureSchema).readonly(),
79013
+ groups: array(RequestCensusGroupSchema).readonly()
79014
+ }).extend({ persisted: boolean() });
79015
+ /** Severity vocabulary. Mirrors `LogLevel` in `interfaces/logging.ts`. */
79016
+ var LogLevelSchema$1 = _enum([
79017
+ "debug",
79018
+ "info",
79019
+ "warn",
79020
+ "error"
79021
+ ]);
79022
+ /**
79023
+ * The diagnostics that can be ARMED for a window. Exactly one today.
79024
+ *
79025
+ * A diagnostic is anything whose cost is only worth paying while a question is
79026
+ * open. It never persists as a boolean: see {@link DiagnosticWindowSchema}.
79027
+ */
79028
+ var DiagnosticIdSchema = _enum(["request-census"]);
79029
+ /**
79030
+ * The layers of the level hierarchy, general → specific. The most specific
79031
+ * layer that carries an explicit value wins.
79032
+ *
79033
+ * `component` is DECLARED and not yet resolvable: the per-component channels
79034
+ * are a later slice of the same plan, and a `levelSource` enum that has to
79035
+ * grow later would force every consumer of this document to change with it.
79036
+ * Nothing returns `component` today.
79037
+ */
79038
+ var LoggingScopeKindSchema = _enum([
79039
+ "cluster",
79040
+ "node",
79041
+ "component"
79042
+ ]);
79043
+ /** Where an effective level came from. `default` = nothing is set anywhere. */
79044
+ var LoggingLevelSourceSchema = _enum([
79045
+ "default",
79046
+ "cluster",
79047
+ "node",
79048
+ "component"
79049
+ ]);
79050
+ /**
79051
+ * One layer of the hierarchy as it actually STANDS.
79052
+ *
79053
+ * `level: null` is the whole reason this array is returned: it is the
79054
+ * difference between "this node is at `info` because I decided it" and
79055
+ * "...because it inherits". An operator who clears an override believing they
79056
+ * are clearing an inherited value has been handed the same defect as the two
79057
+ * contradicting knobs this document exists to remove, moved one floor up.
79058
+ */
79059
+ var LoggingLevelLayerSchema = object({
79060
+ scope: LoggingScopeKindSchema,
79061
+ /** The node this layer speaks for; `null` on the cluster layer. */
79062
+ nodeId: string().nullable(),
79063
+ /** Explicitly set here, or `null` when this layer inherits. */
79064
+ level: LogLevelSchema$1.nullable()
79065
+ });
79066
+ /** What a line is judged against, and WHICH layer decided it. */
79067
+ var LoggingEffectiveSchema = object({
79068
+ level: LogLevelSchema$1,
79069
+ levelSource: LoggingLevelSourceSchema
79070
+ });
79071
+ /** Every layer, general → specific. Never collapsed into the effective value. */
79072
+ var LoggingExplicitSchema = object({ layers: array(LoggingLevelLayerSchema).readonly() });
79073
+ /**
79074
+ * An armed diagnostic, with its DEADLINE.
79075
+ *
79076
+ * The shape of ADR-0244: what is stored is a deadline and never a flag, so a
79077
+ * diagnostic somebody forgot expires by itself, and a boot-window measurement
79078
+ * survives the restart it exists to measure. `remainingMs` is 0 whenever
79079
+ * `armed` is false — a window is never reported as slightly expired.
79080
+ */
79081
+ var DiagnosticWindowSchema = object({
79082
+ id: DiagnosticIdSchema,
79083
+ armed: boolean(),
79084
+ /** Epoch ms the window closes at. 0 when disarmed. */
79085
+ armedUntilMs: number(),
79086
+ /** Ms left before it expires on its own. 0 when disarmed. */
79087
+ remainingMs: number(),
79088
+ /** Whether the stored deadline is the one the live diagnostic is running —
79089
+ * i.e. whether this window would survive a restart. */
79090
+ persisted: boolean()
79091
+ });
79092
+ /**
79093
+ * `armMs: 0` DISARMS. Any positive value arms for that long, clamped by the
79094
+ * server — there is no maximum here on purpose: a bound repeated in a schema
79095
+ * is a second knob that disagrees with the first the day one of them moves.
79096
+ */
79097
+ var DiagnosticWindowPatchSchema = object({
79098
+ id: DiagnosticIdSchema,
79099
+ armMs: number().int().min(0),
79100
+ /** Cadence of the diagnostic's aggregated report line. Clamped by the server. */
79101
+ reportEveryMs: number().int().positive().optional()
79102
+ });
79103
+ /**
79104
+ * A PATCH, and patches MERGE.
79105
+ *
79106
+ * A field absent from the patch is left exactly as it was — arming a
79107
+ * diagnostic never resets a level, and setting a level never disarms a window.
79108
+ * The provider must not reconstruct the document as `{ ...snapshot, ...patch }`:
79109
+ * `setAll` already merges, and rebuilding the object is how an absent field
79110
+ * turns into an erased one.
79111
+ */
79112
+ var LoggingSettingsPatchSchema = object({
79113
+ /**
79114
+ * Absent leaves the level untouched. `null` CLEARS the explicit value at the
79115
+ * addressed scope so it inherits again. A value sets it.
79116
+ */
79117
+ level: LogLevelSchema$1.nullable().optional(),
79118
+ /**
79119
+ * Only the diagnostics NAMED here change. An armed window that is not listed
79120
+ * keeps running — a patch is never a full replacement.
79121
+ */
79122
+ diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional()
79123
+ });
79124
+ /**
79125
+ * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
79126
+ *
79127
+ * Deliberately NOT called `nodeId`: that key is reserved on every cap method
79128
+ * input — the generated router strips it and uses it to resolve the PROVIDER
79129
+ * on that node (`resolveProvider(cap, nodeId, …)`). A document about
79130
+ * `agent-1` addressed as `nodeId` would be forwarded to agent-1 and answered
79131
+ * by an agent that holds no cluster document at all. The hub is the single
79132
+ * authority over the whole hierarchy and answers for every layer, so the
79133
+ * layer selector needs a name the transport does not already own.
79134
+ */
79135
+ var GetLoggingSettingsInputSchema = object({ scopeNodeId: string().optional() });
79136
+ var SetLoggingSettingsInputSchema = object({
79137
+ scopeNodeId: string().optional(),
79138
+ patch: LoggingSettingsPatchSchema
79139
+ });
79140
+ /**
79141
+ * The whole document, as read and as returned after every write.
79142
+ *
79143
+ * `persisted: false` means the settings store could not be read or written.
79144
+ * The in-memory mirror still governs behaviour and is unchanged by the
79145
+ * failure — a read that fails neither switches a level nor disarms a window
79146
+ * (D49) — but the operator is told that what they are looking at would not
79147
+ * survive a restart.
79148
+ */
79149
+ var LoggingSettingsStateSchema = object({
79150
+ /** The layer this document was read at. `null` = the cluster layer. */
79151
+ scopeNodeId: string().nullable(),
79152
+ effective: LoggingEffectiveSchema,
79153
+ explicit: LoggingExplicitSchema,
79154
+ activeWindows: array(DiagnosticWindowSchema).readonly(),
79155
+ persisted: boolean()
79156
+ });
78721
79157
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
78722
79158
  kind: "mutation",
78723
79159
  auth: "admin"
@@ -78730,6 +79166,9 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
78730
79166
  }), method(_void(), SiteLocationStatusSchema, {
78731
79167
  kind: "mutation",
78732
79168
  auth: "admin"
79169
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
79170
+ kind: "mutation",
79171
+ auth: "admin"
78733
79172
  });
78734
79173
  /**
78735
79174
  * Tamper / case-open detection sensor. Drives Home Assistant
@@ -83713,6 +84152,12 @@ Object.freeze({
83713
84152
  addonId: null,
83714
84153
  access: "view"
83715
84154
  },
84155
+ "notificationRules.resolveArtifactUrl": {
84156
+ capName: "notification-rules",
84157
+ capScope: "system",
84158
+ addonId: null,
84159
+ access: "view"
84160
+ },
83716
84161
  "notificationRules.setAlarmConfig": {
83717
84162
  capName: "notification-rules",
83718
84163
  capScope: "system",
@@ -85117,6 +85562,12 @@ Object.freeze({
85117
85562
  addonId: null,
85118
85563
  access: "view"
85119
85564
  },
85565
+ "recording.readWindowBytes": {
85566
+ capName: "recording",
85567
+ capScope: "system",
85568
+ addonId: null,
85569
+ access: "view"
85570
+ },
85120
85571
  "recording.refreshStorageLocationsForMigration": {
85121
85572
  capName: "recording",
85122
85573
  capScope: "system",
@@ -85957,6 +86408,18 @@ Object.freeze({
85957
86408
  addonId: null,
85958
86409
  access: "create"
85959
86410
  },
86411
+ "system.getLoggingSettings": {
86412
+ capName: "system",
86413
+ capScope: "system",
86414
+ addonId: null,
86415
+ access: "view"
86416
+ },
86417
+ "system.getRequestCensus": {
86418
+ capName: "system",
86419
+ capScope: "system",
86420
+ addonId: null,
86421
+ access: "view"
86422
+ },
85960
86423
  "system.getRetentionConfig": {
85961
86424
  capName: "system",
85962
86425
  capScope: "system",
@@ -85987,6 +86450,12 @@ Object.freeze({
85987
86450
  addonId: null,
85988
86451
  access: "view"
85989
86452
  },
86453
+ "system.setLoggingSettings": {
86454
+ capName: "system",
86455
+ capScope: "system",
86456
+ addonId: null,
86457
+ access: "create"
86458
+ },
85990
86459
  "system.setRetentionConfig": {
85991
86460
  capName: "system",
85992
86461
  capScope: "system",
@@ -87142,6 +87611,10 @@ Object.freeze({
87142
87611
  name: "deviceId",
87143
87612
  form: "single",
87144
87613
  optional: true
87614
+ }, {
87615
+ name: "deviceIds",
87616
+ form: "array",
87617
+ optional: true
87145
87618
  }],
87146
87619
  "fanControl.setDirection": [{
87147
87620
  name: "deviceId",
@@ -87947,6 +88420,11 @@ Object.freeze({
87947
88420
  form: "single",
87948
88421
  optional: false
87949
88422
  }],
88423
+ "recording.readWindowBytes": [{
88424
+ name: "deviceId",
88425
+ form: "single",
88426
+ optional: false
88427
+ }],
87950
88428
  "recording.relocateFootage": [{
87951
88429
  name: "deviceId",
87952
88430
  form: "single",
@@ -88747,7 +89225,38 @@ object({
88747
89225
  * fallback — i.e. the pre-2026-08-13 miss profile. Set it there only to
88748
89226
  * reproduce that.
88749
89227
  */
88750
- tileBudgetMb: number().int().min(0).max(1024)
89228
+ tileBudgetMb: number().int().min(0).max(1024),
89229
+ /**
89230
+ * RAM ceiling per decode worker, in MB, for the SCENE TILES — one
89231
+ * JPEG-encoded copy of the WHOLE native frame, cut in the same instant as the
89232
+ * subject tiles, on frames that detected something.
89233
+ *
89234
+ * It exists because a subject tile cannot answer a FULL-FRAME request:
89235
+ * containment is strict by design, so the native `keyFrame`, the detail
89236
+ * plane's `frameJpeg` rung and the display-crop fallback had no rung at all
89237
+ * below the hold. Measured on the live cluster: 23.3% of key-frame captures
89238
+ * missed, 95.7% of them with `worker-lease-gone` — the raster released one
89239
+ * frame-time after delivery, with the request only p50 367 ms behind it.
89240
+ *
89241
+ * Sizing, and why this is a budget and not a duration: a scene tile is
89242
+ * ~1.5-2.5 MB at 4K (against ~23.75 MB for the raster it was cut from and
89243
+ * ~60-120 KB for a subject tile), and it is cut ~0.4 times a second per busy
89244
+ * camera — only detection-bearing frames get one. 48 MB is therefore ~20-30
89245
+ * frames, i.e. the store's 30 s TTL binds at the steady state and the budget
89246
+ * binds only through a detection burst, where it still covers well past the
89247
+ * measured p90 ask age of 4.3 s. Holding the RASTERS for the same window
89248
+ * would be ~498 MB per camera and ~6 GB at peak concurrency — the OOM this
89249
+ * whole shape exists to avoid.
89250
+ *
89251
+ * A SEPARATE ceiling from `tileBudgetMb` on purpose: a scene tile is ~20× a
89252
+ * subject tile, so one shared budget would let a busy camera's key frames
89253
+ * evict the face/plate tiles the recognisers depend on. Two budgets make that
89254
+ * impossible rather than unlikely. `0` DISABLES scene tiles and restores the
89255
+ * pre-existing behaviour, where a late full-frame request had nothing but the
89256
+ * ≤640 RAM raster — which the `keyFrame` gate rejects, so in practice it had
89257
+ * nothing.
89258
+ */
89259
+ sceneBudgetMb: number().int().min(0).max(1024)
88751
89260
  });
88752
89261
  /**
88753
89262
  * The values in force when the operator has set nothing.
@@ -88763,12 +89272,14 @@ var DEFAULT_NATIVE_LEASE_SETTINGS = {
88763
89272
  budgetMb: 1024,
88764
89273
  activityMs: 15e3,
88765
89274
  tileBudgetMb: 64,
89275
+ sceneBudgetMb: 48,
88766
89276
  admission: "inferred"
88767
89277
  };
88768
89278
  DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
88769
89279
  DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
88770
89280
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
88771
89281
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
89282
+ DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
88772
89283
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
88773
89284
  var MB = 1024 * 1024;
88774
89285
  1024 * MB, 3072 * MB;