@camstack/addon-export-hap 1.2.58 → 1.2.60
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.
- package/dist/hap-export.addon.js +290 -4
- package/dist/hap-export.addon.mjs +290 -4
- package/package.json +1 -1
package/dist/hap-export.addon.js
CHANGED
|
@@ -8684,6 +8684,20 @@ var RelocateJobSchema = object({
|
|
|
8684
8684
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8685
8685
|
*/
|
|
8686
8686
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8687
|
+
/**
|
|
8688
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8689
|
+
*
|
|
8690
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8691
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8692
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8693
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8694
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8695
|
+
* the same failure as one that quietly skips them (D295).
|
|
8696
|
+
*
|
|
8697
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8698
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8699
|
+
*/
|
|
8700
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8687
8701
|
startedAt: number(),
|
|
8688
8702
|
finishedAt: number().nullable(),
|
|
8689
8703
|
error: string().nullable()
|
|
@@ -9047,6 +9061,91 @@ var RelocateResidueSchema = object({
|
|
|
9047
9061
|
segments: number().int().nonnegative(),
|
|
9048
9062
|
bytes: number().int().nonnegative()
|
|
9049
9063
|
}).nullable();
|
|
9064
|
+
/**
|
|
9065
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
9066
|
+
* (D319).
|
|
9067
|
+
*
|
|
9068
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
9069
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
9070
|
+
* destructive run before authorising it.
|
|
9071
|
+
*/
|
|
9072
|
+
var LedgerWalkInputSchema = object({
|
|
9073
|
+
locationId: string().min(1),
|
|
9074
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
9075
|
+
apply: boolean().optional(),
|
|
9076
|
+
/** Narrow to one camera. */
|
|
9077
|
+
deviceId: number().int().positive().optional(),
|
|
9078
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
9079
|
+
profiles: array(string().min(1)).optional()
|
|
9080
|
+
});
|
|
9081
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
9082
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
9083
|
+
"location-unknown",
|
|
9084
|
+
"source-writable",
|
|
9085
|
+
"no-ledger",
|
|
9086
|
+
"archive-unreadable",
|
|
9087
|
+
"anchor-absent",
|
|
9088
|
+
"anchor-unreadable",
|
|
9089
|
+
"anchor-moved"
|
|
9090
|
+
]);
|
|
9091
|
+
_enum([
|
|
9092
|
+
"live-tail",
|
|
9093
|
+
"listing-error",
|
|
9094
|
+
"path-mismatch",
|
|
9095
|
+
"durable-refused"
|
|
9096
|
+
]);
|
|
9097
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
9098
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
9099
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
9100
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
9101
|
+
"live-tail": number().int().nonnegative(),
|
|
9102
|
+
"listing-error": number().int().nonnegative(),
|
|
9103
|
+
"path-mismatch": number().int().nonnegative(),
|
|
9104
|
+
"durable-refused": number().int().nonnegative()
|
|
9105
|
+
});
|
|
9106
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
9107
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
9108
|
+
deviceId: number().int(),
|
|
9109
|
+
hoursWalked: number().int().nonnegative(),
|
|
9110
|
+
hoursMissing: number().int().nonnegative(),
|
|
9111
|
+
ghostSegments: number().int().nonnegative(),
|
|
9112
|
+
ghostBytes: number().int().nonnegative(),
|
|
9113
|
+
forgottenSegments: number().int().nonnegative(),
|
|
9114
|
+
orphanFiles: number().int().nonnegative()
|
|
9115
|
+
});
|
|
9116
|
+
/**
|
|
9117
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
9118
|
+
*
|
|
9119
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
9120
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
9121
|
+
* than in the absence of one.
|
|
9122
|
+
*/
|
|
9123
|
+
var LedgerWalkReportSchema = object({
|
|
9124
|
+
locationId: string(),
|
|
9125
|
+
applied: boolean(),
|
|
9126
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
9127
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
9128
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
9129
|
+
hoursClaimed: number().int().nonnegative(),
|
|
9130
|
+
hoursWalked: number().int().nonnegative(),
|
|
9131
|
+
hoursMissing: number().int().nonnegative(),
|
|
9132
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
9133
|
+
listings: number().int().nonnegative(),
|
|
9134
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
9135
|
+
ghostSegments: number().int().nonnegative(),
|
|
9136
|
+
ghostBytes: number().int().nonnegative(),
|
|
9137
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
9138
|
+
forgottenSegments: number().int().nonnegative(),
|
|
9139
|
+
forgottenBytes: number().int().nonnegative(),
|
|
9140
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
9141
|
+
orphanFiles: number().int().nonnegative(),
|
|
9142
|
+
orphanSample: array(string()).readonly(),
|
|
9143
|
+
hoursSkipped: number().int().nonnegative(),
|
|
9144
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
9145
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
9146
|
+
bounded: boolean(),
|
|
9147
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
9148
|
+
});
|
|
9050
9149
|
/** How many rows a media pass would still act on against a given target — the
|
|
9051
9150
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
9052
9151
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -19455,13 +19554,15 @@ var ListGroupsPageSchema = object({
|
|
|
19455
19554
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
19456
19555
|
nextCursor: string().nullable()
|
|
19457
19556
|
});
|
|
19557
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
19558
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
19458
19559
|
var KeyEventQueryInput = object({
|
|
19459
19560
|
deviceId: number(),
|
|
19460
19561
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
19461
19562
|
since: number(),
|
|
19462
19563
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
19463
19564
|
until: number(),
|
|
19464
|
-
limit: number().int().min(1).max(
|
|
19565
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19465
19566
|
/** Drop tracks scoring below this importance. */
|
|
19466
19567
|
minImportance: number().min(0).max(1).optional(),
|
|
19467
19568
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -19483,6 +19584,32 @@ var KeyEventSchema = object({
|
|
|
19483
19584
|
...TrackFlagFields,
|
|
19484
19585
|
...TrackRetrainFields
|
|
19485
19586
|
});
|
|
19587
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
19588
|
+
var KeyEventBatchQueryInput = object({
|
|
19589
|
+
deviceIds: array(number()).min(1).max(200),
|
|
19590
|
+
since: number(),
|
|
19591
|
+
until: number(),
|
|
19592
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
19593
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
19594
|
+
* rows and change what the merged feed contains. */
|
|
19595
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19596
|
+
minImportance: number().min(0).max(1).optional(),
|
|
19597
|
+
classFilter: string().optional()
|
|
19598
|
+
});
|
|
19599
|
+
/**
|
|
19600
|
+
* One camera's key events in a batch answer.
|
|
19601
|
+
*
|
|
19602
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
19603
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
19604
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
19605
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
19606
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
19607
|
+
* key, which only worked because there was one query per camera).
|
|
19608
|
+
*/
|
|
19609
|
+
var KeyEventsForDeviceSchema = object({
|
|
19610
|
+
deviceId: number(),
|
|
19611
|
+
events: array(KeyEventSchema).readonly()
|
|
19612
|
+
});
|
|
19486
19613
|
object({
|
|
19487
19614
|
trackId: string(),
|
|
19488
19615
|
className: string(),
|
|
@@ -19554,6 +19681,50 @@ var EventStoreFootprintSchema = object({
|
|
|
19554
19681
|
totalBytes: number().int(),
|
|
19555
19682
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
19556
19683
|
});
|
|
19684
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
19685
|
+
var EventMediaKindFootprintSchema = object({
|
|
19686
|
+
kind: MediaFileKindEnum,
|
|
19687
|
+
/** Media rows of this kind. */
|
|
19688
|
+
rows: number().int(),
|
|
19689
|
+
/** Bytes on disk held by those rows. */
|
|
19690
|
+
bytes: number().int()
|
|
19691
|
+
});
|
|
19692
|
+
/**
|
|
19693
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
19694
|
+
* actually turns on.
|
|
19695
|
+
*
|
|
19696
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
19697
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
19698
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
19699
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
19700
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
19701
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
19702
|
+
*
|
|
19703
|
+
* ## Why `unaccounted*` exists
|
|
19704
|
+
*
|
|
19705
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
19706
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
19707
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
19708
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
19709
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
19710
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
19711
|
+
* against a denominator smaller than the disk.
|
|
19712
|
+
*
|
|
19713
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
19714
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
19715
|
+
*/
|
|
19716
|
+
var EventMediaKindBreakdownSchema = object({
|
|
19717
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
19718
|
+
totalRows: number().int(),
|
|
19719
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
19720
|
+
totalBytes: number().int(),
|
|
19721
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
19722
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
19723
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
19724
|
+
unaccountedRows: number().int(),
|
|
19725
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
19726
|
+
unaccountedBytes: number().int()
|
|
19727
|
+
});
|
|
19557
19728
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
19558
19729
|
var EventPruneCountsSchema = object({
|
|
19559
19730
|
motion: number().int(),
|
|
@@ -19708,7 +19879,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19708
19879
|
until: number().optional(),
|
|
19709
19880
|
kinds: array(string()).optional(),
|
|
19710
19881
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
19711
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
19882
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
19712
19883
|
deviceId: number(),
|
|
19713
19884
|
since: number(),
|
|
19714
19885
|
until: number(),
|
|
@@ -19757,6 +19928,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19757
19928
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
19758
19929
|
kind: "query",
|
|
19759
19930
|
auth: "admin"
|
|
19931
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
19932
|
+
kind: "query",
|
|
19933
|
+
auth: "admin"
|
|
19760
19934
|
}), method(object({
|
|
19761
19935
|
olderThanMs: number(),
|
|
19762
19936
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -21708,6 +21882,20 @@ method(object({
|
|
|
21708
21882
|
error: string().optional()
|
|
21709
21883
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
21710
21884
|
providerId: string(),
|
|
21885
|
+
/**
|
|
21886
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
21887
|
+
*
|
|
21888
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
21889
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
21890
|
+
* credential the operator did not retype — and posting that here
|
|
21891
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
21892
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
21893
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
21894
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
21895
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
21896
|
+
* every value was typed just now and nothing is stored yet.
|
|
21897
|
+
*/
|
|
21898
|
+
locationId: string().optional(),
|
|
21711
21899
|
config: record(string(), unknown())
|
|
21712
21900
|
}), object({
|
|
21713
21901
|
ok: boolean(),
|
|
@@ -27391,6 +27579,9 @@ method(object({
|
|
|
27391
27579
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
27392
27580
|
kind: "query",
|
|
27393
27581
|
auth: "admin"
|
|
27582
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
27583
|
+
kind: "mutation",
|
|
27584
|
+
auth: "admin"
|
|
27394
27585
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
27395
27586
|
kind: "mutation",
|
|
27396
27587
|
auth: "admin"
|
|
@@ -27782,7 +27973,26 @@ var SceneMonitorStatusSchema = object({
|
|
|
27782
27973
|
monitors: array(SceneMonitorSchema),
|
|
27783
27974
|
lastFetchedAt: number()
|
|
27784
27975
|
});
|
|
27785
|
-
|
|
27976
|
+
/**
|
|
27977
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
27978
|
+
*
|
|
27979
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
27980
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
27981
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
27982
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
27983
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
27984
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
27985
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
27986
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
27987
|
+
*
|
|
27988
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
27989
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
27990
|
+
*/
|
|
27991
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
27992
|
+
deviceId: number(),
|
|
27993
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
27994
|
+
});
|
|
27995
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
|
|
27786
27996
|
deviceId: number(),
|
|
27787
27997
|
label: string(),
|
|
27788
27998
|
roi: MaskRectShapeSchema,
|
|
@@ -29131,6 +29341,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
29131
29341
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
29132
29342
|
});
|
|
29133
29343
|
/**
|
|
29344
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
29345
|
+
*
|
|
29346
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
29347
|
+
* because `snapshot: null` was already spoken for:
|
|
29348
|
+
*
|
|
29349
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
29350
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
29351
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
29352
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
29353
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
29354
|
+
*
|
|
29355
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
29356
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
29357
|
+
* which is a definite claim about a camera nobody could read.
|
|
29358
|
+
*/
|
|
29359
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
29360
|
+
deviceId: number(),
|
|
29361
|
+
read: _enum(["read", "unreadable"]),
|
|
29362
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
29363
|
+
});
|
|
29364
|
+
/**
|
|
29134
29365
|
* Time-series resolution. The history methods return one bucket per
|
|
29135
29366
|
* step over the requested range. Smaller resolutions cost more
|
|
29136
29367
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -29154,7 +29385,7 @@ var HistoryPointSchema = object({
|
|
|
29154
29385
|
/** Object count averaged over the bucket (rounded to nearest integer). */
|
|
29155
29386
|
count: number().int().nonnegative()
|
|
29156
29387
|
});
|
|
29157
|
-
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
|
|
29388
|
+
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
|
|
29158
29389
|
deviceId: number(),
|
|
29159
29390
|
zoneId: string(),
|
|
29160
29391
|
className: string().optional()
|
|
@@ -32435,6 +32666,12 @@ Object.freeze({
|
|
|
32435
32666
|
addonId: null,
|
|
32436
32667
|
access: "view"
|
|
32437
32668
|
},
|
|
32669
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
32670
|
+
capName: "pipeline-analytics",
|
|
32671
|
+
capScope: "device",
|
|
32672
|
+
addonId: null,
|
|
32673
|
+
access: "view"
|
|
32674
|
+
},
|
|
32438
32675
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
32439
32676
|
capName: "pipeline-analytics",
|
|
32440
32677
|
capScope: "device",
|
|
@@ -32453,6 +32690,12 @@ Object.freeze({
|
|
|
32453
32690
|
addonId: null,
|
|
32454
32691
|
access: "view"
|
|
32455
32692
|
},
|
|
32693
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
32694
|
+
capName: "pipeline-analytics",
|
|
32695
|
+
capScope: "device",
|
|
32696
|
+
addonId: null,
|
|
32697
|
+
access: "view"
|
|
32698
|
+
},
|
|
32456
32699
|
"pipelineAnalytics.getMotionEvents": {
|
|
32457
32700
|
capName: "pipeline-analytics",
|
|
32458
32701
|
capScope: "device",
|
|
@@ -33629,6 +33872,12 @@ Object.freeze({
|
|
|
33629
33872
|
addonId: null,
|
|
33630
33873
|
access: "view"
|
|
33631
33874
|
},
|
|
33875
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
33876
|
+
capName: "recording",
|
|
33877
|
+
capScope: "system",
|
|
33878
|
+
addonId: null,
|
|
33879
|
+
access: "create"
|
|
33880
|
+
},
|
|
33632
33881
|
"recording.refreshStorageLocationsForMigration": {
|
|
33633
33882
|
capName: "recording",
|
|
33634
33883
|
capScope: "system",
|
|
@@ -33755,6 +34004,12 @@ Object.freeze({
|
|
|
33755
34004
|
addonId: null,
|
|
33756
34005
|
access: "view"
|
|
33757
34006
|
},
|
|
34007
|
+
"sceneMonitor.listScenesBatch": {
|
|
34008
|
+
capName: "scene-monitor",
|
|
34009
|
+
capScope: "device",
|
|
34010
|
+
addonId: null,
|
|
34011
|
+
access: "view"
|
|
34012
|
+
},
|
|
33758
34013
|
"sceneMonitor.recheckNow": {
|
|
33759
34014
|
capName: "scene-monitor",
|
|
33760
34015
|
capScope: "device",
|
|
@@ -35111,6 +35366,12 @@ Object.freeze({
|
|
|
35111
35366
|
addonId: null,
|
|
35112
35367
|
access: "view"
|
|
35113
35368
|
},
|
|
35369
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
35370
|
+
capName: "zone-analytics",
|
|
35371
|
+
capScope: "device",
|
|
35372
|
+
addonId: null,
|
|
35373
|
+
access: "view"
|
|
35374
|
+
},
|
|
35114
35375
|
"zoneAnalytics.getUnzonedHistory": {
|
|
35115
35376
|
capName: "zone-analytics",
|
|
35116
35377
|
capScope: "device",
|
|
@@ -36100,6 +36361,11 @@ Object.freeze({
|
|
|
36100
36361
|
form: "single",
|
|
36101
36362
|
optional: false
|
|
36102
36363
|
}],
|
|
36364
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
36365
|
+
name: "deviceId",
|
|
36366
|
+
form: "single",
|
|
36367
|
+
optional: true
|
|
36368
|
+
}],
|
|
36103
36369
|
"pipelineAnalytics.getGroup": [{
|
|
36104
36370
|
name: "deviceId",
|
|
36105
36371
|
form: "single",
|
|
@@ -36110,6 +36376,11 @@ Object.freeze({
|
|
|
36110
36376
|
form: "single",
|
|
36111
36377
|
optional: false
|
|
36112
36378
|
}],
|
|
36379
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
36380
|
+
name: "deviceIds",
|
|
36381
|
+
form: "array",
|
|
36382
|
+
optional: false
|
|
36383
|
+
}],
|
|
36113
36384
|
"pipelineAnalytics.getMotionEvents": [{
|
|
36114
36385
|
name: "deviceId",
|
|
36115
36386
|
form: "single",
|
|
@@ -36550,6 +36821,11 @@ Object.freeze({
|
|
|
36550
36821
|
form: "single",
|
|
36551
36822
|
optional: false
|
|
36552
36823
|
}],
|
|
36824
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
36825
|
+
name: "deviceId",
|
|
36826
|
+
form: "single",
|
|
36827
|
+
optional: true
|
|
36828
|
+
}],
|
|
36553
36829
|
"recording.relocateFootage": [{
|
|
36554
36830
|
name: "deviceId",
|
|
36555
36831
|
form: "single",
|
|
@@ -36615,6 +36891,11 @@ Object.freeze({
|
|
|
36615
36891
|
form: "single",
|
|
36616
36892
|
optional: false
|
|
36617
36893
|
}],
|
|
36894
|
+
"sceneMonitor.listScenesBatch": [{
|
|
36895
|
+
name: "deviceIds",
|
|
36896
|
+
form: "array",
|
|
36897
|
+
optional: false
|
|
36898
|
+
}],
|
|
36618
36899
|
"sceneMonitor.recheckNow": [{
|
|
36619
36900
|
name: "deviceId",
|
|
36620
36901
|
form: "single",
|
|
@@ -36876,6 +37157,11 @@ Object.freeze({
|
|
|
36876
37157
|
form: "single",
|
|
36877
37158
|
optional: false
|
|
36878
37159
|
}],
|
|
37160
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
37161
|
+
name: "deviceIds",
|
|
37162
|
+
form: "array",
|
|
37163
|
+
optional: false
|
|
37164
|
+
}],
|
|
36879
37165
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
36880
37166
|
name: "deviceId",
|
|
36881
37167
|
form: "single",
|
|
@@ -8672,6 +8672,20 @@ var RelocateJobSchema = object({
|
|
|
8672
8672
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8673
8673
|
*/
|
|
8674
8674
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8675
|
+
/**
|
|
8676
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8677
|
+
*
|
|
8678
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8679
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8680
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8681
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8682
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8683
|
+
* the same failure as one that quietly skips them (D295).
|
|
8684
|
+
*
|
|
8685
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8686
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8687
|
+
*/
|
|
8688
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8675
8689
|
startedAt: number(),
|
|
8676
8690
|
finishedAt: number().nullable(),
|
|
8677
8691
|
error: string().nullable()
|
|
@@ -9035,6 +9049,91 @@ var RelocateResidueSchema = object({
|
|
|
9035
9049
|
segments: number().int().nonnegative(),
|
|
9036
9050
|
bytes: number().int().nonnegative()
|
|
9037
9051
|
}).nullable();
|
|
9052
|
+
/**
|
|
9053
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
9054
|
+
* (D319).
|
|
9055
|
+
*
|
|
9056
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
9057
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
9058
|
+
* destructive run before authorising it.
|
|
9059
|
+
*/
|
|
9060
|
+
var LedgerWalkInputSchema = object({
|
|
9061
|
+
locationId: string().min(1),
|
|
9062
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
9063
|
+
apply: boolean().optional(),
|
|
9064
|
+
/** Narrow to one camera. */
|
|
9065
|
+
deviceId: number().int().positive().optional(),
|
|
9066
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
9067
|
+
profiles: array(string().min(1)).optional()
|
|
9068
|
+
});
|
|
9069
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
9070
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
9071
|
+
"location-unknown",
|
|
9072
|
+
"source-writable",
|
|
9073
|
+
"no-ledger",
|
|
9074
|
+
"archive-unreadable",
|
|
9075
|
+
"anchor-absent",
|
|
9076
|
+
"anchor-unreadable",
|
|
9077
|
+
"anchor-moved"
|
|
9078
|
+
]);
|
|
9079
|
+
_enum([
|
|
9080
|
+
"live-tail",
|
|
9081
|
+
"listing-error",
|
|
9082
|
+
"path-mismatch",
|
|
9083
|
+
"durable-refused"
|
|
9084
|
+
]);
|
|
9085
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
9086
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
9087
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
9088
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
9089
|
+
"live-tail": number().int().nonnegative(),
|
|
9090
|
+
"listing-error": number().int().nonnegative(),
|
|
9091
|
+
"path-mismatch": number().int().nonnegative(),
|
|
9092
|
+
"durable-refused": number().int().nonnegative()
|
|
9093
|
+
});
|
|
9094
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
9095
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
9096
|
+
deviceId: number().int(),
|
|
9097
|
+
hoursWalked: number().int().nonnegative(),
|
|
9098
|
+
hoursMissing: number().int().nonnegative(),
|
|
9099
|
+
ghostSegments: number().int().nonnegative(),
|
|
9100
|
+
ghostBytes: number().int().nonnegative(),
|
|
9101
|
+
forgottenSegments: number().int().nonnegative(),
|
|
9102
|
+
orphanFiles: number().int().nonnegative()
|
|
9103
|
+
});
|
|
9104
|
+
/**
|
|
9105
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
9106
|
+
*
|
|
9107
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
9108
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
9109
|
+
* than in the absence of one.
|
|
9110
|
+
*/
|
|
9111
|
+
var LedgerWalkReportSchema = object({
|
|
9112
|
+
locationId: string(),
|
|
9113
|
+
applied: boolean(),
|
|
9114
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
9115
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
9116
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
9117
|
+
hoursClaimed: number().int().nonnegative(),
|
|
9118
|
+
hoursWalked: number().int().nonnegative(),
|
|
9119
|
+
hoursMissing: number().int().nonnegative(),
|
|
9120
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
9121
|
+
listings: number().int().nonnegative(),
|
|
9122
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
9123
|
+
ghostSegments: number().int().nonnegative(),
|
|
9124
|
+
ghostBytes: number().int().nonnegative(),
|
|
9125
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
9126
|
+
forgottenSegments: number().int().nonnegative(),
|
|
9127
|
+
forgottenBytes: number().int().nonnegative(),
|
|
9128
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
9129
|
+
orphanFiles: number().int().nonnegative(),
|
|
9130
|
+
orphanSample: array(string()).readonly(),
|
|
9131
|
+
hoursSkipped: number().int().nonnegative(),
|
|
9132
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
9133
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
9134
|
+
bounded: boolean(),
|
|
9135
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
9136
|
+
});
|
|
9038
9137
|
/** How many rows a media pass would still act on against a given target — the
|
|
9039
9138
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
9040
9139
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -19443,13 +19542,15 @@ var ListGroupsPageSchema = object({
|
|
|
19443
19542
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
19444
19543
|
nextCursor: string().nullable()
|
|
19445
19544
|
});
|
|
19545
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
19546
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
19446
19547
|
var KeyEventQueryInput = object({
|
|
19447
19548
|
deviceId: number(),
|
|
19448
19549
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
19449
19550
|
since: number(),
|
|
19450
19551
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
19451
19552
|
until: number(),
|
|
19452
|
-
limit: number().int().min(1).max(
|
|
19553
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19453
19554
|
/** Drop tracks scoring below this importance. */
|
|
19454
19555
|
minImportance: number().min(0).max(1).optional(),
|
|
19455
19556
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -19471,6 +19572,32 @@ var KeyEventSchema = object({
|
|
|
19471
19572
|
...TrackFlagFields,
|
|
19472
19573
|
...TrackRetrainFields
|
|
19473
19574
|
});
|
|
19575
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
19576
|
+
var KeyEventBatchQueryInput = object({
|
|
19577
|
+
deviceIds: array(number()).min(1).max(200),
|
|
19578
|
+
since: number(),
|
|
19579
|
+
until: number(),
|
|
19580
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
19581
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
19582
|
+
* rows and change what the merged feed contains. */
|
|
19583
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19584
|
+
minImportance: number().min(0).max(1).optional(),
|
|
19585
|
+
classFilter: string().optional()
|
|
19586
|
+
});
|
|
19587
|
+
/**
|
|
19588
|
+
* One camera's key events in a batch answer.
|
|
19589
|
+
*
|
|
19590
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
19591
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
19592
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
19593
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
19594
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
19595
|
+
* key, which only worked because there was one query per camera).
|
|
19596
|
+
*/
|
|
19597
|
+
var KeyEventsForDeviceSchema = object({
|
|
19598
|
+
deviceId: number(),
|
|
19599
|
+
events: array(KeyEventSchema).readonly()
|
|
19600
|
+
});
|
|
19474
19601
|
object({
|
|
19475
19602
|
trackId: string(),
|
|
19476
19603
|
className: string(),
|
|
@@ -19542,6 +19669,50 @@ var EventStoreFootprintSchema = object({
|
|
|
19542
19669
|
totalBytes: number().int(),
|
|
19543
19670
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
19544
19671
|
});
|
|
19672
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
19673
|
+
var EventMediaKindFootprintSchema = object({
|
|
19674
|
+
kind: MediaFileKindEnum,
|
|
19675
|
+
/** Media rows of this kind. */
|
|
19676
|
+
rows: number().int(),
|
|
19677
|
+
/** Bytes on disk held by those rows. */
|
|
19678
|
+
bytes: number().int()
|
|
19679
|
+
});
|
|
19680
|
+
/**
|
|
19681
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
19682
|
+
* actually turns on.
|
|
19683
|
+
*
|
|
19684
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
19685
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
19686
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
19687
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
19688
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
19689
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
19690
|
+
*
|
|
19691
|
+
* ## Why `unaccounted*` exists
|
|
19692
|
+
*
|
|
19693
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
19694
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
19695
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
19696
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
19697
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
19698
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
19699
|
+
* against a denominator smaller than the disk.
|
|
19700
|
+
*
|
|
19701
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
19702
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
19703
|
+
*/
|
|
19704
|
+
var EventMediaKindBreakdownSchema = object({
|
|
19705
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
19706
|
+
totalRows: number().int(),
|
|
19707
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
19708
|
+
totalBytes: number().int(),
|
|
19709
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
19710
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
19711
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
19712
|
+
unaccountedRows: number().int(),
|
|
19713
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
19714
|
+
unaccountedBytes: number().int()
|
|
19715
|
+
});
|
|
19545
19716
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
19546
19717
|
var EventPruneCountsSchema = object({
|
|
19547
19718
|
motion: number().int(),
|
|
@@ -19696,7 +19867,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19696
19867
|
until: number().optional(),
|
|
19697
19868
|
kinds: array(string()).optional(),
|
|
19698
19869
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
19699
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
19870
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
19700
19871
|
deviceId: number(),
|
|
19701
19872
|
since: number(),
|
|
19702
19873
|
until: number(),
|
|
@@ -19745,6 +19916,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19745
19916
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
19746
19917
|
kind: "query",
|
|
19747
19918
|
auth: "admin"
|
|
19919
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
19920
|
+
kind: "query",
|
|
19921
|
+
auth: "admin"
|
|
19748
19922
|
}), method(object({
|
|
19749
19923
|
olderThanMs: number(),
|
|
19750
19924
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -21696,6 +21870,20 @@ method(object({
|
|
|
21696
21870
|
error: string().optional()
|
|
21697
21871
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
21698
21872
|
providerId: string(),
|
|
21873
|
+
/**
|
|
21874
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
21875
|
+
*
|
|
21876
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
21877
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
21878
|
+
* credential the operator did not retype — and posting that here
|
|
21879
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
21880
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
21881
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
21882
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
21883
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
21884
|
+
* every value was typed just now and nothing is stored yet.
|
|
21885
|
+
*/
|
|
21886
|
+
locationId: string().optional(),
|
|
21699
21887
|
config: record(string(), unknown())
|
|
21700
21888
|
}), object({
|
|
21701
21889
|
ok: boolean(),
|
|
@@ -27379,6 +27567,9 @@ method(object({
|
|
|
27379
27567
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
27380
27568
|
kind: "query",
|
|
27381
27569
|
auth: "admin"
|
|
27570
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
27571
|
+
kind: "mutation",
|
|
27572
|
+
auth: "admin"
|
|
27382
27573
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
27383
27574
|
kind: "mutation",
|
|
27384
27575
|
auth: "admin"
|
|
@@ -27770,7 +27961,26 @@ var SceneMonitorStatusSchema = object({
|
|
|
27770
27961
|
monitors: array(SceneMonitorSchema),
|
|
27771
27962
|
lastFetchedAt: number()
|
|
27772
27963
|
});
|
|
27773
|
-
|
|
27964
|
+
/**
|
|
27965
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
27966
|
+
*
|
|
27967
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
27968
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
27969
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
27970
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
27971
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
27972
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
27973
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
27974
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
27975
|
+
*
|
|
27976
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
27977
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
27978
|
+
*/
|
|
27979
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
27980
|
+
deviceId: number(),
|
|
27981
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
27982
|
+
});
|
|
27983
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
|
|
27774
27984
|
deviceId: number(),
|
|
27775
27985
|
label: string(),
|
|
27776
27986
|
roi: MaskRectShapeSchema,
|
|
@@ -29119,6 +29329,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
29119
29329
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
29120
29330
|
});
|
|
29121
29331
|
/**
|
|
29332
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
29333
|
+
*
|
|
29334
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
29335
|
+
* because `snapshot: null` was already spoken for:
|
|
29336
|
+
*
|
|
29337
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
29338
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
29339
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
29340
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
29341
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
29342
|
+
*
|
|
29343
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
29344
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
29345
|
+
* which is a definite claim about a camera nobody could read.
|
|
29346
|
+
*/
|
|
29347
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
29348
|
+
deviceId: number(),
|
|
29349
|
+
read: _enum(["read", "unreadable"]),
|
|
29350
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
29351
|
+
});
|
|
29352
|
+
/**
|
|
29122
29353
|
* Time-series resolution. The history methods return one bucket per
|
|
29123
29354
|
* step over the requested range. Smaller resolutions cost more
|
|
29124
29355
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -29142,7 +29373,7 @@ var HistoryPointSchema = object({
|
|
|
29142
29373
|
/** Object count averaged over the bucket (rounded to nearest integer). */
|
|
29143
29374
|
count: number().int().nonnegative()
|
|
29144
29375
|
});
|
|
29145
|
-
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
|
|
29376
|
+
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
|
|
29146
29377
|
deviceId: number(),
|
|
29147
29378
|
zoneId: string(),
|
|
29148
29379
|
className: string().optional()
|
|
@@ -32423,6 +32654,12 @@ Object.freeze({
|
|
|
32423
32654
|
addonId: null,
|
|
32424
32655
|
access: "view"
|
|
32425
32656
|
},
|
|
32657
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
32658
|
+
capName: "pipeline-analytics",
|
|
32659
|
+
capScope: "device",
|
|
32660
|
+
addonId: null,
|
|
32661
|
+
access: "view"
|
|
32662
|
+
},
|
|
32426
32663
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
32427
32664
|
capName: "pipeline-analytics",
|
|
32428
32665
|
capScope: "device",
|
|
@@ -32441,6 +32678,12 @@ Object.freeze({
|
|
|
32441
32678
|
addonId: null,
|
|
32442
32679
|
access: "view"
|
|
32443
32680
|
},
|
|
32681
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
32682
|
+
capName: "pipeline-analytics",
|
|
32683
|
+
capScope: "device",
|
|
32684
|
+
addonId: null,
|
|
32685
|
+
access: "view"
|
|
32686
|
+
},
|
|
32444
32687
|
"pipelineAnalytics.getMotionEvents": {
|
|
32445
32688
|
capName: "pipeline-analytics",
|
|
32446
32689
|
capScope: "device",
|
|
@@ -33617,6 +33860,12 @@ Object.freeze({
|
|
|
33617
33860
|
addonId: null,
|
|
33618
33861
|
access: "view"
|
|
33619
33862
|
},
|
|
33863
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
33864
|
+
capName: "recording",
|
|
33865
|
+
capScope: "system",
|
|
33866
|
+
addonId: null,
|
|
33867
|
+
access: "create"
|
|
33868
|
+
},
|
|
33620
33869
|
"recording.refreshStorageLocationsForMigration": {
|
|
33621
33870
|
capName: "recording",
|
|
33622
33871
|
capScope: "system",
|
|
@@ -33743,6 +33992,12 @@ Object.freeze({
|
|
|
33743
33992
|
addonId: null,
|
|
33744
33993
|
access: "view"
|
|
33745
33994
|
},
|
|
33995
|
+
"sceneMonitor.listScenesBatch": {
|
|
33996
|
+
capName: "scene-monitor",
|
|
33997
|
+
capScope: "device",
|
|
33998
|
+
addonId: null,
|
|
33999
|
+
access: "view"
|
|
34000
|
+
},
|
|
33746
34001
|
"sceneMonitor.recheckNow": {
|
|
33747
34002
|
capName: "scene-monitor",
|
|
33748
34003
|
capScope: "device",
|
|
@@ -35099,6 +35354,12 @@ Object.freeze({
|
|
|
35099
35354
|
addonId: null,
|
|
35100
35355
|
access: "view"
|
|
35101
35356
|
},
|
|
35357
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
35358
|
+
capName: "zone-analytics",
|
|
35359
|
+
capScope: "device",
|
|
35360
|
+
addonId: null,
|
|
35361
|
+
access: "view"
|
|
35362
|
+
},
|
|
35102
35363
|
"zoneAnalytics.getUnzonedHistory": {
|
|
35103
35364
|
capName: "zone-analytics",
|
|
35104
35365
|
capScope: "device",
|
|
@@ -36088,6 +36349,11 @@ Object.freeze({
|
|
|
36088
36349
|
form: "single",
|
|
36089
36350
|
optional: false
|
|
36090
36351
|
}],
|
|
36352
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
36353
|
+
name: "deviceId",
|
|
36354
|
+
form: "single",
|
|
36355
|
+
optional: true
|
|
36356
|
+
}],
|
|
36091
36357
|
"pipelineAnalytics.getGroup": [{
|
|
36092
36358
|
name: "deviceId",
|
|
36093
36359
|
form: "single",
|
|
@@ -36098,6 +36364,11 @@ Object.freeze({
|
|
|
36098
36364
|
form: "single",
|
|
36099
36365
|
optional: false
|
|
36100
36366
|
}],
|
|
36367
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
36368
|
+
name: "deviceIds",
|
|
36369
|
+
form: "array",
|
|
36370
|
+
optional: false
|
|
36371
|
+
}],
|
|
36101
36372
|
"pipelineAnalytics.getMotionEvents": [{
|
|
36102
36373
|
name: "deviceId",
|
|
36103
36374
|
form: "single",
|
|
@@ -36538,6 +36809,11 @@ Object.freeze({
|
|
|
36538
36809
|
form: "single",
|
|
36539
36810
|
optional: false
|
|
36540
36811
|
}],
|
|
36812
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
36813
|
+
name: "deviceId",
|
|
36814
|
+
form: "single",
|
|
36815
|
+
optional: true
|
|
36816
|
+
}],
|
|
36541
36817
|
"recording.relocateFootage": [{
|
|
36542
36818
|
name: "deviceId",
|
|
36543
36819
|
form: "single",
|
|
@@ -36603,6 +36879,11 @@ Object.freeze({
|
|
|
36603
36879
|
form: "single",
|
|
36604
36880
|
optional: false
|
|
36605
36881
|
}],
|
|
36882
|
+
"sceneMonitor.listScenesBatch": [{
|
|
36883
|
+
name: "deviceIds",
|
|
36884
|
+
form: "array",
|
|
36885
|
+
optional: false
|
|
36886
|
+
}],
|
|
36606
36887
|
"sceneMonitor.recheckNow": [{
|
|
36607
36888
|
name: "deviceId",
|
|
36608
36889
|
form: "single",
|
|
@@ -36864,6 +37145,11 @@ Object.freeze({
|
|
|
36864
37145
|
form: "single",
|
|
36865
37146
|
optional: false
|
|
36866
37147
|
}],
|
|
37148
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
37149
|
+
name: "deviceIds",
|
|
37150
|
+
form: "array",
|
|
37151
|
+
optional: false
|
|
37152
|
+
}],
|
|
36867
37153
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
36868
37154
|
name: "deviceId",
|
|
36869
37155
|
form: "single",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-export-hap",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.60",
|
|
4
4
|
"description": "HomeKit (HAP) exporter for CamStack devices. Publishes each exposed device as its own HomeKit accessory: cameras and doorbells with SRTP streaming, HomeKit Secure Video, motion, two-way audio, PTZ and battery; switches, lights, locks and sensors through a capability→service table.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|