@camstack/addon-provider-wyze 0.2.51 → 0.2.53
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/addon.js +318 -2
- package/dist/addon.mjs +318 -2
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -8050,6 +8050,20 @@ var RelocateJobSchema = object({
|
|
|
8050
8050
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8051
8051
|
*/
|
|
8052
8052
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8053
|
+
/**
|
|
8054
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8055
|
+
*
|
|
8056
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8057
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8058
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8059
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8060
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8061
|
+
* the same failure as one that quietly skips them (D295).
|
|
8062
|
+
*
|
|
8063
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8064
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8065
|
+
*/
|
|
8066
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8053
8067
|
startedAt: number(),
|
|
8054
8068
|
finishedAt: number().nullable(),
|
|
8055
8069
|
error: string().nullable()
|
|
@@ -8413,6 +8427,91 @@ var RelocateResidueSchema = object({
|
|
|
8413
8427
|
segments: number().int().nonnegative(),
|
|
8414
8428
|
bytes: number().int().nonnegative()
|
|
8415
8429
|
}).nullable();
|
|
8430
|
+
/**
|
|
8431
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
8432
|
+
* (D319).
|
|
8433
|
+
*
|
|
8434
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
8435
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
8436
|
+
* destructive run before authorising it.
|
|
8437
|
+
*/
|
|
8438
|
+
var LedgerWalkInputSchema = object({
|
|
8439
|
+
locationId: string().min(1),
|
|
8440
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
8441
|
+
apply: boolean().optional(),
|
|
8442
|
+
/** Narrow to one camera. */
|
|
8443
|
+
deviceId: number().int().positive().optional(),
|
|
8444
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
8445
|
+
profiles: array(string().min(1)).optional()
|
|
8446
|
+
});
|
|
8447
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
8448
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
8449
|
+
"location-unknown",
|
|
8450
|
+
"source-writable",
|
|
8451
|
+
"no-ledger",
|
|
8452
|
+
"archive-unreadable",
|
|
8453
|
+
"anchor-absent",
|
|
8454
|
+
"anchor-unreadable",
|
|
8455
|
+
"anchor-moved"
|
|
8456
|
+
]);
|
|
8457
|
+
_enum([
|
|
8458
|
+
"live-tail",
|
|
8459
|
+
"listing-error",
|
|
8460
|
+
"path-mismatch",
|
|
8461
|
+
"durable-refused"
|
|
8462
|
+
]);
|
|
8463
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
8464
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
8465
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
8466
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
8467
|
+
"live-tail": number().int().nonnegative(),
|
|
8468
|
+
"listing-error": number().int().nonnegative(),
|
|
8469
|
+
"path-mismatch": number().int().nonnegative(),
|
|
8470
|
+
"durable-refused": number().int().nonnegative()
|
|
8471
|
+
});
|
|
8472
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
8473
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
8474
|
+
deviceId: number().int(),
|
|
8475
|
+
hoursWalked: number().int().nonnegative(),
|
|
8476
|
+
hoursMissing: number().int().nonnegative(),
|
|
8477
|
+
ghostSegments: number().int().nonnegative(),
|
|
8478
|
+
ghostBytes: number().int().nonnegative(),
|
|
8479
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8480
|
+
orphanFiles: number().int().nonnegative()
|
|
8481
|
+
});
|
|
8482
|
+
/**
|
|
8483
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
8484
|
+
*
|
|
8485
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
8486
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
8487
|
+
* than in the absence of one.
|
|
8488
|
+
*/
|
|
8489
|
+
var LedgerWalkReportSchema = object({
|
|
8490
|
+
locationId: string(),
|
|
8491
|
+
applied: boolean(),
|
|
8492
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
8493
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
8494
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
8495
|
+
hoursClaimed: number().int().nonnegative(),
|
|
8496
|
+
hoursWalked: number().int().nonnegative(),
|
|
8497
|
+
hoursMissing: number().int().nonnegative(),
|
|
8498
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
8499
|
+
listings: number().int().nonnegative(),
|
|
8500
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
8501
|
+
ghostSegments: number().int().nonnegative(),
|
|
8502
|
+
ghostBytes: number().int().nonnegative(),
|
|
8503
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
8504
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8505
|
+
forgottenBytes: number().int().nonnegative(),
|
|
8506
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
8507
|
+
orphanFiles: number().int().nonnegative(),
|
|
8508
|
+
orphanSample: array(string()).readonly(),
|
|
8509
|
+
hoursSkipped: number().int().nonnegative(),
|
|
8510
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
8511
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
8512
|
+
bounded: boolean(),
|
|
8513
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
8514
|
+
});
|
|
8416
8515
|
/** How many rows a media pass would still act on against a given target — the
|
|
8417
8516
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
8418
8517
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -19045,13 +19144,15 @@ var ListGroupsPageSchema = object({
|
|
|
19045
19144
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
19046
19145
|
nextCursor: string().nullable()
|
|
19047
19146
|
});
|
|
19147
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
19148
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
19048
19149
|
var KeyEventQueryInput = object({
|
|
19049
19150
|
deviceId: number(),
|
|
19050
19151
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
19051
19152
|
since: number(),
|
|
19052
19153
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
19053
19154
|
until: number(),
|
|
19054
|
-
limit: number().int().min(1).max(
|
|
19155
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19055
19156
|
/** Drop tracks scoring below this importance. */
|
|
19056
19157
|
minImportance: number().min(0).max(1).optional(),
|
|
19057
19158
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -19073,6 +19174,32 @@ var KeyEventSchema = object({
|
|
|
19073
19174
|
...TrackFlagFields,
|
|
19074
19175
|
...TrackRetrainFields
|
|
19075
19176
|
});
|
|
19177
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
19178
|
+
var KeyEventBatchQueryInput = object({
|
|
19179
|
+
deviceIds: array(number()).min(1).max(200),
|
|
19180
|
+
since: number(),
|
|
19181
|
+
until: number(),
|
|
19182
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
19183
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
19184
|
+
* rows and change what the merged feed contains. */
|
|
19185
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19186
|
+
minImportance: number().min(0).max(1).optional(),
|
|
19187
|
+
classFilter: string().optional()
|
|
19188
|
+
});
|
|
19189
|
+
/**
|
|
19190
|
+
* One camera's key events in a batch answer.
|
|
19191
|
+
*
|
|
19192
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
19193
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
19194
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
19195
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
19196
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
19197
|
+
* key, which only worked because there was one query per camera).
|
|
19198
|
+
*/
|
|
19199
|
+
var KeyEventsForDeviceSchema = object({
|
|
19200
|
+
deviceId: number(),
|
|
19201
|
+
events: array(KeyEventSchema).readonly()
|
|
19202
|
+
});
|
|
19076
19203
|
object({
|
|
19077
19204
|
trackId: string(),
|
|
19078
19205
|
className: string(),
|
|
@@ -19144,6 +19271,50 @@ var EventStoreFootprintSchema = object({
|
|
|
19144
19271
|
totalBytes: number().int(),
|
|
19145
19272
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
19146
19273
|
});
|
|
19274
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
19275
|
+
var EventMediaKindFootprintSchema = object({
|
|
19276
|
+
kind: MediaFileKindEnum,
|
|
19277
|
+
/** Media rows of this kind. */
|
|
19278
|
+
rows: number().int(),
|
|
19279
|
+
/** Bytes on disk held by those rows. */
|
|
19280
|
+
bytes: number().int()
|
|
19281
|
+
});
|
|
19282
|
+
/**
|
|
19283
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
19284
|
+
* actually turns on.
|
|
19285
|
+
*
|
|
19286
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
19287
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
19288
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
19289
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
19290
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
19291
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
19292
|
+
*
|
|
19293
|
+
* ## Why `unaccounted*` exists
|
|
19294
|
+
*
|
|
19295
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
19296
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
19297
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
19298
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
19299
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
19300
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
19301
|
+
* against a denominator smaller than the disk.
|
|
19302
|
+
*
|
|
19303
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
19304
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
19305
|
+
*/
|
|
19306
|
+
var EventMediaKindBreakdownSchema = object({
|
|
19307
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
19308
|
+
totalRows: number().int(),
|
|
19309
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
19310
|
+
totalBytes: number().int(),
|
|
19311
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
19312
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
19313
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
19314
|
+
unaccountedRows: number().int(),
|
|
19315
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
19316
|
+
unaccountedBytes: number().int()
|
|
19317
|
+
});
|
|
19147
19318
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
19148
19319
|
var EventPruneCountsSchema = object({
|
|
19149
19320
|
motion: number().int(),
|
|
@@ -19298,7 +19469,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19298
19469
|
until: number().optional(),
|
|
19299
19470
|
kinds: array(string()).optional(),
|
|
19300
19471
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
19301
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
19472
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
19302
19473
|
deviceId: number(),
|
|
19303
19474
|
since: number(),
|
|
19304
19475
|
until: number(),
|
|
@@ -19347,6 +19518,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19347
19518
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
19348
19519
|
kind: "query",
|
|
19349
19520
|
auth: "admin"
|
|
19521
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
19522
|
+
kind: "query",
|
|
19523
|
+
auth: "admin"
|
|
19350
19524
|
}), method(object({
|
|
19351
19525
|
olderThanMs: number(),
|
|
19352
19526
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -21402,6 +21576,20 @@ method(object({
|
|
|
21402
21576
|
error: string().optional()
|
|
21403
21577
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
21404
21578
|
providerId: string(),
|
|
21579
|
+
/**
|
|
21580
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
21581
|
+
*
|
|
21582
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
21583
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
21584
|
+
* credential the operator did not retype — and posting that here
|
|
21585
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
21586
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
21587
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
21588
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
21589
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
21590
|
+
* every value was typed just now and nothing is stored yet.
|
|
21591
|
+
*/
|
|
21592
|
+
locationId: string().optional(),
|
|
21405
21593
|
config: record(string(), unknown())
|
|
21406
21594
|
}), object({
|
|
21407
21595
|
ok: boolean(),
|
|
@@ -28760,6 +28948,9 @@ method(object({
|
|
|
28760
28948
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
28761
28949
|
kind: "query",
|
|
28762
28950
|
auth: "admin"
|
|
28951
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
28952
|
+
kind: "mutation",
|
|
28953
|
+
auth: "admin"
|
|
28763
28954
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
28764
28955
|
kind: "mutation",
|
|
28765
28956
|
auth: "admin"
|
|
@@ -29151,6 +29342,25 @@ var SceneMonitorStatusSchema = object({
|
|
|
29151
29342
|
monitors: array(SceneMonitorSchema),
|
|
29152
29343
|
lastFetchedAt: number()
|
|
29153
29344
|
});
|
|
29345
|
+
/**
|
|
29346
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
29347
|
+
*
|
|
29348
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
29349
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
29350
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
29351
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
29352
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
29353
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
29354
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
29355
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
29356
|
+
*
|
|
29357
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
29358
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
29359
|
+
*/
|
|
29360
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
29361
|
+
deviceId: number(),
|
|
29362
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
29363
|
+
});
|
|
29154
29364
|
var sceneMonitorCapability = {
|
|
29155
29365
|
name: "scene-monitor",
|
|
29156
29366
|
scope: "device",
|
|
@@ -29160,6 +29370,22 @@ var sceneMonitorCapability = {
|
|
|
29160
29370
|
deviceTypes: [DeviceType.Camera],
|
|
29161
29371
|
methods: {
|
|
29162
29372
|
listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
|
|
29373
|
+
/**
|
|
29374
|
+
* The same answer, for a SET of cameras, in one round trip.
|
|
29375
|
+
*
|
|
29376
|
+
* `/scenes` renders every camera and re-reads them on a 30s safety-net
|
|
29377
|
+
* poll behind the push slice. Fanned out client-side that was one query
|
|
29378
|
+
* per camera — 29 round trips through the browser, the hub and the
|
|
29379
|
+
* post-analysis runner every 30 seconds to read an in-memory map the
|
|
29380
|
+
* owner had already merged. The work is unchanged (`statusFor` per
|
|
29381
|
+
* device, all in-process at the owner); what collapses is the transport.
|
|
29382
|
+
*
|
|
29383
|
+
* A camera that cannot answer still gets a row, with `status: null` —
|
|
29384
|
+
* see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
|
|
29385
|
+
* ids: a caller that asked for twenty-nine and got twenty-seven cannot
|
|
29386
|
+
* tell which two are missing, or that any are.
|
|
29387
|
+
*/
|
|
29388
|
+
listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
|
|
29163
29389
|
createScene: method(object({
|
|
29164
29390
|
deviceId: number(),
|
|
29165
29391
|
label: string(),
|
|
@@ -30995,6 +31221,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
30995
31221
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
30996
31222
|
});
|
|
30997
31223
|
/**
|
|
31224
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
31225
|
+
*
|
|
31226
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
31227
|
+
* because `snapshot: null` was already spoken for:
|
|
31228
|
+
*
|
|
31229
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
31230
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
31231
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
31232
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
31233
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
31234
|
+
*
|
|
31235
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
31236
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
31237
|
+
* which is a definite claim about a camera nobody could read.
|
|
31238
|
+
*/
|
|
31239
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
31240
|
+
deviceId: number(),
|
|
31241
|
+
read: _enum(["read", "unreadable"]),
|
|
31242
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
31243
|
+
});
|
|
31244
|
+
/**
|
|
30998
31245
|
* Time-series resolution. The history methods return one bucket per
|
|
30999
31246
|
* step over the requested range. Smaller resolutions cost more
|
|
31000
31247
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -31051,6 +31298,20 @@ var zoneAnalyticsCapability = {
|
|
|
31051
31298
|
* (no inference result emitted since boot or since binding was
|
|
31052
31299
|
* activated). */
|
|
31053
31300
|
getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
|
|
31301
|
+
/**
|
|
31302
|
+
* The same snapshot, for a SET of cameras, in one round trip.
|
|
31303
|
+
*
|
|
31304
|
+
* The Events page's Stationary section polls this every 15s for every
|
|
31305
|
+
* selected camera. Fanned out client-side that is one query per camera to
|
|
31306
|
+
* read a `Map.get` at the owner — the answer costs nothing, the round trip
|
|
31307
|
+
* costs everything. Batched, N transports become one and the per-device
|
|
31308
|
+
* work is unchanged.
|
|
31309
|
+
*
|
|
31310
|
+
* Every requested deviceId gets a row, tagged `read` — see
|
|
31311
|
+
* {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
|
|
31312
|
+
* not answer for is `'unreadable'`, never an empty reading.
|
|
31313
|
+
*/
|
|
31314
|
+
getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
|
|
31054
31315
|
/** Time-series object count inside one zone. `className` optional —
|
|
31055
31316
|
* omit to count every class in the zone. */
|
|
31056
31317
|
getZoneHistory: method(object({
|
|
@@ -35448,6 +35709,12 @@ Object.freeze({
|
|
|
35448
35709
|
addonId: null,
|
|
35449
35710
|
access: "view"
|
|
35450
35711
|
},
|
|
35712
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
35713
|
+
capName: "pipeline-analytics",
|
|
35714
|
+
capScope: "device",
|
|
35715
|
+
addonId: null,
|
|
35716
|
+
access: "view"
|
|
35717
|
+
},
|
|
35451
35718
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
35452
35719
|
capName: "pipeline-analytics",
|
|
35453
35720
|
capScope: "device",
|
|
@@ -35466,6 +35733,12 @@ Object.freeze({
|
|
|
35466
35733
|
addonId: null,
|
|
35467
35734
|
access: "view"
|
|
35468
35735
|
},
|
|
35736
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
35737
|
+
capName: "pipeline-analytics",
|
|
35738
|
+
capScope: "device",
|
|
35739
|
+
addonId: null,
|
|
35740
|
+
access: "view"
|
|
35741
|
+
},
|
|
35469
35742
|
"pipelineAnalytics.getMotionEvents": {
|
|
35470
35743
|
capName: "pipeline-analytics",
|
|
35471
35744
|
capScope: "device",
|
|
@@ -36642,6 +36915,12 @@ Object.freeze({
|
|
|
36642
36915
|
addonId: null,
|
|
36643
36916
|
access: "view"
|
|
36644
36917
|
},
|
|
36918
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
36919
|
+
capName: "recording",
|
|
36920
|
+
capScope: "system",
|
|
36921
|
+
addonId: null,
|
|
36922
|
+
access: "create"
|
|
36923
|
+
},
|
|
36645
36924
|
"recording.refreshStorageLocationsForMigration": {
|
|
36646
36925
|
capName: "recording",
|
|
36647
36926
|
capScope: "system",
|
|
@@ -36768,6 +37047,12 @@ Object.freeze({
|
|
|
36768
37047
|
addonId: null,
|
|
36769
37048
|
access: "view"
|
|
36770
37049
|
},
|
|
37050
|
+
"sceneMonitor.listScenesBatch": {
|
|
37051
|
+
capName: "scene-monitor",
|
|
37052
|
+
capScope: "device",
|
|
37053
|
+
addonId: null,
|
|
37054
|
+
access: "view"
|
|
37055
|
+
},
|
|
36771
37056
|
"sceneMonitor.recheckNow": {
|
|
36772
37057
|
capName: "scene-monitor",
|
|
36773
37058
|
capScope: "device",
|
|
@@ -38124,6 +38409,12 @@ Object.freeze({
|
|
|
38124
38409
|
addonId: null,
|
|
38125
38410
|
access: "view"
|
|
38126
38411
|
},
|
|
38412
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
38413
|
+
capName: "zone-analytics",
|
|
38414
|
+
capScope: "device",
|
|
38415
|
+
addonId: null,
|
|
38416
|
+
access: "view"
|
|
38417
|
+
},
|
|
38127
38418
|
"zoneAnalytics.getUnzonedHistory": {
|
|
38128
38419
|
capName: "zone-analytics",
|
|
38129
38420
|
capScope: "device",
|
|
@@ -39113,6 +39404,11 @@ Object.freeze({
|
|
|
39113
39404
|
form: "single",
|
|
39114
39405
|
optional: false
|
|
39115
39406
|
}],
|
|
39407
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
39408
|
+
name: "deviceId",
|
|
39409
|
+
form: "single",
|
|
39410
|
+
optional: true
|
|
39411
|
+
}],
|
|
39116
39412
|
"pipelineAnalytics.getGroup": [{
|
|
39117
39413
|
name: "deviceId",
|
|
39118
39414
|
form: "single",
|
|
@@ -39123,6 +39419,11 @@ Object.freeze({
|
|
|
39123
39419
|
form: "single",
|
|
39124
39420
|
optional: false
|
|
39125
39421
|
}],
|
|
39422
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
39423
|
+
name: "deviceIds",
|
|
39424
|
+
form: "array",
|
|
39425
|
+
optional: false
|
|
39426
|
+
}],
|
|
39126
39427
|
"pipelineAnalytics.getMotionEvents": [{
|
|
39127
39428
|
name: "deviceId",
|
|
39128
39429
|
form: "single",
|
|
@@ -39563,6 +39864,11 @@ Object.freeze({
|
|
|
39563
39864
|
form: "single",
|
|
39564
39865
|
optional: false
|
|
39565
39866
|
}],
|
|
39867
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
39868
|
+
name: "deviceId",
|
|
39869
|
+
form: "single",
|
|
39870
|
+
optional: true
|
|
39871
|
+
}],
|
|
39566
39872
|
"recording.relocateFootage": [{
|
|
39567
39873
|
name: "deviceId",
|
|
39568
39874
|
form: "single",
|
|
@@ -39628,6 +39934,11 @@ Object.freeze({
|
|
|
39628
39934
|
form: "single",
|
|
39629
39935
|
optional: false
|
|
39630
39936
|
}],
|
|
39937
|
+
"sceneMonitor.listScenesBatch": [{
|
|
39938
|
+
name: "deviceIds",
|
|
39939
|
+
form: "array",
|
|
39940
|
+
optional: false
|
|
39941
|
+
}],
|
|
39631
39942
|
"sceneMonitor.recheckNow": [{
|
|
39632
39943
|
name: "deviceId",
|
|
39633
39944
|
form: "single",
|
|
@@ -39889,6 +40200,11 @@ Object.freeze({
|
|
|
39889
40200
|
form: "single",
|
|
39890
40201
|
optional: false
|
|
39891
40202
|
}],
|
|
40203
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
40204
|
+
name: "deviceIds",
|
|
40205
|
+
form: "array",
|
|
40206
|
+
optional: false
|
|
40207
|
+
}],
|
|
39892
40208
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
39893
40209
|
name: "deviceId",
|
|
39894
40210
|
form: "single",
|
package/dist/addon.mjs
CHANGED
|
@@ -8029,6 +8029,20 @@ var RelocateJobSchema = object({
|
|
|
8029
8029
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8030
8030
|
*/
|
|
8031
8031
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8032
|
+
/**
|
|
8033
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8034
|
+
*
|
|
8035
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8036
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8037
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8038
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8039
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8040
|
+
* the same failure as one that quietly skips them (D295).
|
|
8041
|
+
*
|
|
8042
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8043
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8044
|
+
*/
|
|
8045
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8032
8046
|
startedAt: number(),
|
|
8033
8047
|
finishedAt: number().nullable(),
|
|
8034
8048
|
error: string().nullable()
|
|
@@ -8392,6 +8406,91 @@ var RelocateResidueSchema = object({
|
|
|
8392
8406
|
segments: number().int().nonnegative(),
|
|
8393
8407
|
bytes: number().int().nonnegative()
|
|
8394
8408
|
}).nullable();
|
|
8409
|
+
/**
|
|
8410
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
8411
|
+
* (D319).
|
|
8412
|
+
*
|
|
8413
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
8414
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
8415
|
+
* destructive run before authorising it.
|
|
8416
|
+
*/
|
|
8417
|
+
var LedgerWalkInputSchema = object({
|
|
8418
|
+
locationId: string().min(1),
|
|
8419
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
8420
|
+
apply: boolean().optional(),
|
|
8421
|
+
/** Narrow to one camera. */
|
|
8422
|
+
deviceId: number().int().positive().optional(),
|
|
8423
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
8424
|
+
profiles: array(string().min(1)).optional()
|
|
8425
|
+
});
|
|
8426
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
8427
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
8428
|
+
"location-unknown",
|
|
8429
|
+
"source-writable",
|
|
8430
|
+
"no-ledger",
|
|
8431
|
+
"archive-unreadable",
|
|
8432
|
+
"anchor-absent",
|
|
8433
|
+
"anchor-unreadable",
|
|
8434
|
+
"anchor-moved"
|
|
8435
|
+
]);
|
|
8436
|
+
_enum([
|
|
8437
|
+
"live-tail",
|
|
8438
|
+
"listing-error",
|
|
8439
|
+
"path-mismatch",
|
|
8440
|
+
"durable-refused"
|
|
8441
|
+
]);
|
|
8442
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
8443
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
8444
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
8445
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
8446
|
+
"live-tail": number().int().nonnegative(),
|
|
8447
|
+
"listing-error": number().int().nonnegative(),
|
|
8448
|
+
"path-mismatch": number().int().nonnegative(),
|
|
8449
|
+
"durable-refused": number().int().nonnegative()
|
|
8450
|
+
});
|
|
8451
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
8452
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
8453
|
+
deviceId: number().int(),
|
|
8454
|
+
hoursWalked: number().int().nonnegative(),
|
|
8455
|
+
hoursMissing: number().int().nonnegative(),
|
|
8456
|
+
ghostSegments: number().int().nonnegative(),
|
|
8457
|
+
ghostBytes: number().int().nonnegative(),
|
|
8458
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8459
|
+
orphanFiles: number().int().nonnegative()
|
|
8460
|
+
});
|
|
8461
|
+
/**
|
|
8462
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
8463
|
+
*
|
|
8464
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
8465
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
8466
|
+
* than in the absence of one.
|
|
8467
|
+
*/
|
|
8468
|
+
var LedgerWalkReportSchema = object({
|
|
8469
|
+
locationId: string(),
|
|
8470
|
+
applied: boolean(),
|
|
8471
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
8472
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
8473
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
8474
|
+
hoursClaimed: number().int().nonnegative(),
|
|
8475
|
+
hoursWalked: number().int().nonnegative(),
|
|
8476
|
+
hoursMissing: number().int().nonnegative(),
|
|
8477
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
8478
|
+
listings: number().int().nonnegative(),
|
|
8479
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
8480
|
+
ghostSegments: number().int().nonnegative(),
|
|
8481
|
+
ghostBytes: number().int().nonnegative(),
|
|
8482
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
8483
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8484
|
+
forgottenBytes: number().int().nonnegative(),
|
|
8485
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
8486
|
+
orphanFiles: number().int().nonnegative(),
|
|
8487
|
+
orphanSample: array(string()).readonly(),
|
|
8488
|
+
hoursSkipped: number().int().nonnegative(),
|
|
8489
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
8490
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
8491
|
+
bounded: boolean(),
|
|
8492
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
8493
|
+
});
|
|
8395
8494
|
/** How many rows a media pass would still act on against a given target — the
|
|
8396
8495
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
8397
8496
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -19024,13 +19123,15 @@ var ListGroupsPageSchema = object({
|
|
|
19024
19123
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
19025
19124
|
nextCursor: string().nullable()
|
|
19026
19125
|
});
|
|
19126
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
19127
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
19027
19128
|
var KeyEventQueryInput = object({
|
|
19028
19129
|
deviceId: number(),
|
|
19029
19130
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
19030
19131
|
since: number(),
|
|
19031
19132
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
19032
19133
|
until: number(),
|
|
19033
|
-
limit: number().int().min(1).max(
|
|
19134
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19034
19135
|
/** Drop tracks scoring below this importance. */
|
|
19035
19136
|
minImportance: number().min(0).max(1).optional(),
|
|
19036
19137
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -19052,6 +19153,32 @@ var KeyEventSchema = object({
|
|
|
19052
19153
|
...TrackFlagFields,
|
|
19053
19154
|
...TrackRetrainFields
|
|
19054
19155
|
});
|
|
19156
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
19157
|
+
var KeyEventBatchQueryInput = object({
|
|
19158
|
+
deviceIds: array(number()).min(1).max(200),
|
|
19159
|
+
since: number(),
|
|
19160
|
+
until: number(),
|
|
19161
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
19162
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
19163
|
+
* rows and change what the merged feed contains. */
|
|
19164
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19165
|
+
minImportance: number().min(0).max(1).optional(),
|
|
19166
|
+
classFilter: string().optional()
|
|
19167
|
+
});
|
|
19168
|
+
/**
|
|
19169
|
+
* One camera's key events in a batch answer.
|
|
19170
|
+
*
|
|
19171
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
19172
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
19173
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
19174
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
19175
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
19176
|
+
* key, which only worked because there was one query per camera).
|
|
19177
|
+
*/
|
|
19178
|
+
var KeyEventsForDeviceSchema = object({
|
|
19179
|
+
deviceId: number(),
|
|
19180
|
+
events: array(KeyEventSchema).readonly()
|
|
19181
|
+
});
|
|
19055
19182
|
object({
|
|
19056
19183
|
trackId: string(),
|
|
19057
19184
|
className: string(),
|
|
@@ -19123,6 +19250,50 @@ var EventStoreFootprintSchema = object({
|
|
|
19123
19250
|
totalBytes: number().int(),
|
|
19124
19251
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
19125
19252
|
});
|
|
19253
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
19254
|
+
var EventMediaKindFootprintSchema = object({
|
|
19255
|
+
kind: MediaFileKindEnum,
|
|
19256
|
+
/** Media rows of this kind. */
|
|
19257
|
+
rows: number().int(),
|
|
19258
|
+
/** Bytes on disk held by those rows. */
|
|
19259
|
+
bytes: number().int()
|
|
19260
|
+
});
|
|
19261
|
+
/**
|
|
19262
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
19263
|
+
* actually turns on.
|
|
19264
|
+
*
|
|
19265
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
19266
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
19267
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
19268
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
19269
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
19270
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
19271
|
+
*
|
|
19272
|
+
* ## Why `unaccounted*` exists
|
|
19273
|
+
*
|
|
19274
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
19275
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
19276
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
19277
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
19278
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
19279
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
19280
|
+
* against a denominator smaller than the disk.
|
|
19281
|
+
*
|
|
19282
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
19283
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
19284
|
+
*/
|
|
19285
|
+
var EventMediaKindBreakdownSchema = object({
|
|
19286
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
19287
|
+
totalRows: number().int(),
|
|
19288
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
19289
|
+
totalBytes: number().int(),
|
|
19290
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
19291
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
19292
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
19293
|
+
unaccountedRows: number().int(),
|
|
19294
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
19295
|
+
unaccountedBytes: number().int()
|
|
19296
|
+
});
|
|
19126
19297
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
19127
19298
|
var EventPruneCountsSchema = object({
|
|
19128
19299
|
motion: number().int(),
|
|
@@ -19277,7 +19448,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19277
19448
|
until: number().optional(),
|
|
19278
19449
|
kinds: array(string()).optional(),
|
|
19279
19450
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
19280
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
19451
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
19281
19452
|
deviceId: number(),
|
|
19282
19453
|
since: number(),
|
|
19283
19454
|
until: number(),
|
|
@@ -19326,6 +19497,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19326
19497
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
19327
19498
|
kind: "query",
|
|
19328
19499
|
auth: "admin"
|
|
19500
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
19501
|
+
kind: "query",
|
|
19502
|
+
auth: "admin"
|
|
19329
19503
|
}), method(object({
|
|
19330
19504
|
olderThanMs: number(),
|
|
19331
19505
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -21381,6 +21555,20 @@ method(object({
|
|
|
21381
21555
|
error: string().optional()
|
|
21382
21556
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
21383
21557
|
providerId: string(),
|
|
21558
|
+
/**
|
|
21559
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
21560
|
+
*
|
|
21561
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
21562
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
21563
|
+
* credential the operator did not retype — and posting that here
|
|
21564
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
21565
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
21566
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
21567
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
21568
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
21569
|
+
* every value was typed just now and nothing is stored yet.
|
|
21570
|
+
*/
|
|
21571
|
+
locationId: string().optional(),
|
|
21384
21572
|
config: record(string(), unknown())
|
|
21385
21573
|
}), object({
|
|
21386
21574
|
ok: boolean(),
|
|
@@ -28739,6 +28927,9 @@ method(object({
|
|
|
28739
28927
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
28740
28928
|
kind: "query",
|
|
28741
28929
|
auth: "admin"
|
|
28930
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
28931
|
+
kind: "mutation",
|
|
28932
|
+
auth: "admin"
|
|
28742
28933
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
28743
28934
|
kind: "mutation",
|
|
28744
28935
|
auth: "admin"
|
|
@@ -29130,6 +29321,25 @@ var SceneMonitorStatusSchema = object({
|
|
|
29130
29321
|
monitors: array(SceneMonitorSchema),
|
|
29131
29322
|
lastFetchedAt: number()
|
|
29132
29323
|
});
|
|
29324
|
+
/**
|
|
29325
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
29326
|
+
*
|
|
29327
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
29328
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
29329
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
29330
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
29331
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
29332
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
29333
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
29334
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
29335
|
+
*
|
|
29336
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
29337
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
29338
|
+
*/
|
|
29339
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
29340
|
+
deviceId: number(),
|
|
29341
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
29342
|
+
});
|
|
29133
29343
|
var sceneMonitorCapability = {
|
|
29134
29344
|
name: "scene-monitor",
|
|
29135
29345
|
scope: "device",
|
|
@@ -29139,6 +29349,22 @@ var sceneMonitorCapability = {
|
|
|
29139
29349
|
deviceTypes: [DeviceType.Camera],
|
|
29140
29350
|
methods: {
|
|
29141
29351
|
listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
|
|
29352
|
+
/**
|
|
29353
|
+
* The same answer, for a SET of cameras, in one round trip.
|
|
29354
|
+
*
|
|
29355
|
+
* `/scenes` renders every camera and re-reads them on a 30s safety-net
|
|
29356
|
+
* poll behind the push slice. Fanned out client-side that was one query
|
|
29357
|
+
* per camera — 29 round trips through the browser, the hub and the
|
|
29358
|
+
* post-analysis runner every 30 seconds to read an in-memory map the
|
|
29359
|
+
* owner had already merged. The work is unchanged (`statusFor` per
|
|
29360
|
+
* device, all in-process at the owner); what collapses is the transport.
|
|
29361
|
+
*
|
|
29362
|
+
* A camera that cannot answer still gets a row, with `status: null` —
|
|
29363
|
+
* see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
|
|
29364
|
+
* ids: a caller that asked for twenty-nine and got twenty-seven cannot
|
|
29365
|
+
* tell which two are missing, or that any are.
|
|
29366
|
+
*/
|
|
29367
|
+
listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
|
|
29142
29368
|
createScene: method(object({
|
|
29143
29369
|
deviceId: number(),
|
|
29144
29370
|
label: string(),
|
|
@@ -30974,6 +31200,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
30974
31200
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
30975
31201
|
});
|
|
30976
31202
|
/**
|
|
31203
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
31204
|
+
*
|
|
31205
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
31206
|
+
* because `snapshot: null` was already spoken for:
|
|
31207
|
+
*
|
|
31208
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
31209
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
31210
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
31211
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
31212
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
31213
|
+
*
|
|
31214
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
31215
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
31216
|
+
* which is a definite claim about a camera nobody could read.
|
|
31217
|
+
*/
|
|
31218
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
31219
|
+
deviceId: number(),
|
|
31220
|
+
read: _enum(["read", "unreadable"]),
|
|
31221
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
31222
|
+
});
|
|
31223
|
+
/**
|
|
30977
31224
|
* Time-series resolution. The history methods return one bucket per
|
|
30978
31225
|
* step over the requested range. Smaller resolutions cost more
|
|
30979
31226
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -31030,6 +31277,20 @@ var zoneAnalyticsCapability = {
|
|
|
31030
31277
|
* (no inference result emitted since boot or since binding was
|
|
31031
31278
|
* activated). */
|
|
31032
31279
|
getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
|
|
31280
|
+
/**
|
|
31281
|
+
* The same snapshot, for a SET of cameras, in one round trip.
|
|
31282
|
+
*
|
|
31283
|
+
* The Events page's Stationary section polls this every 15s for every
|
|
31284
|
+
* selected camera. Fanned out client-side that is one query per camera to
|
|
31285
|
+
* read a `Map.get` at the owner — the answer costs nothing, the round trip
|
|
31286
|
+
* costs everything. Batched, N transports become one and the per-device
|
|
31287
|
+
* work is unchanged.
|
|
31288
|
+
*
|
|
31289
|
+
* Every requested deviceId gets a row, tagged `read` — see
|
|
31290
|
+
* {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
|
|
31291
|
+
* not answer for is `'unreadable'`, never an empty reading.
|
|
31292
|
+
*/
|
|
31293
|
+
getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
|
|
31033
31294
|
/** Time-series object count inside one zone. `className` optional —
|
|
31034
31295
|
* omit to count every class in the zone. */
|
|
31035
31296
|
getZoneHistory: method(object({
|
|
@@ -35427,6 +35688,12 @@ Object.freeze({
|
|
|
35427
35688
|
addonId: null,
|
|
35428
35689
|
access: "view"
|
|
35429
35690
|
},
|
|
35691
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
35692
|
+
capName: "pipeline-analytics",
|
|
35693
|
+
capScope: "device",
|
|
35694
|
+
addonId: null,
|
|
35695
|
+
access: "view"
|
|
35696
|
+
},
|
|
35430
35697
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
35431
35698
|
capName: "pipeline-analytics",
|
|
35432
35699
|
capScope: "device",
|
|
@@ -35445,6 +35712,12 @@ Object.freeze({
|
|
|
35445
35712
|
addonId: null,
|
|
35446
35713
|
access: "view"
|
|
35447
35714
|
},
|
|
35715
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
35716
|
+
capName: "pipeline-analytics",
|
|
35717
|
+
capScope: "device",
|
|
35718
|
+
addonId: null,
|
|
35719
|
+
access: "view"
|
|
35720
|
+
},
|
|
35448
35721
|
"pipelineAnalytics.getMotionEvents": {
|
|
35449
35722
|
capName: "pipeline-analytics",
|
|
35450
35723
|
capScope: "device",
|
|
@@ -36621,6 +36894,12 @@ Object.freeze({
|
|
|
36621
36894
|
addonId: null,
|
|
36622
36895
|
access: "view"
|
|
36623
36896
|
},
|
|
36897
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
36898
|
+
capName: "recording",
|
|
36899
|
+
capScope: "system",
|
|
36900
|
+
addonId: null,
|
|
36901
|
+
access: "create"
|
|
36902
|
+
},
|
|
36624
36903
|
"recording.refreshStorageLocationsForMigration": {
|
|
36625
36904
|
capName: "recording",
|
|
36626
36905
|
capScope: "system",
|
|
@@ -36747,6 +37026,12 @@ Object.freeze({
|
|
|
36747
37026
|
addonId: null,
|
|
36748
37027
|
access: "view"
|
|
36749
37028
|
},
|
|
37029
|
+
"sceneMonitor.listScenesBatch": {
|
|
37030
|
+
capName: "scene-monitor",
|
|
37031
|
+
capScope: "device",
|
|
37032
|
+
addonId: null,
|
|
37033
|
+
access: "view"
|
|
37034
|
+
},
|
|
36750
37035
|
"sceneMonitor.recheckNow": {
|
|
36751
37036
|
capName: "scene-monitor",
|
|
36752
37037
|
capScope: "device",
|
|
@@ -38103,6 +38388,12 @@ Object.freeze({
|
|
|
38103
38388
|
addonId: null,
|
|
38104
38389
|
access: "view"
|
|
38105
38390
|
},
|
|
38391
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
38392
|
+
capName: "zone-analytics",
|
|
38393
|
+
capScope: "device",
|
|
38394
|
+
addonId: null,
|
|
38395
|
+
access: "view"
|
|
38396
|
+
},
|
|
38106
38397
|
"zoneAnalytics.getUnzonedHistory": {
|
|
38107
38398
|
capName: "zone-analytics",
|
|
38108
38399
|
capScope: "device",
|
|
@@ -39092,6 +39383,11 @@ Object.freeze({
|
|
|
39092
39383
|
form: "single",
|
|
39093
39384
|
optional: false
|
|
39094
39385
|
}],
|
|
39386
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
39387
|
+
name: "deviceId",
|
|
39388
|
+
form: "single",
|
|
39389
|
+
optional: true
|
|
39390
|
+
}],
|
|
39095
39391
|
"pipelineAnalytics.getGroup": [{
|
|
39096
39392
|
name: "deviceId",
|
|
39097
39393
|
form: "single",
|
|
@@ -39102,6 +39398,11 @@ Object.freeze({
|
|
|
39102
39398
|
form: "single",
|
|
39103
39399
|
optional: false
|
|
39104
39400
|
}],
|
|
39401
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
39402
|
+
name: "deviceIds",
|
|
39403
|
+
form: "array",
|
|
39404
|
+
optional: false
|
|
39405
|
+
}],
|
|
39105
39406
|
"pipelineAnalytics.getMotionEvents": [{
|
|
39106
39407
|
name: "deviceId",
|
|
39107
39408
|
form: "single",
|
|
@@ -39542,6 +39843,11 @@ Object.freeze({
|
|
|
39542
39843
|
form: "single",
|
|
39543
39844
|
optional: false
|
|
39544
39845
|
}],
|
|
39846
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
39847
|
+
name: "deviceId",
|
|
39848
|
+
form: "single",
|
|
39849
|
+
optional: true
|
|
39850
|
+
}],
|
|
39545
39851
|
"recording.relocateFootage": [{
|
|
39546
39852
|
name: "deviceId",
|
|
39547
39853
|
form: "single",
|
|
@@ -39607,6 +39913,11 @@ Object.freeze({
|
|
|
39607
39913
|
form: "single",
|
|
39608
39914
|
optional: false
|
|
39609
39915
|
}],
|
|
39916
|
+
"sceneMonitor.listScenesBatch": [{
|
|
39917
|
+
name: "deviceIds",
|
|
39918
|
+
form: "array",
|
|
39919
|
+
optional: false
|
|
39920
|
+
}],
|
|
39610
39921
|
"sceneMonitor.recheckNow": [{
|
|
39611
39922
|
name: "deviceId",
|
|
39612
39923
|
form: "single",
|
|
@@ -39868,6 +40179,11 @@ Object.freeze({
|
|
|
39868
40179
|
form: "single",
|
|
39869
40180
|
optional: false
|
|
39870
40181
|
}],
|
|
40182
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
40183
|
+
name: "deviceIds",
|
|
40184
|
+
form: "array",
|
|
40185
|
+
optional: false
|
|
40186
|
+
}],
|
|
39871
40187
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
39872
40188
|
name: "deviceId",
|
|
39873
40189
|
form: "single",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-wyze",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.53",
|
|
4
4
|
"description": "Wyze camera device-provider addon for CamStack — wraps the @apocaliss92/wyze-bridge-js P2P/DTLS client, feeding the stream-broker via the pull-rfc4571 lazy-publish path (a structural twin of addon-provider-reolink)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|