@camstack/addon-provider-wyze 0.2.106 → 0.2.108
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 +355 -14
- package/dist/addon.mjs +355 -14
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -5392,7 +5392,7 @@ var ZodIssueCode = {
|
|
|
5392
5392
|
var ZodFirstPartyTypeKind;
|
|
5393
5393
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5394
5394
|
//#endregion
|
|
5395
|
-
//#region ../types/dist/sleep-
|
|
5395
|
+
//#region ../types/dist/sleep-BDR76Ykr.mjs
|
|
5396
5396
|
/**
|
|
5397
5397
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5398
5398
|
* window to float samples (D455).
|
|
@@ -11594,6 +11594,21 @@ var streamSignalsCapability = {
|
|
|
11594
11594
|
mode: "singleton",
|
|
11595
11595
|
deviceTypes: Object.values(DeviceType),
|
|
11596
11596
|
runtimeState: StreamSignalsStatusSchema,
|
|
11597
|
+
/**
|
|
11598
|
+
* Runtime-state durability: **session** — mirrored in RAM, never written.
|
|
11599
|
+
*
|
|
11600
|
+
* The slice holds what the DEVICE says it can emit. That is a probed fact,
|
|
11601
|
+
* not an operator choice: the provider re-declares it on every registration,
|
|
11602
|
+
* so losing it loses nothing and persisting it would freeze an answer the
|
|
11603
|
+
* camera is entitled to change. Measured the same day on the sibling case —
|
|
11604
|
+
* `native-object-detection.supportedClasses` was persisted, and a firmware
|
|
11605
|
+
* class the camera really detected stayed missing for the life of the row
|
|
11606
|
+
* because the fix could not reach it.
|
|
11607
|
+
*
|
|
11608
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
11609
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
11610
|
+
*/
|
|
11611
|
+
durability: "session",
|
|
11597
11612
|
methods: {
|
|
11598
11613
|
/**
|
|
11599
11614
|
* What this device can emit. Empty is a valid and common answer — most
|
|
@@ -18225,6 +18240,48 @@ var NcAlarmConfigSchema = object({
|
|
|
18225
18240
|
settings: NcAlarmSettingsSchema,
|
|
18226
18241
|
coverage: array(NcAlarmModeCoverageSchema)
|
|
18227
18242
|
});
|
|
18243
|
+
/**
|
|
18244
|
+
* ONE rule's demand on ONE camera's clip ring.
|
|
18245
|
+
*
|
|
18246
|
+
* A rule, not a camera: the broker takes the MAX over the asks that reach a
|
|
18247
|
+
* camera, so the answer stays a faithful description of the rules and the
|
|
18248
|
+
* bounding (the byte budget, the broker's own ceiling) stays where the cost
|
|
18249
|
+
* lives. A camera that appears in no ask is being told **nothing** — which is
|
|
18250
|
+
* a different thing from never having been told, and only the envelope
|
|
18251
|
+
* (`rulesLoaded`) can tell those apart.
|
|
18252
|
+
*/
|
|
18253
|
+
var NcClipRetentionAskSchema = object({
|
|
18254
|
+
/**
|
|
18255
|
+
* The camera this ask is about. **Absent = every camera**, which is what a
|
|
18256
|
+
* rule with no `conditions.devices` means: it can fire anywhere, so it is
|
|
18257
|
+
* asking everywhere.
|
|
18258
|
+
*/
|
|
18259
|
+
deviceId: number().int().nonnegative().optional(),
|
|
18260
|
+
/** Seconds of history the rule needs held for it. Never zero — a rule that
|
|
18261
|
+
* needs nothing produces no ask at all. */
|
|
18262
|
+
seconds: number().min(0).max(60),
|
|
18263
|
+
/** The profile the rule cuts from, when it named one. Absent = the cheapest
|
|
18264
|
+
* assigned, which is what the cut defaults to. */
|
|
18265
|
+
profile: CamProfileSchema.optional(),
|
|
18266
|
+
/** Who is asking — so a retention the operator did not expect names a rule. */
|
|
18267
|
+
ruleId: string(),
|
|
18268
|
+
ruleName: string(),
|
|
18269
|
+
/** Why this many seconds: the rule's own window, or the occupancy ceiling. */
|
|
18270
|
+
reason: _enum(["window", "occupancy"])
|
|
18271
|
+
});
|
|
18272
|
+
/**
|
|
18273
|
+
* The whole answer, with the one bit that keeps absence from reading as zero.
|
|
18274
|
+
*
|
|
18275
|
+
* `rulesLoaded` is false until the rule ledger has completed a load at least
|
|
18276
|
+
* once. A centre that is up but has not read its rules yet answers with an
|
|
18277
|
+
* empty ask list that means nothing at all — applying it would drop every
|
|
18278
|
+
* camera's ring to zero for the first events after a restart, which is the
|
|
18279
|
+
* footage this mechanism exists to keep.
|
|
18280
|
+
*/
|
|
18281
|
+
var NcClipRetentionPlanSchema = object({
|
|
18282
|
+
rulesLoaded: boolean(),
|
|
18283
|
+
asks: array(NcClipRetentionAskSchema).readonly()
|
|
18284
|
+
});
|
|
18228
18285
|
method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
|
|
18229
18286
|
kind: "mutation",
|
|
18230
18287
|
auth: "admin",
|
|
@@ -18245,7 +18302,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
18245
18302
|
}), object({ success: literal(true) }), {
|
|
18246
18303
|
kind: "mutation",
|
|
18247
18304
|
auth: "admin"
|
|
18248
|
-
}), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
|
|
18305
|
+
}), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
|
|
18249
18306
|
deviceId: number().int(),
|
|
18250
18307
|
muted: boolean()
|
|
18251
18308
|
}), object({ success: literal(true) }), {
|
|
@@ -19430,13 +19487,26 @@ var TrackAudioLabelSchema = object({
|
|
|
19430
19487
|
* - `audio` — an audio event on the camera itself that was anomalous for
|
|
19431
19488
|
* THAT camera, loud, and heard while nothing visual was happening (D62).
|
|
19432
19489
|
*
|
|
19490
|
+
* - `onboard` — the CAMERA's own firmware, paired with a decoded frame in
|
|
19491
|
+
* that frame's pixel space. A real root detector and a SPATIAL one: its
|
|
19492
|
+
* boxes are the same kind of fact `pipeline`'s are, produced by a different
|
|
19493
|
+
* detector.
|
|
19494
|
+
*
|
|
19495
|
+
* `onboard` was missing here while `DetectionSourceSchema` already had it, so
|
|
19496
|
+
* a persisted onboard track failed `safeParse` on read and came back with NO
|
|
19497
|
+
* source at all — which `isSpatialTrack` then admitted by accident, through the
|
|
19498
|
+
* `undefined` arm rather than by anybody's decision, and which `excludeSources`
|
|
19499
|
+
* could not name.
|
|
19500
|
+
*
|
|
19433
19501
|
* The spatial subsystems (tracker association, occupancy count, re-id /
|
|
19434
19502
|
* embedding, resurrection) MUST skip every synthetic source. Test for that
|
|
19435
|
-
* with `isSpatialTrack`, which allow-lists
|
|
19436
|
-
* check silently readmits every source added after it was
|
|
19503
|
+
* with `isSpatialTrack`, which allow-lists the spatial sources explicitly — a
|
|
19504
|
+
* `!== 'sensor'` check silently readmits every source added after it was
|
|
19505
|
+
* written.
|
|
19437
19506
|
*/
|
|
19438
19507
|
var TrackSourceSchema = _enum([
|
|
19439
19508
|
"pipeline",
|
|
19509
|
+
"onboard",
|
|
19440
19510
|
"sensor",
|
|
19441
19511
|
"audio"
|
|
19442
19512
|
]);
|
|
@@ -20819,6 +20889,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
|
|
|
20819
20889
|
* solid is this track", cheaper than re-deriving it from a trajectory. */
|
|
20820
20890
|
framesMatched: number().int()
|
|
20821
20891
|
})).readonly() });
|
|
20892
|
+
/**
|
|
20893
|
+
* The long-term series a camera produces. A closed union so a typo cannot
|
|
20894
|
+
* invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
|
|
20895
|
+
* is an alias of this, not a second list.
|
|
20896
|
+
*/
|
|
20897
|
+
var AnalyticsLtsSeriesSchema = _enum([
|
|
20898
|
+
"motion",
|
|
20899
|
+
"audio-dbfs",
|
|
20900
|
+
"battery",
|
|
20901
|
+
"occupancy"
|
|
20902
|
+
]);
|
|
20903
|
+
/**
|
|
20904
|
+
* One closed 5-minute bucket of a long-term series.
|
|
20905
|
+
*
|
|
20906
|
+
* `samples` is how many readings the bucket folded — it is the ACTIVITY for a
|
|
20907
|
+
* series like `motion`, and the confidence for one like `occupancy`. A bucket
|
|
20908
|
+
* that exists at all was measured; a bucket that is absent was not, which is a
|
|
20909
|
+
* different claim from zero and the reason this is a sparse series.
|
|
20910
|
+
*/
|
|
20911
|
+
var LtsBucketSchema = object({
|
|
20912
|
+
scope: string(),
|
|
20913
|
+
bucketStart: number().int(),
|
|
20914
|
+
samples: number().int().nonnegative(),
|
|
20915
|
+
sum: number(),
|
|
20916
|
+
min: number(),
|
|
20917
|
+
max: number()
|
|
20918
|
+
});
|
|
20822
20919
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
20823
20920
|
deviceId: number(),
|
|
20824
20921
|
trackId: string()
|
|
@@ -20865,6 +20962,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20865
20962
|
kinds: array(string()).optional(),
|
|
20866
20963
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
20867
20964
|
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
20965
|
+
deviceId: number(),
|
|
20966
|
+
series: AnalyticsLtsSeriesSchema,
|
|
20967
|
+
/** Omit for EVERY scope of this series in the range. */
|
|
20968
|
+
scope: string().optional(),
|
|
20969
|
+
from: number(),
|
|
20970
|
+
to: number(),
|
|
20971
|
+
limit: number().int().positive().optional()
|
|
20972
|
+
}), array(LtsBucketSchema).readonly()), method(object({
|
|
20868
20973
|
deviceId: number(),
|
|
20869
20974
|
since: number(),
|
|
20870
20975
|
until: number(),
|
|
@@ -21483,6 +21588,40 @@ var MotionSourceEnum = _enum([
|
|
|
21483
21588
|
*/
|
|
21484
21589
|
var MotionSourcesSchema = array(MotionSourceEnum);
|
|
21485
21590
|
/**
|
|
21591
|
+
* Which root detector a camera runs — EXACTLY ONE.
|
|
21592
|
+
*
|
|
21593
|
+
* Deliberately the SAME vocabulary post-analysis already tags every detection
|
|
21594
|
+
* with (`DetectionSource`) rather than a second spelling of the same two
|
|
21595
|
+
* ideas: the value an operator picks here is the value that comes back on the
|
|
21596
|
+
* track, the overlay and the debug row.
|
|
21597
|
+
*
|
|
21598
|
+
* ## Why one, and why it is still an array
|
|
21599
|
+
*
|
|
21600
|
+
* Two roots on one camera is a capability nobody asked for and the operator has
|
|
21601
|
+
* since ruled out. It was never free: both planes feed the SAME per-device
|
|
21602
|
+
* stationary registry, both can promote the same parked object, and the two
|
|
21603
|
+
* detectors disagree about the same box by construction — which is the
|
|
21604
|
+
* disagreement D505 had to arbitrate. Removing the case removes the arbitration.
|
|
21605
|
+
*
|
|
21606
|
+
* The ARRAY shape survives because the stored settings and the attach payload
|
|
21607
|
+
* already speak it on every camera of every fleet, and a cap input that
|
|
21608
|
+
* suddenly refuses a stored value makes the camera un-attachable — a fail-closed
|
|
21609
|
+
* in the one direction that costs an operator their cameras. So the wire stays
|
|
21610
|
+
* tolerant and the TYPE is exact: a longer list is truncated to its first
|
|
21611
|
+
* element rather than refused, and `max(1)` is what every consumer can then
|
|
21612
|
+
* rely on.
|
|
21613
|
+
*
|
|
21614
|
+
* AT MOST one, not exactly one. The EMPTY list is a legal, deliberate pick —
|
|
21615
|
+
* a camera an operator left with no root detector — and `planDetectionSources`
|
|
21616
|
+
* has always said so: inventing a root for them is how a default acts without
|
|
21617
|
+
* anybody choosing it. Tightening this to `length(1)` broke that, and the
|
|
21618
|
+
* suite caught it.
|
|
21619
|
+
*
|
|
21620
|
+
* Measured before tightening (2026-09-15, this fleet, 6 h of attaches): 3327
|
|
21621
|
+
* `["pipeline"]` and 2 `["onboard"]`. Not one camera had two.
|
|
21622
|
+
*/
|
|
21623
|
+
var DetectionSourcesSchema = preprocess((value) => Array.isArray(value) && value.length > 1 ? value.slice(0, 1) : value, array(DetectionSourceSchema).max(1));
|
|
21624
|
+
/**
|
|
21486
21625
|
* Input shape for `pipeline-runner.reportMotion` cap method. Exported
|
|
21487
21626
|
* so cap-side consumers (the orchestrator forward, the runner addon's
|
|
21488
21627
|
* cap implementation, tests) can reuse the type instead of redeclaring
|
|
@@ -21633,6 +21772,35 @@ var RunnerCameraConfigSchema = object({
|
|
|
21633
21772
|
* events; the orchestrator forwards to `reportMotion`.
|
|
21634
21773
|
*/
|
|
21635
21774
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
21775
|
+
/**
|
|
21776
|
+
* WHICH root detector runs for this camera. The detection counterpart of
|
|
21777
|
+
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
21778
|
+
* of sources the system understands, vendor-agnostic, owned here.
|
|
21779
|
+
*
|
|
21780
|
+
* - `pipeline` — this runner's own root step on decoded pixels (`steps`).
|
|
21781
|
+
* - `onboard` — the CAMERA's own detector, admitted only when it is named
|
|
21782
|
+
* here. Its boxes reach post-analysis on the same
|
|
21783
|
+
* `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
|
|
21784
|
+
* track gets the same tracker, the same overlay and the same detail
|
|
21785
|
+
* subtree (face / plate / embedding).
|
|
21786
|
+
*
|
|
21787
|
+
* `['onboard']` alone is the REPLACEMENT: this runner's root step does not
|
|
21788
|
+
* run, and that is where a camera with a usable onboard detector buys back
|
|
21789
|
+
* its share of the accelerator. It saves the root inference, not the decode
|
|
21790
|
+
* — the detail subtree still needs pixels to cut crops from.
|
|
21791
|
+
*
|
|
21792
|
+
* A camera whose onboard detections carry no geometry cannot serve as a root
|
|
21793
|
+
* step at all (there is nothing to track), and the cap that knows this says
|
|
21794
|
+
* so on `native-object-detection.getOptions().geometry`. On Reolink that is
|
|
21795
|
+
* every battery camera: the boxes ride a sub-stream a battery camera never
|
|
21796
|
+
* attaches, so such a camera stays on `['pipeline']` and its onboard AI is
|
|
21797
|
+
* worth exactly what it already is — a wake signal with a class.
|
|
21798
|
+
*
|
|
21799
|
+
* Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
|
|
21800
|
+
* vendor never turns a detector on for the operator, and neither does a
|
|
21801
|
+
* default.
|
|
21802
|
+
*/
|
|
21803
|
+
detectionSources: DetectionSourcesSchema.default(["pipeline"]),
|
|
21636
21804
|
pipelineEnabled: boolean().default(true),
|
|
21637
21805
|
/** Ordered tree of video steps. Absent → runner skips video detection. */
|
|
21638
21806
|
steps: array(PipelineStepInputSchema).readonly().optional(),
|
|
@@ -21814,7 +21982,47 @@ var RunnerLocalMetricsSchema = object({
|
|
|
21814
21982
|
queueDepth: number(),
|
|
21815
21983
|
frameLazy: FrameLazyMetricsSchema.optional()
|
|
21816
21984
|
});
|
|
21985
|
+
/**
|
|
21986
|
+
* Pipeline Runner capability — runtime detection workhorse.
|
|
21987
|
+
*
|
|
21988
|
+
* One instance per node. Receives camera assignments from
|
|
21989
|
+
* `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
|
|
21990
|
+
* decoded frames, drains motion + detection queues, calls the local
|
|
21991
|
+
* `motion-detection` and `pipeline-executor` capabilities, and emits typed
|
|
21992
|
+
* `pipeline.inference-result` and `detection.motion-analysis` events on
|
|
21993
|
+
* the bus.
|
|
21994
|
+
*
|
|
21995
|
+
* Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
|
|
21996
|
+
* runner has zero knowledge of other agents, no global state, and never
|
|
21997
|
+
* makes assignment decisions itself.
|
|
21998
|
+
*/
|
|
21999
|
+
/**
|
|
22000
|
+
* Why a runner declined to look at a camera out of band. Named rather than a
|
|
22001
|
+
* bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
|
|
22002
|
+
* already mid-session and one already bursting are four different facts, and a
|
|
22003
|
+
* caller told only "no" cannot tell a protection from a bug.
|
|
22004
|
+
*/
|
|
22005
|
+
var OccupancyBurstRefusalSchema = _enum([
|
|
22006
|
+
"not-attached",
|
|
22007
|
+
"battery-asleep",
|
|
22008
|
+
"not-watching",
|
|
22009
|
+
"already-bursting"
|
|
22010
|
+
]);
|
|
21817
22011
|
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
|
|
22012
|
+
deviceId: number(),
|
|
22013
|
+
/** How many frames to collect. The caller sizes this against what its
|
|
22014
|
+
* own confirmation needs; the runner clamps it to the field's range. */
|
|
22015
|
+
frames: number().int().positive().optional(),
|
|
22016
|
+
/** Why, for the log. A burst nobody can attribute is a cost nobody can
|
|
22017
|
+
* defend — the periodic one at least has a timer to point at. */
|
|
22018
|
+
reason: string()
|
|
22019
|
+
}), object({
|
|
22020
|
+
started: boolean(),
|
|
22021
|
+
refusedBecause: OccupancyBurstRefusalSchema.optional()
|
|
22022
|
+
}), {
|
|
22023
|
+
auth: "admin",
|
|
22024
|
+
kind: "mutation"
|
|
22025
|
+
}), method(object({
|
|
21818
22026
|
handle: FrameHandleSchema,
|
|
21819
22027
|
bbox: NativeCropBboxSchema,
|
|
21820
22028
|
maxWidth: number().int().positive().optional(),
|
|
@@ -28756,11 +28964,46 @@ var NativeObjectDetectionStatusSchema = object({
|
|
|
28756
28964
|
supportedClasses: array(NativeObjectClassEnum).readonly(),
|
|
28757
28965
|
/**
|
|
28758
28966
|
* Whether forwarding of onboard AI detections is enabled for this device.
|
|
28759
|
-
*
|
|
28760
|
-
*
|
|
28967
|
+
*
|
|
28968
|
+
* Cold-start OFF on every camera. A vendor does not decide which detector a
|
|
28969
|
+
* camera runs — that is the operator's choice, and it is made where every
|
|
28970
|
+
* other detection choice is made. `geometry` on
|
|
28971
|
+
* {@link NativeObjectDetectionOptionsSchema} is how the form says what the
|
|
28972
|
+
* choice would buy this particular camera.
|
|
28761
28973
|
*/
|
|
28762
28974
|
enabled: boolean()
|
|
28763
28975
|
});
|
|
28976
|
+
/**
|
|
28977
|
+
* WHEN a camera's onboard detections carry geometry.
|
|
28978
|
+
*
|
|
28979
|
+
* - `boxed` — always. The provider holds a standing channel for the boxes, so
|
|
28980
|
+
* a detection is a trackable subject whenever the camera sees one.
|
|
28981
|
+
* - `while-streaming` — only while some stream of this camera is already open.
|
|
28982
|
+
* The boxes ride the video's own side-channel rather than a feed of their
|
|
28983
|
+
* own, so they cost nothing and they exist only when something is pulling.
|
|
28984
|
+
* On Reolink this is every BATTERY camera: a persistent second feed is real
|
|
28985
|
+
* radio drain, but a camera that is awake is awake precisely because
|
|
28986
|
+
* something is in front of it, and its stream is already being pulled.
|
|
28987
|
+
* - `flag-only` — never. The firmware ships the class and nothing else; the
|
|
28988
|
+
* pipeline needs geometry to make a subject, so such a push reaches the
|
|
28989
|
+
* tracker as motion and no further.
|
|
28990
|
+
*
|
|
28991
|
+
* Reported and not inferred, because the operator's question in front of the
|
|
28992
|
+
* picker is "what do I get", and these are three different answers (D14: the
|
|
28993
|
+
* derived form is only as honest as `getOptions`).
|
|
28994
|
+
*/
|
|
28995
|
+
var NativeObjectGeometryEnum = _enum([
|
|
28996
|
+
"boxed",
|
|
28997
|
+
"while-streaming",
|
|
28998
|
+
"flag-only"
|
|
28999
|
+
]);
|
|
29000
|
+
var NativeObjectDetectionOptionsSchema = object({
|
|
29001
|
+
/** Classes this firmware can detect — the same list the status reports. */
|
|
29002
|
+
supportedClasses: array(NativeObjectClassEnum).readonly(),
|
|
29003
|
+
/** What a detection from this camera carries. */
|
|
29004
|
+
geometry: NativeObjectGeometryEnum
|
|
29005
|
+
});
|
|
29006
|
+
var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
|
|
28764
29007
|
var NativeObjectDetectionRuntimeStateSchema = NativeObjectDetectionStatusSchema.extend({
|
|
28765
29008
|
/** Required by createRuntimeStateBridge — epoch ms of last refresh. */
|
|
28766
29009
|
lastFetchedAt: number() });
|
|
@@ -28770,13 +29013,23 @@ var nativeObjectDetectionCapability = {
|
|
|
28770
29013
|
deviceNative: true,
|
|
28771
29014
|
mode: "singleton",
|
|
28772
29015
|
deviceTypes: [DeviceType.Camera],
|
|
28773
|
-
methods: {
|
|
28774
|
-
deviceId: number(),
|
|
28775
|
-
|
|
28776
|
-
|
|
28777
|
-
|
|
28778
|
-
|
|
28779
|
-
|
|
29016
|
+
methods: {
|
|
29017
|
+
getOptions: method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema),
|
|
29018
|
+
setSettings: method(object({
|
|
29019
|
+
deviceId: number(),
|
|
29020
|
+
settings: NativeObjectDetectionSettingsPatchSchema
|
|
29021
|
+
}), _void(), {
|
|
29022
|
+
kind: "mutation",
|
|
29023
|
+
auth: "admin"
|
|
29024
|
+
}),
|
|
29025
|
+
setEnabled: method(object({
|
|
29026
|
+
deviceId: number(),
|
|
29027
|
+
enabled: boolean()
|
|
29028
|
+
}), _void(), {
|
|
29029
|
+
kind: "mutation",
|
|
29030
|
+
auth: "admin"
|
|
29031
|
+
})
|
|
29032
|
+
},
|
|
28780
29033
|
events: { onDetected: { data: object({
|
|
28781
29034
|
deviceId: number(),
|
|
28782
29035
|
detection: NativeDetectionSchema
|
|
@@ -33624,7 +33877,28 @@ var PerScopeBreakdownSchema = object({
|
|
|
33624
33877
|
/** Total tracked objects in this scope (frame / zone / unzoned). */
|
|
33625
33878
|
totalObjects: number().int().nonnegative(),
|
|
33626
33879
|
/** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
|
|
33627
|
-
byClass: record(string(), number().int().nonnegative())
|
|
33880
|
+
byClass: record(string(), number().int().nonnegative()),
|
|
33881
|
+
/**
|
|
33882
|
+
* Of `totalObjects`, how many are STANDING — objects the census holds,
|
|
33883
|
+
* present but not going anywhere (a parked car, a bin, a statue).
|
|
33884
|
+
*
|
|
33885
|
+
* The operator's question about a scene has two halves and they answer
|
|
33886
|
+
* different things: "what is parked here" is stable for hours and must not
|
|
33887
|
+
* flap, while "who is walking through" is the thing an alert is about. One
|
|
33888
|
+
* number could only serve one of them, and `totalObjects` served the first
|
|
33889
|
+
* badly — a passer-by moved it and every occupancy rule saw a change.
|
|
33890
|
+
*
|
|
33891
|
+
* `standing + transient === totalObjects`, always. The total keeps its old
|
|
33892
|
+
* meaning exactly, so no rule written against it changes behaviour.
|
|
33893
|
+
*
|
|
33894
|
+
* ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
|
|
33895
|
+
* this field cannot say, and a reader that folds absence in as 0 reports "no
|
|
33896
|
+
* parked objects here" about a camera whose lot is full.
|
|
33897
|
+
*/
|
|
33898
|
+
standingObjects: number().int().nonnegative().optional(),
|
|
33899
|
+
/** Of `totalObjects`, how many are passing through. See `standingObjects` —
|
|
33900
|
+
* same absence rule. */
|
|
33901
|
+
transientObjects: number().int().nonnegative().optional()
|
|
33628
33902
|
});
|
|
33629
33903
|
var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
33630
33904
|
zoneId: string(),
|
|
@@ -33747,6 +34021,23 @@ var HistoryPointSchema = object({
|
|
|
33747
34021
|
* triple covers the three spatial scopes (per-zone, frame-wide,
|
|
33748
34022
|
* outside-any-zone). Crossing them with `className?` gives the full
|
|
33749
34023
|
* combinatorial coverage the operator UI requested.
|
|
34024
|
+
*
|
|
34025
|
+
* ## ⚠️ The three history methods are DEPRECATED
|
|
34026
|
+
*
|
|
34027
|
+
* They are served from a 60-MINUTE in-memory ring. It dies with the process,
|
|
34028
|
+
* so after a restart they report nothing about a night that did happen, and any
|
|
34029
|
+
* window past an hour is silently clamped — a caller cannot tell a clamp from
|
|
34030
|
+
* an empty scene. The presets in the occupancy chart existed to hide that
|
|
34031
|
+
* horizon rather than to answer a question.
|
|
34032
|
+
*
|
|
34033
|
+
* Use `pipelineAnalytics.readLongTermSeries({ series: 'occupancy', scope })`
|
|
34034
|
+
* instead. Those rows are already written for every camera, ~60 bytes per
|
|
34035
|
+
* (camera, scope, 5-minute bucket), kept for a year, and survive a restart.
|
|
34036
|
+
* Spell the scope with `occupancyScope(zoneId?, className?)` — the same
|
|
34037
|
+
* function the writer uses, so the two cannot drift.
|
|
34038
|
+
*
|
|
34039
|
+
* The ring stays until these three go, and it is the only thing left reading
|
|
34040
|
+
* it. Do not add a fourth caller.
|
|
33750
34041
|
*/
|
|
33751
34042
|
var zoneAnalyticsCapability = {
|
|
33752
34043
|
name: "zone-analytics",
|
|
@@ -38146,12 +38437,24 @@ Object.freeze({
|
|
|
38146
38437
|
addonId: null,
|
|
38147
38438
|
access: "create"
|
|
38148
38439
|
},
|
|
38440
|
+
"nativeObjectDetection.getOptions": {
|
|
38441
|
+
capName: "native-object-detection",
|
|
38442
|
+
capScope: "device",
|
|
38443
|
+
addonId: null,
|
|
38444
|
+
access: "view"
|
|
38445
|
+
},
|
|
38149
38446
|
"nativeObjectDetection.setEnabled": {
|
|
38150
38447
|
capName: "native-object-detection",
|
|
38151
38448
|
capScope: "device",
|
|
38152
38449
|
addonId: null,
|
|
38153
38450
|
access: "create"
|
|
38154
38451
|
},
|
|
38452
|
+
"nativeObjectDetection.setSettings": {
|
|
38453
|
+
capName: "native-object-detection",
|
|
38454
|
+
capScope: "device",
|
|
38455
|
+
addonId: null,
|
|
38456
|
+
access: "create"
|
|
38457
|
+
},
|
|
38155
38458
|
"navigation.getFeatures": {
|
|
38156
38459
|
capName: "navigation",
|
|
38157
38460
|
capScope: "device",
|
|
@@ -38416,6 +38719,12 @@ Object.freeze({
|
|
|
38416
38719
|
addonId: null,
|
|
38417
38720
|
access: "view"
|
|
38418
38721
|
},
|
|
38722
|
+
"notificationRules.getClipRetentionAsks": {
|
|
38723
|
+
capName: "notification-rules",
|
|
38724
|
+
capScope: "system",
|
|
38725
|
+
addonId: null,
|
|
38726
|
+
access: "view"
|
|
38727
|
+
},
|
|
38419
38728
|
"notificationRules.getConditionCatalog": {
|
|
38420
38729
|
capName: "notification-rules",
|
|
38421
38730
|
capScope: "system",
|
|
@@ -38926,6 +39235,12 @@ Object.freeze({
|
|
|
38926
39235
|
addonId: null,
|
|
38927
39236
|
access: "create"
|
|
38928
39237
|
},
|
|
39238
|
+
"pipelineAnalytics.readLongTermSeries": {
|
|
39239
|
+
capName: "pipeline-analytics",
|
|
39240
|
+
capScope: "device",
|
|
39241
|
+
addonId: null,
|
|
39242
|
+
access: "view"
|
|
39243
|
+
},
|
|
38929
39244
|
"pipelineAnalytics.rebuildObjectEmbeddings": {
|
|
38930
39245
|
capName: "pipeline-analytics",
|
|
38931
39246
|
capScope: "device",
|
|
@@ -39598,6 +39913,12 @@ Object.freeze({
|
|
|
39598
39913
|
addonId: null,
|
|
39599
39914
|
access: "create"
|
|
39600
39915
|
},
|
|
39916
|
+
"pipelineRunner.requestOccupancyBurst": {
|
|
39917
|
+
capName: "pipeline-runner",
|
|
39918
|
+
capScope: "system",
|
|
39919
|
+
addonId: null,
|
|
39920
|
+
access: "create"
|
|
39921
|
+
},
|
|
39601
39922
|
"pipelineRunner.runDetailSubtree": {
|
|
39602
39923
|
capName: "pipeline-runner",
|
|
39603
39924
|
capScope: "system",
|
|
@@ -42378,11 +42699,21 @@ Object.freeze({
|
|
|
42378
42699
|
form: "single",
|
|
42379
42700
|
optional: false
|
|
42380
42701
|
}],
|
|
42702
|
+
"nativeObjectDetection.getOptions": [{
|
|
42703
|
+
name: "deviceId",
|
|
42704
|
+
form: "single",
|
|
42705
|
+
optional: false
|
|
42706
|
+
}],
|
|
42381
42707
|
"nativeObjectDetection.setEnabled": [{
|
|
42382
42708
|
name: "deviceId",
|
|
42383
42709
|
form: "single",
|
|
42384
42710
|
optional: false
|
|
42385
42711
|
}],
|
|
42712
|
+
"nativeObjectDetection.setSettings": [{
|
|
42713
|
+
name: "deviceId",
|
|
42714
|
+
form: "single",
|
|
42715
|
+
optional: false
|
|
42716
|
+
}],
|
|
42386
42717
|
"navigation.getFeatures": [{
|
|
42387
42718
|
name: "deviceId",
|
|
42388
42719
|
form: "single",
|
|
@@ -42732,6 +43063,11 @@ Object.freeze({
|
|
|
42732
43063
|
form: "single",
|
|
42733
43064
|
optional: false
|
|
42734
43065
|
}],
|
|
43066
|
+
"pipelineAnalytics.readLongTermSeries": [{
|
|
43067
|
+
name: "deviceId",
|
|
43068
|
+
form: "single",
|
|
43069
|
+
optional: false
|
|
43070
|
+
}],
|
|
42735
43071
|
"pipelineAnalytics.rebuildObjectEmbeddings": [{
|
|
42736
43072
|
name: "deviceId",
|
|
42737
43073
|
form: "single",
|
|
@@ -42922,6 +43258,11 @@ Object.freeze({
|
|
|
42922
43258
|
form: "single",
|
|
42923
43259
|
optional: false
|
|
42924
43260
|
}],
|
|
43261
|
+
"pipelineRunner.requestOccupancyBurst": [{
|
|
43262
|
+
name: "deviceId",
|
|
43263
|
+
form: "single",
|
|
43264
|
+
optional: false
|
|
43265
|
+
}],
|
|
42925
43266
|
"pipelineRunner.runDetailSubtree": [{
|
|
42926
43267
|
name: "deviceId",
|
|
42927
43268
|
form: "single",
|
package/dist/addon.mjs
CHANGED
|
@@ -5371,7 +5371,7 @@ var ZodIssueCode = {
|
|
|
5371
5371
|
var ZodFirstPartyTypeKind;
|
|
5372
5372
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5373
5373
|
//#endregion
|
|
5374
|
-
//#region ../types/dist/sleep-
|
|
5374
|
+
//#region ../types/dist/sleep-BDR76Ykr.mjs
|
|
5375
5375
|
/**
|
|
5376
5376
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5377
5377
|
* window to float samples (D455).
|
|
@@ -11573,6 +11573,21 @@ var streamSignalsCapability = {
|
|
|
11573
11573
|
mode: "singleton",
|
|
11574
11574
|
deviceTypes: Object.values(DeviceType),
|
|
11575
11575
|
runtimeState: StreamSignalsStatusSchema,
|
|
11576
|
+
/**
|
|
11577
|
+
* Runtime-state durability: **session** — mirrored in RAM, never written.
|
|
11578
|
+
*
|
|
11579
|
+
* The slice holds what the DEVICE says it can emit. That is a probed fact,
|
|
11580
|
+
* not an operator choice: the provider re-declares it on every registration,
|
|
11581
|
+
* so losing it loses nothing and persisting it would freeze an answer the
|
|
11582
|
+
* camera is entitled to change. Measured the same day on the sibling case —
|
|
11583
|
+
* `native-object-detection.supportedClasses` was persisted, and a firmware
|
|
11584
|
+
* class the camera really detected stayed missing for the life of the row
|
|
11585
|
+
* because the fix could not reach it.
|
|
11586
|
+
*
|
|
11587
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
11588
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
11589
|
+
*/
|
|
11590
|
+
durability: "session",
|
|
11576
11591
|
methods: {
|
|
11577
11592
|
/**
|
|
11578
11593
|
* What this device can emit. Empty is a valid and common answer — most
|
|
@@ -18204,6 +18219,48 @@ var NcAlarmConfigSchema = object({
|
|
|
18204
18219
|
settings: NcAlarmSettingsSchema,
|
|
18205
18220
|
coverage: array(NcAlarmModeCoverageSchema)
|
|
18206
18221
|
});
|
|
18222
|
+
/**
|
|
18223
|
+
* ONE rule's demand on ONE camera's clip ring.
|
|
18224
|
+
*
|
|
18225
|
+
* A rule, not a camera: the broker takes the MAX over the asks that reach a
|
|
18226
|
+
* camera, so the answer stays a faithful description of the rules and the
|
|
18227
|
+
* bounding (the byte budget, the broker's own ceiling) stays where the cost
|
|
18228
|
+
* lives. A camera that appears in no ask is being told **nothing** — which is
|
|
18229
|
+
* a different thing from never having been told, and only the envelope
|
|
18230
|
+
* (`rulesLoaded`) can tell those apart.
|
|
18231
|
+
*/
|
|
18232
|
+
var NcClipRetentionAskSchema = object({
|
|
18233
|
+
/**
|
|
18234
|
+
* The camera this ask is about. **Absent = every camera**, which is what a
|
|
18235
|
+
* rule with no `conditions.devices` means: it can fire anywhere, so it is
|
|
18236
|
+
* asking everywhere.
|
|
18237
|
+
*/
|
|
18238
|
+
deviceId: number().int().nonnegative().optional(),
|
|
18239
|
+
/** Seconds of history the rule needs held for it. Never zero — a rule that
|
|
18240
|
+
* needs nothing produces no ask at all. */
|
|
18241
|
+
seconds: number().min(0).max(60),
|
|
18242
|
+
/** The profile the rule cuts from, when it named one. Absent = the cheapest
|
|
18243
|
+
* assigned, which is what the cut defaults to. */
|
|
18244
|
+
profile: CamProfileSchema.optional(),
|
|
18245
|
+
/** Who is asking — so a retention the operator did not expect names a rule. */
|
|
18246
|
+
ruleId: string(),
|
|
18247
|
+
ruleName: string(),
|
|
18248
|
+
/** Why this many seconds: the rule's own window, or the occupancy ceiling. */
|
|
18249
|
+
reason: _enum(["window", "occupancy"])
|
|
18250
|
+
});
|
|
18251
|
+
/**
|
|
18252
|
+
* The whole answer, with the one bit that keeps absence from reading as zero.
|
|
18253
|
+
*
|
|
18254
|
+
* `rulesLoaded` is false until the rule ledger has completed a load at least
|
|
18255
|
+
* once. A centre that is up but has not read its rules yet answers with an
|
|
18256
|
+
* empty ask list that means nothing at all — applying it would drop every
|
|
18257
|
+
* camera's ring to zero for the first events after a restart, which is the
|
|
18258
|
+
* footage this mechanism exists to keep.
|
|
18259
|
+
*/
|
|
18260
|
+
var NcClipRetentionPlanSchema = object({
|
|
18261
|
+
rulesLoaded: boolean(),
|
|
18262
|
+
asks: array(NcClipRetentionAskSchema).readonly()
|
|
18263
|
+
});
|
|
18207
18264
|
method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
|
|
18208
18265
|
kind: "mutation",
|
|
18209
18266
|
auth: "admin",
|
|
@@ -18224,7 +18281,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
18224
18281
|
}), object({ success: literal(true) }), {
|
|
18225
18282
|
kind: "mutation",
|
|
18226
18283
|
auth: "admin"
|
|
18227
|
-
}), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
|
|
18284
|
+
}), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
|
|
18228
18285
|
deviceId: number().int(),
|
|
18229
18286
|
muted: boolean()
|
|
18230
18287
|
}), object({ success: literal(true) }), {
|
|
@@ -19409,13 +19466,26 @@ var TrackAudioLabelSchema = object({
|
|
|
19409
19466
|
* - `audio` — an audio event on the camera itself that was anomalous for
|
|
19410
19467
|
* THAT camera, loud, and heard while nothing visual was happening (D62).
|
|
19411
19468
|
*
|
|
19469
|
+
* - `onboard` — the CAMERA's own firmware, paired with a decoded frame in
|
|
19470
|
+
* that frame's pixel space. A real root detector and a SPATIAL one: its
|
|
19471
|
+
* boxes are the same kind of fact `pipeline`'s are, produced by a different
|
|
19472
|
+
* detector.
|
|
19473
|
+
*
|
|
19474
|
+
* `onboard` was missing here while `DetectionSourceSchema` already had it, so
|
|
19475
|
+
* a persisted onboard track failed `safeParse` on read and came back with NO
|
|
19476
|
+
* source at all — which `isSpatialTrack` then admitted by accident, through the
|
|
19477
|
+
* `undefined` arm rather than by anybody's decision, and which `excludeSources`
|
|
19478
|
+
* could not name.
|
|
19479
|
+
*
|
|
19412
19480
|
* The spatial subsystems (tracker association, occupancy count, re-id /
|
|
19413
19481
|
* embedding, resurrection) MUST skip every synthetic source. Test for that
|
|
19414
|
-
* with `isSpatialTrack`, which allow-lists
|
|
19415
|
-
* check silently readmits every source added after it was
|
|
19482
|
+
* with `isSpatialTrack`, which allow-lists the spatial sources explicitly — a
|
|
19483
|
+
* `!== 'sensor'` check silently readmits every source added after it was
|
|
19484
|
+
* written.
|
|
19416
19485
|
*/
|
|
19417
19486
|
var TrackSourceSchema = _enum([
|
|
19418
19487
|
"pipeline",
|
|
19488
|
+
"onboard",
|
|
19419
19489
|
"sensor",
|
|
19420
19490
|
"audio"
|
|
19421
19491
|
]);
|
|
@@ -20798,6 +20868,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
|
|
|
20798
20868
|
* solid is this track", cheaper than re-deriving it from a trajectory. */
|
|
20799
20869
|
framesMatched: number().int()
|
|
20800
20870
|
})).readonly() });
|
|
20871
|
+
/**
|
|
20872
|
+
* The long-term series a camera produces. A closed union so a typo cannot
|
|
20873
|
+
* invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
|
|
20874
|
+
* is an alias of this, not a second list.
|
|
20875
|
+
*/
|
|
20876
|
+
var AnalyticsLtsSeriesSchema = _enum([
|
|
20877
|
+
"motion",
|
|
20878
|
+
"audio-dbfs",
|
|
20879
|
+
"battery",
|
|
20880
|
+
"occupancy"
|
|
20881
|
+
]);
|
|
20882
|
+
/**
|
|
20883
|
+
* One closed 5-minute bucket of a long-term series.
|
|
20884
|
+
*
|
|
20885
|
+
* `samples` is how many readings the bucket folded — it is the ACTIVITY for a
|
|
20886
|
+
* series like `motion`, and the confidence for one like `occupancy`. A bucket
|
|
20887
|
+
* that exists at all was measured; a bucket that is absent was not, which is a
|
|
20888
|
+
* different claim from zero and the reason this is a sparse series.
|
|
20889
|
+
*/
|
|
20890
|
+
var LtsBucketSchema = object({
|
|
20891
|
+
scope: string(),
|
|
20892
|
+
bucketStart: number().int(),
|
|
20893
|
+
samples: number().int().nonnegative(),
|
|
20894
|
+
sum: number(),
|
|
20895
|
+
min: number(),
|
|
20896
|
+
max: number()
|
|
20897
|
+
});
|
|
20801
20898
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
20802
20899
|
deviceId: number(),
|
|
20803
20900
|
trackId: string()
|
|
@@ -20844,6 +20941,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20844
20941
|
kinds: array(string()).optional(),
|
|
20845
20942
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
20846
20943
|
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
20944
|
+
deviceId: number(),
|
|
20945
|
+
series: AnalyticsLtsSeriesSchema,
|
|
20946
|
+
/** Omit for EVERY scope of this series in the range. */
|
|
20947
|
+
scope: string().optional(),
|
|
20948
|
+
from: number(),
|
|
20949
|
+
to: number(),
|
|
20950
|
+
limit: number().int().positive().optional()
|
|
20951
|
+
}), array(LtsBucketSchema).readonly()), method(object({
|
|
20847
20952
|
deviceId: number(),
|
|
20848
20953
|
since: number(),
|
|
20849
20954
|
until: number(),
|
|
@@ -21462,6 +21567,40 @@ var MotionSourceEnum = _enum([
|
|
|
21462
21567
|
*/
|
|
21463
21568
|
var MotionSourcesSchema = array(MotionSourceEnum);
|
|
21464
21569
|
/**
|
|
21570
|
+
* Which root detector a camera runs — EXACTLY ONE.
|
|
21571
|
+
*
|
|
21572
|
+
* Deliberately the SAME vocabulary post-analysis already tags every detection
|
|
21573
|
+
* with (`DetectionSource`) rather than a second spelling of the same two
|
|
21574
|
+
* ideas: the value an operator picks here is the value that comes back on the
|
|
21575
|
+
* track, the overlay and the debug row.
|
|
21576
|
+
*
|
|
21577
|
+
* ## Why one, and why it is still an array
|
|
21578
|
+
*
|
|
21579
|
+
* Two roots on one camera is a capability nobody asked for and the operator has
|
|
21580
|
+
* since ruled out. It was never free: both planes feed the SAME per-device
|
|
21581
|
+
* stationary registry, both can promote the same parked object, and the two
|
|
21582
|
+
* detectors disagree about the same box by construction — which is the
|
|
21583
|
+
* disagreement D505 had to arbitrate. Removing the case removes the arbitration.
|
|
21584
|
+
*
|
|
21585
|
+
* The ARRAY shape survives because the stored settings and the attach payload
|
|
21586
|
+
* already speak it on every camera of every fleet, and a cap input that
|
|
21587
|
+
* suddenly refuses a stored value makes the camera un-attachable — a fail-closed
|
|
21588
|
+
* in the one direction that costs an operator their cameras. So the wire stays
|
|
21589
|
+
* tolerant and the TYPE is exact: a longer list is truncated to its first
|
|
21590
|
+
* element rather than refused, and `max(1)` is what every consumer can then
|
|
21591
|
+
* rely on.
|
|
21592
|
+
*
|
|
21593
|
+
* AT MOST one, not exactly one. The EMPTY list is a legal, deliberate pick —
|
|
21594
|
+
* a camera an operator left with no root detector — and `planDetectionSources`
|
|
21595
|
+
* has always said so: inventing a root for them is how a default acts without
|
|
21596
|
+
* anybody choosing it. Tightening this to `length(1)` broke that, and the
|
|
21597
|
+
* suite caught it.
|
|
21598
|
+
*
|
|
21599
|
+
* Measured before tightening (2026-09-15, this fleet, 6 h of attaches): 3327
|
|
21600
|
+
* `["pipeline"]` and 2 `["onboard"]`. Not one camera had two.
|
|
21601
|
+
*/
|
|
21602
|
+
var DetectionSourcesSchema = preprocess((value) => Array.isArray(value) && value.length > 1 ? value.slice(0, 1) : value, array(DetectionSourceSchema).max(1));
|
|
21603
|
+
/**
|
|
21465
21604
|
* Input shape for `pipeline-runner.reportMotion` cap method. Exported
|
|
21466
21605
|
* so cap-side consumers (the orchestrator forward, the runner addon's
|
|
21467
21606
|
* cap implementation, tests) can reuse the type instead of redeclaring
|
|
@@ -21612,6 +21751,35 @@ var RunnerCameraConfigSchema = object({
|
|
|
21612
21751
|
* events; the orchestrator forwards to `reportMotion`.
|
|
21613
21752
|
*/
|
|
21614
21753
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
21754
|
+
/**
|
|
21755
|
+
* WHICH root detector runs for this camera. The detection counterpart of
|
|
21756
|
+
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
21757
|
+
* of sources the system understands, vendor-agnostic, owned here.
|
|
21758
|
+
*
|
|
21759
|
+
* - `pipeline` — this runner's own root step on decoded pixels (`steps`).
|
|
21760
|
+
* - `onboard` — the CAMERA's own detector, admitted only when it is named
|
|
21761
|
+
* here. Its boxes reach post-analysis on the same
|
|
21762
|
+
* `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
|
|
21763
|
+
* track gets the same tracker, the same overlay and the same detail
|
|
21764
|
+
* subtree (face / plate / embedding).
|
|
21765
|
+
*
|
|
21766
|
+
* `['onboard']` alone is the REPLACEMENT: this runner's root step does not
|
|
21767
|
+
* run, and that is where a camera with a usable onboard detector buys back
|
|
21768
|
+
* its share of the accelerator. It saves the root inference, not the decode
|
|
21769
|
+
* — the detail subtree still needs pixels to cut crops from.
|
|
21770
|
+
*
|
|
21771
|
+
* A camera whose onboard detections carry no geometry cannot serve as a root
|
|
21772
|
+
* step at all (there is nothing to track), and the cap that knows this says
|
|
21773
|
+
* so on `native-object-detection.getOptions().geometry`. On Reolink that is
|
|
21774
|
+
* every battery camera: the boxes ride a sub-stream a battery camera never
|
|
21775
|
+
* attaches, so such a camera stays on `['pipeline']` and its onboard AI is
|
|
21776
|
+
* worth exactly what it already is — a wake signal with a class.
|
|
21777
|
+
*
|
|
21778
|
+
* Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
|
|
21779
|
+
* vendor never turns a detector on for the operator, and neither does a
|
|
21780
|
+
* default.
|
|
21781
|
+
*/
|
|
21782
|
+
detectionSources: DetectionSourcesSchema.default(["pipeline"]),
|
|
21615
21783
|
pipelineEnabled: boolean().default(true),
|
|
21616
21784
|
/** Ordered tree of video steps. Absent → runner skips video detection. */
|
|
21617
21785
|
steps: array(PipelineStepInputSchema).readonly().optional(),
|
|
@@ -21793,7 +21961,47 @@ var RunnerLocalMetricsSchema = object({
|
|
|
21793
21961
|
queueDepth: number(),
|
|
21794
21962
|
frameLazy: FrameLazyMetricsSchema.optional()
|
|
21795
21963
|
});
|
|
21964
|
+
/**
|
|
21965
|
+
* Pipeline Runner capability — runtime detection workhorse.
|
|
21966
|
+
*
|
|
21967
|
+
* One instance per node. Receives camera assignments from
|
|
21968
|
+
* `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
|
|
21969
|
+
* decoded frames, drains motion + detection queues, calls the local
|
|
21970
|
+
* `motion-detection` and `pipeline-executor` capabilities, and emits typed
|
|
21971
|
+
* `pipeline.inference-result` and `detection.motion-analysis` events on
|
|
21972
|
+
* the bus.
|
|
21973
|
+
*
|
|
21974
|
+
* Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
|
|
21975
|
+
* runner has zero knowledge of other agents, no global state, and never
|
|
21976
|
+
* makes assignment decisions itself.
|
|
21977
|
+
*/
|
|
21978
|
+
/**
|
|
21979
|
+
* Why a runner declined to look at a camera out of band. Named rather than a
|
|
21980
|
+
* bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
|
|
21981
|
+
* already mid-session and one already bursting are four different facts, and a
|
|
21982
|
+
* caller told only "no" cannot tell a protection from a bug.
|
|
21983
|
+
*/
|
|
21984
|
+
var OccupancyBurstRefusalSchema = _enum([
|
|
21985
|
+
"not-attached",
|
|
21986
|
+
"battery-asleep",
|
|
21987
|
+
"not-watching",
|
|
21988
|
+
"already-bursting"
|
|
21989
|
+
]);
|
|
21796
21990
|
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
|
|
21991
|
+
deviceId: number(),
|
|
21992
|
+
/** How many frames to collect. The caller sizes this against what its
|
|
21993
|
+
* own confirmation needs; the runner clamps it to the field's range. */
|
|
21994
|
+
frames: number().int().positive().optional(),
|
|
21995
|
+
/** Why, for the log. A burst nobody can attribute is a cost nobody can
|
|
21996
|
+
* defend — the periodic one at least has a timer to point at. */
|
|
21997
|
+
reason: string()
|
|
21998
|
+
}), object({
|
|
21999
|
+
started: boolean(),
|
|
22000
|
+
refusedBecause: OccupancyBurstRefusalSchema.optional()
|
|
22001
|
+
}), {
|
|
22002
|
+
auth: "admin",
|
|
22003
|
+
kind: "mutation"
|
|
22004
|
+
}), method(object({
|
|
21797
22005
|
handle: FrameHandleSchema,
|
|
21798
22006
|
bbox: NativeCropBboxSchema,
|
|
21799
22007
|
maxWidth: number().int().positive().optional(),
|
|
@@ -28735,11 +28943,46 @@ var NativeObjectDetectionStatusSchema = object({
|
|
|
28735
28943
|
supportedClasses: array(NativeObjectClassEnum).readonly(),
|
|
28736
28944
|
/**
|
|
28737
28945
|
* Whether forwarding of onboard AI detections is enabled for this device.
|
|
28738
|
-
*
|
|
28739
|
-
*
|
|
28946
|
+
*
|
|
28947
|
+
* Cold-start OFF on every camera. A vendor does not decide which detector a
|
|
28948
|
+
* camera runs — that is the operator's choice, and it is made where every
|
|
28949
|
+
* other detection choice is made. `geometry` on
|
|
28950
|
+
* {@link NativeObjectDetectionOptionsSchema} is how the form says what the
|
|
28951
|
+
* choice would buy this particular camera.
|
|
28740
28952
|
*/
|
|
28741
28953
|
enabled: boolean()
|
|
28742
28954
|
});
|
|
28955
|
+
/**
|
|
28956
|
+
* WHEN a camera's onboard detections carry geometry.
|
|
28957
|
+
*
|
|
28958
|
+
* - `boxed` — always. The provider holds a standing channel for the boxes, so
|
|
28959
|
+
* a detection is a trackable subject whenever the camera sees one.
|
|
28960
|
+
* - `while-streaming` — only while some stream of this camera is already open.
|
|
28961
|
+
* The boxes ride the video's own side-channel rather than a feed of their
|
|
28962
|
+
* own, so they cost nothing and they exist only when something is pulling.
|
|
28963
|
+
* On Reolink this is every BATTERY camera: a persistent second feed is real
|
|
28964
|
+
* radio drain, but a camera that is awake is awake precisely because
|
|
28965
|
+
* something is in front of it, and its stream is already being pulled.
|
|
28966
|
+
* - `flag-only` — never. The firmware ships the class and nothing else; the
|
|
28967
|
+
* pipeline needs geometry to make a subject, so such a push reaches the
|
|
28968
|
+
* tracker as motion and no further.
|
|
28969
|
+
*
|
|
28970
|
+
* Reported and not inferred, because the operator's question in front of the
|
|
28971
|
+
* picker is "what do I get", and these are three different answers (D14: the
|
|
28972
|
+
* derived form is only as honest as `getOptions`).
|
|
28973
|
+
*/
|
|
28974
|
+
var NativeObjectGeometryEnum = _enum([
|
|
28975
|
+
"boxed",
|
|
28976
|
+
"while-streaming",
|
|
28977
|
+
"flag-only"
|
|
28978
|
+
]);
|
|
28979
|
+
var NativeObjectDetectionOptionsSchema = object({
|
|
28980
|
+
/** Classes this firmware can detect — the same list the status reports. */
|
|
28981
|
+
supportedClasses: array(NativeObjectClassEnum).readonly(),
|
|
28982
|
+
/** What a detection from this camera carries. */
|
|
28983
|
+
geometry: NativeObjectGeometryEnum
|
|
28984
|
+
});
|
|
28985
|
+
var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
|
|
28743
28986
|
var NativeObjectDetectionRuntimeStateSchema = NativeObjectDetectionStatusSchema.extend({
|
|
28744
28987
|
/** Required by createRuntimeStateBridge — epoch ms of last refresh. */
|
|
28745
28988
|
lastFetchedAt: number() });
|
|
@@ -28749,13 +28992,23 @@ var nativeObjectDetectionCapability = {
|
|
|
28749
28992
|
deviceNative: true,
|
|
28750
28993
|
mode: "singleton",
|
|
28751
28994
|
deviceTypes: [DeviceType.Camera],
|
|
28752
|
-
methods: {
|
|
28753
|
-
deviceId: number(),
|
|
28754
|
-
|
|
28755
|
-
|
|
28756
|
-
|
|
28757
|
-
|
|
28758
|
-
|
|
28995
|
+
methods: {
|
|
28996
|
+
getOptions: method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema),
|
|
28997
|
+
setSettings: method(object({
|
|
28998
|
+
deviceId: number(),
|
|
28999
|
+
settings: NativeObjectDetectionSettingsPatchSchema
|
|
29000
|
+
}), _void(), {
|
|
29001
|
+
kind: "mutation",
|
|
29002
|
+
auth: "admin"
|
|
29003
|
+
}),
|
|
29004
|
+
setEnabled: method(object({
|
|
29005
|
+
deviceId: number(),
|
|
29006
|
+
enabled: boolean()
|
|
29007
|
+
}), _void(), {
|
|
29008
|
+
kind: "mutation",
|
|
29009
|
+
auth: "admin"
|
|
29010
|
+
})
|
|
29011
|
+
},
|
|
28759
29012
|
events: { onDetected: { data: object({
|
|
28760
29013
|
deviceId: number(),
|
|
28761
29014
|
detection: NativeDetectionSchema
|
|
@@ -33603,7 +33856,28 @@ var PerScopeBreakdownSchema = object({
|
|
|
33603
33856
|
/** Total tracked objects in this scope (frame / zone / unzoned). */
|
|
33604
33857
|
totalObjects: number().int().nonnegative(),
|
|
33605
33858
|
/** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
|
|
33606
|
-
byClass: record(string(), number().int().nonnegative())
|
|
33859
|
+
byClass: record(string(), number().int().nonnegative()),
|
|
33860
|
+
/**
|
|
33861
|
+
* Of `totalObjects`, how many are STANDING — objects the census holds,
|
|
33862
|
+
* present but not going anywhere (a parked car, a bin, a statue).
|
|
33863
|
+
*
|
|
33864
|
+
* The operator's question about a scene has two halves and they answer
|
|
33865
|
+
* different things: "what is parked here" is stable for hours and must not
|
|
33866
|
+
* flap, while "who is walking through" is the thing an alert is about. One
|
|
33867
|
+
* number could only serve one of them, and `totalObjects` served the first
|
|
33868
|
+
* badly — a passer-by moved it and every occupancy rule saw a change.
|
|
33869
|
+
*
|
|
33870
|
+
* `standing + transient === totalObjects`, always. The total keeps its old
|
|
33871
|
+
* meaning exactly, so no rule written against it changes behaviour.
|
|
33872
|
+
*
|
|
33873
|
+
* ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
|
|
33874
|
+
* this field cannot say, and a reader that folds absence in as 0 reports "no
|
|
33875
|
+
* parked objects here" about a camera whose lot is full.
|
|
33876
|
+
*/
|
|
33877
|
+
standingObjects: number().int().nonnegative().optional(),
|
|
33878
|
+
/** Of `totalObjects`, how many are passing through. See `standingObjects` —
|
|
33879
|
+
* same absence rule. */
|
|
33880
|
+
transientObjects: number().int().nonnegative().optional()
|
|
33607
33881
|
});
|
|
33608
33882
|
var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
33609
33883
|
zoneId: string(),
|
|
@@ -33726,6 +34000,23 @@ var HistoryPointSchema = object({
|
|
|
33726
34000
|
* triple covers the three spatial scopes (per-zone, frame-wide,
|
|
33727
34001
|
* outside-any-zone). Crossing them with `className?` gives the full
|
|
33728
34002
|
* combinatorial coverage the operator UI requested.
|
|
34003
|
+
*
|
|
34004
|
+
* ## ⚠️ The three history methods are DEPRECATED
|
|
34005
|
+
*
|
|
34006
|
+
* They are served from a 60-MINUTE in-memory ring. It dies with the process,
|
|
34007
|
+
* so after a restart they report nothing about a night that did happen, and any
|
|
34008
|
+
* window past an hour is silently clamped — a caller cannot tell a clamp from
|
|
34009
|
+
* an empty scene. The presets in the occupancy chart existed to hide that
|
|
34010
|
+
* horizon rather than to answer a question.
|
|
34011
|
+
*
|
|
34012
|
+
* Use `pipelineAnalytics.readLongTermSeries({ series: 'occupancy', scope })`
|
|
34013
|
+
* instead. Those rows are already written for every camera, ~60 bytes per
|
|
34014
|
+
* (camera, scope, 5-minute bucket), kept for a year, and survive a restart.
|
|
34015
|
+
* Spell the scope with `occupancyScope(zoneId?, className?)` — the same
|
|
34016
|
+
* function the writer uses, so the two cannot drift.
|
|
34017
|
+
*
|
|
34018
|
+
* The ring stays until these three go, and it is the only thing left reading
|
|
34019
|
+
* it. Do not add a fourth caller.
|
|
33729
34020
|
*/
|
|
33730
34021
|
var zoneAnalyticsCapability = {
|
|
33731
34022
|
name: "zone-analytics",
|
|
@@ -38125,12 +38416,24 @@ Object.freeze({
|
|
|
38125
38416
|
addonId: null,
|
|
38126
38417
|
access: "create"
|
|
38127
38418
|
},
|
|
38419
|
+
"nativeObjectDetection.getOptions": {
|
|
38420
|
+
capName: "native-object-detection",
|
|
38421
|
+
capScope: "device",
|
|
38422
|
+
addonId: null,
|
|
38423
|
+
access: "view"
|
|
38424
|
+
},
|
|
38128
38425
|
"nativeObjectDetection.setEnabled": {
|
|
38129
38426
|
capName: "native-object-detection",
|
|
38130
38427
|
capScope: "device",
|
|
38131
38428
|
addonId: null,
|
|
38132
38429
|
access: "create"
|
|
38133
38430
|
},
|
|
38431
|
+
"nativeObjectDetection.setSettings": {
|
|
38432
|
+
capName: "native-object-detection",
|
|
38433
|
+
capScope: "device",
|
|
38434
|
+
addonId: null,
|
|
38435
|
+
access: "create"
|
|
38436
|
+
},
|
|
38134
38437
|
"navigation.getFeatures": {
|
|
38135
38438
|
capName: "navigation",
|
|
38136
38439
|
capScope: "device",
|
|
@@ -38395,6 +38698,12 @@ Object.freeze({
|
|
|
38395
38698
|
addonId: null,
|
|
38396
38699
|
access: "view"
|
|
38397
38700
|
},
|
|
38701
|
+
"notificationRules.getClipRetentionAsks": {
|
|
38702
|
+
capName: "notification-rules",
|
|
38703
|
+
capScope: "system",
|
|
38704
|
+
addonId: null,
|
|
38705
|
+
access: "view"
|
|
38706
|
+
},
|
|
38398
38707
|
"notificationRules.getConditionCatalog": {
|
|
38399
38708
|
capName: "notification-rules",
|
|
38400
38709
|
capScope: "system",
|
|
@@ -38905,6 +39214,12 @@ Object.freeze({
|
|
|
38905
39214
|
addonId: null,
|
|
38906
39215
|
access: "create"
|
|
38907
39216
|
},
|
|
39217
|
+
"pipelineAnalytics.readLongTermSeries": {
|
|
39218
|
+
capName: "pipeline-analytics",
|
|
39219
|
+
capScope: "device",
|
|
39220
|
+
addonId: null,
|
|
39221
|
+
access: "view"
|
|
39222
|
+
},
|
|
38908
39223
|
"pipelineAnalytics.rebuildObjectEmbeddings": {
|
|
38909
39224
|
capName: "pipeline-analytics",
|
|
38910
39225
|
capScope: "device",
|
|
@@ -39577,6 +39892,12 @@ Object.freeze({
|
|
|
39577
39892
|
addonId: null,
|
|
39578
39893
|
access: "create"
|
|
39579
39894
|
},
|
|
39895
|
+
"pipelineRunner.requestOccupancyBurst": {
|
|
39896
|
+
capName: "pipeline-runner",
|
|
39897
|
+
capScope: "system",
|
|
39898
|
+
addonId: null,
|
|
39899
|
+
access: "create"
|
|
39900
|
+
},
|
|
39580
39901
|
"pipelineRunner.runDetailSubtree": {
|
|
39581
39902
|
capName: "pipeline-runner",
|
|
39582
39903
|
capScope: "system",
|
|
@@ -42357,11 +42678,21 @@ Object.freeze({
|
|
|
42357
42678
|
form: "single",
|
|
42358
42679
|
optional: false
|
|
42359
42680
|
}],
|
|
42681
|
+
"nativeObjectDetection.getOptions": [{
|
|
42682
|
+
name: "deviceId",
|
|
42683
|
+
form: "single",
|
|
42684
|
+
optional: false
|
|
42685
|
+
}],
|
|
42360
42686
|
"nativeObjectDetection.setEnabled": [{
|
|
42361
42687
|
name: "deviceId",
|
|
42362
42688
|
form: "single",
|
|
42363
42689
|
optional: false
|
|
42364
42690
|
}],
|
|
42691
|
+
"nativeObjectDetection.setSettings": [{
|
|
42692
|
+
name: "deviceId",
|
|
42693
|
+
form: "single",
|
|
42694
|
+
optional: false
|
|
42695
|
+
}],
|
|
42365
42696
|
"navigation.getFeatures": [{
|
|
42366
42697
|
name: "deviceId",
|
|
42367
42698
|
form: "single",
|
|
@@ -42711,6 +43042,11 @@ Object.freeze({
|
|
|
42711
43042
|
form: "single",
|
|
42712
43043
|
optional: false
|
|
42713
43044
|
}],
|
|
43045
|
+
"pipelineAnalytics.readLongTermSeries": [{
|
|
43046
|
+
name: "deviceId",
|
|
43047
|
+
form: "single",
|
|
43048
|
+
optional: false
|
|
43049
|
+
}],
|
|
42714
43050
|
"pipelineAnalytics.rebuildObjectEmbeddings": [{
|
|
42715
43051
|
name: "deviceId",
|
|
42716
43052
|
form: "single",
|
|
@@ -42901,6 +43237,11 @@ Object.freeze({
|
|
|
42901
43237
|
form: "single",
|
|
42902
43238
|
optional: false
|
|
42903
43239
|
}],
|
|
43240
|
+
"pipelineRunner.requestOccupancyBurst": [{
|
|
43241
|
+
name: "deviceId",
|
|
43242
|
+
form: "single",
|
|
43243
|
+
optional: false
|
|
43244
|
+
}],
|
|
42904
43245
|
"pipelineRunner.runDetailSubtree": [{
|
|
42905
43246
|
name: "deviceId",
|
|
42906
43247
|
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.108",
|
|
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",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"dev": "vite build --watch",
|
|
72
72
|
"typecheck": "tsc --noEmit",
|
|
73
73
|
"test": "vitest run",
|
|
74
|
-
"publish": "npm publish --access public"
|
|
74
|
+
"publish:npm": "npm publish --access public"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@apocaliss92/wyze-bridge-js": "file:./vendor/apocaliss92-wyze-bridge-js-0.1.2.tgz",
|