@camstack/addon-provider-reolink 1.2.175 → 1.2.177
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 +189 -62
- package/dist/addon.mjs +189 -62
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -11101,6 +11101,57 @@ var ClipSearchStrictnessSchema = _enum([
|
|
|
11101
11101
|
"balanced",
|
|
11102
11102
|
"strict"
|
|
11103
11103
|
]);
|
|
11104
|
+
new Set([
|
|
11105
|
+
{
|
|
11106
|
+
id: "person",
|
|
11107
|
+
name: "Person"
|
|
11108
|
+
},
|
|
11109
|
+
{
|
|
11110
|
+
id: "vehicle",
|
|
11111
|
+
name: "Vehicle"
|
|
11112
|
+
},
|
|
11113
|
+
{
|
|
11114
|
+
id: "animal",
|
|
11115
|
+
name: "Animal"
|
|
11116
|
+
},
|
|
11117
|
+
{
|
|
11118
|
+
id: "package",
|
|
11119
|
+
name: "Package"
|
|
11120
|
+
}
|
|
11121
|
+
].map((l) => l.id));
|
|
11122
|
+
var COCO_TO_MACRO = {
|
|
11123
|
+
mapping: {
|
|
11124
|
+
person: "person",
|
|
11125
|
+
bicycle: "vehicle",
|
|
11126
|
+
car: "vehicle",
|
|
11127
|
+
motorcycle: "vehicle",
|
|
11128
|
+
airplane: "vehicle",
|
|
11129
|
+
bus: "vehicle",
|
|
11130
|
+
train: "vehicle",
|
|
11131
|
+
truck: "vehicle",
|
|
11132
|
+
boat: "vehicle",
|
|
11133
|
+
bird: "animal",
|
|
11134
|
+
cat: "animal",
|
|
11135
|
+
dog: "animal",
|
|
11136
|
+
horse: "animal",
|
|
11137
|
+
sheep: "animal",
|
|
11138
|
+
cow: "animal",
|
|
11139
|
+
elephant: "animal",
|
|
11140
|
+
bear: "animal",
|
|
11141
|
+
zebra: "animal",
|
|
11142
|
+
giraffe: "animal",
|
|
11143
|
+
suitcase: "package",
|
|
11144
|
+
backpack: "package",
|
|
11145
|
+
handbag: "package"
|
|
11146
|
+
},
|
|
11147
|
+
preserveOriginal: false
|
|
11148
|
+
};
|
|
11149
|
+
/** Steps a root detector may declare it fills. */
|
|
11150
|
+
var FILLABLE_STEP_IDS = [
|
|
11151
|
+
"animal-classifier",
|
|
11152
|
+
"vehicle-classifier",
|
|
11153
|
+
"package-detection"
|
|
11154
|
+
];
|
|
11104
11155
|
var MODEL_FORMATS = [
|
|
11105
11156
|
"onnx",
|
|
11106
11157
|
"coreml",
|
|
@@ -11233,6 +11284,12 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
|
|
|
11233
11284
|
/** True when the weights or their data forbid commercial use. */
|
|
11234
11285
|
nonCommercial: boolean().optional()
|
|
11235
11286
|
}));
|
|
11287
|
+
/** One `fillsClassifierEffects` entry — see `ModelCatalogEntry.fillsClassifierEffects`. */
|
|
11288
|
+
var FillsClassifierEffectSchema = object({
|
|
11289
|
+
stepId: _enum(FILLABLE_STEP_IDS),
|
|
11290
|
+
labelToEffect: record(string(), string()),
|
|
11291
|
+
uncovered: array(string()).readonly()
|
|
11292
|
+
});
|
|
11236
11293
|
var ModelCatalogEntrySchema = object({
|
|
11237
11294
|
id: string(),
|
|
11238
11295
|
name: string(),
|
|
@@ -11359,6 +11416,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
11359
11416
|
*/
|
|
11360
11417
|
classMap: DetectionCatalogClassMapSchema.optional(),
|
|
11361
11418
|
/**
|
|
11419
|
+
* The pipeline steps this ROOT detector already does the work of (D715):
|
|
11420
|
+
* its labels name the effect the step exists to produce (`deer` for
|
|
11421
|
+
* `animal-classifier`, `package` for `package-detection`). Derived from the
|
|
11422
|
+
* labelmap by `deriveClassifierFills`, never hand-written; a COCO model
|
|
11423
|
+
* declares none. Whether a declaration is honoured is the step's own
|
|
11424
|
+
* `labelLink` policy (`resolveLabelLink`). `labelToEffect` values are in
|
|
11425
|
+
* `FILLABLE_STEP_VOCABULARIES` (guard `check-label-link-vocabulary`).
|
|
11426
|
+
*/
|
|
11427
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional(),
|
|
11428
|
+
/**
|
|
11362
11429
|
* The model's CLIP feature-space contract (text encoder, dimension, vector
|
|
11363
11430
|
* index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
|
|
11364
11431
|
* CLIP image encoder; its absence is what "not a CLIP model" means to the
|
|
@@ -18722,51 +18789,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
18722
18789
|
kind: "mutation",
|
|
18723
18790
|
auth: "admin"
|
|
18724
18791
|
});
|
|
18725
|
-
new Set([
|
|
18726
|
-
{
|
|
18727
|
-
id: "person",
|
|
18728
|
-
name: "Person"
|
|
18729
|
-
},
|
|
18730
|
-
{
|
|
18731
|
-
id: "vehicle",
|
|
18732
|
-
name: "Vehicle"
|
|
18733
|
-
},
|
|
18734
|
-
{
|
|
18735
|
-
id: "animal",
|
|
18736
|
-
name: "Animal"
|
|
18737
|
-
},
|
|
18738
|
-
{
|
|
18739
|
-
id: "package",
|
|
18740
|
-
name: "Package"
|
|
18741
|
-
}
|
|
18742
|
-
].map((l) => l.id));
|
|
18743
|
-
var COCO_TO_MACRO = {
|
|
18744
|
-
mapping: {
|
|
18745
|
-
person: "person",
|
|
18746
|
-
bicycle: "vehicle",
|
|
18747
|
-
car: "vehicle",
|
|
18748
|
-
motorcycle: "vehicle",
|
|
18749
|
-
airplane: "vehicle",
|
|
18750
|
-
bus: "vehicle",
|
|
18751
|
-
train: "vehicle",
|
|
18752
|
-
truck: "vehicle",
|
|
18753
|
-
boat: "vehicle",
|
|
18754
|
-
bird: "animal",
|
|
18755
|
-
cat: "animal",
|
|
18756
|
-
dog: "animal",
|
|
18757
|
-
horse: "animal",
|
|
18758
|
-
sheep: "animal",
|
|
18759
|
-
cow: "animal",
|
|
18760
|
-
elephant: "animal",
|
|
18761
|
-
bear: "animal",
|
|
18762
|
-
zebra: "animal",
|
|
18763
|
-
giraffe: "animal",
|
|
18764
|
-
suitcase: "package",
|
|
18765
|
-
backpack: "package",
|
|
18766
|
-
handbag: "package"
|
|
18767
|
-
},
|
|
18768
|
-
preserveOriginal: false
|
|
18769
|
-
};
|
|
18770
18792
|
var AUDIO_MACRO_LABELS = [
|
|
18771
18793
|
{
|
|
18772
18794
|
id: "speech",
|
|
@@ -19562,6 +19584,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
19562
19584
|
"stream-offline",
|
|
19563
19585
|
"node-online",
|
|
19564
19586
|
"node-offline",
|
|
19587
|
+
"node-restarting",
|
|
19588
|
+
"node-updating",
|
|
19565
19589
|
"node-inference-unavailable",
|
|
19566
19590
|
"detection-blind",
|
|
19567
19591
|
"addon-crash-loop",
|
|
@@ -24208,7 +24232,8 @@ var PipelineModelOptionSchema = object({
|
|
|
24208
24232
|
group: ModelVariantGroupSchema.optional(),
|
|
24209
24233
|
legacy: boolean().optional(),
|
|
24210
24234
|
provider: ModelProviderIdSchema.optional(),
|
|
24211
|
-
defaultConfidence: number().optional()
|
|
24235
|
+
defaultConfidence: number().optional(),
|
|
24236
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional()
|
|
24212
24237
|
});
|
|
24213
24238
|
var ConfigFieldBridge = custom();
|
|
24214
24239
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -33887,7 +33912,8 @@ var MotionStatusSchema = object({
|
|
|
33887
33912
|
* writes this slice (not even a boot seed), and neither does the runner's
|
|
33888
33913
|
* phase machine: its phase is "a session is attached" for every on-motion
|
|
33889
33914
|
* session, not motion, and it now has its own `DetectionPhaseTransition`.
|
|
33890
|
-
* A provider's `isDetected`
|
|
33915
|
+
* A provider's `isDetected` and `getStatus` answer from the hub's slice
|
|
33916
|
+
* (`hubMotionNativeProvider`).
|
|
33891
33917
|
* - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
|
|
33892
33918
|
* imports) — the provider pushes both edges itself, from the upstream
|
|
33893
33919
|
* system's own state change. That is the sensor's own fact.
|
|
@@ -54105,6 +54131,8 @@ new Map(Object.entries({
|
|
|
54105
54131
|
"detection-blind": "device-events",
|
|
54106
54132
|
"node-online": "system",
|
|
54107
54133
|
"node-offline": "system",
|
|
54134
|
+
"node-restarting": "system",
|
|
54135
|
+
"node-updating": "system",
|
|
54108
54136
|
"node-inference-unavailable": "system",
|
|
54109
54137
|
"addon-crash-loop": "system",
|
|
54110
54138
|
"addon-update-available": "system",
|
|
@@ -54182,6 +54210,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
54182
54210
|
kinds: [
|
|
54183
54211
|
"node-offline",
|
|
54184
54212
|
"node-online",
|
|
54213
|
+
"node-restarting",
|
|
54185
54214
|
"node-inference-unavailable"
|
|
54186
54215
|
]
|
|
54187
54216
|
},
|
|
@@ -54203,6 +54232,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
54203
54232
|
label: "Server & container updates",
|
|
54204
54233
|
kinds: [
|
|
54205
54234
|
"server-update-available",
|
|
54235
|
+
"node-updating",
|
|
54206
54236
|
"server-updated",
|
|
54207
54237
|
"wrapper-update-available"
|
|
54208
54238
|
]
|
|
@@ -54350,6 +54380,7 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
54350
54380
|
kinds: [
|
|
54351
54381
|
"node-offline",
|
|
54352
54382
|
"node-online",
|
|
54383
|
+
"node-restarting",
|
|
54353
54384
|
"node-inference-unavailable"
|
|
54354
54385
|
]
|
|
54355
54386
|
},
|
|
@@ -54371,6 +54402,7 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
54371
54402
|
example: "server 1.2.377 → 1.2.378",
|
|
54372
54403
|
kinds: [
|
|
54373
54404
|
"server-update-available",
|
|
54405
|
+
"node-updating",
|
|
54374
54406
|
"server-updated",
|
|
54375
54407
|
"wrapper-update-available"
|
|
54376
54408
|
]
|
|
@@ -54826,10 +54858,9 @@ TimelapseRuleInputSchema.extend({
|
|
|
54826
54858
|
createdAt: number(),
|
|
54827
54859
|
updatedAt: number()
|
|
54828
54860
|
});
|
|
54829
|
-
async function
|
|
54830
|
-
let slice;
|
|
54861
|
+
async function readSlice(deviceState, deviceId) {
|
|
54831
54862
|
try {
|
|
54832
|
-
|
|
54863
|
+
return await deviceState.getCapSlice.query({
|
|
54833
54864
|
deviceId,
|
|
54834
54865
|
capName: "motion"
|
|
54835
54866
|
});
|
|
@@ -54837,10 +54868,44 @@ async function readHubMotionDetected(deviceState, deviceId) {
|
|
|
54837
54868
|
const reason = err instanceof Error ? err.message : String(err);
|
|
54838
54869
|
throw new Error(`motion state of device ${deviceId} is unreadable: ${reason}`, { cause: err });
|
|
54839
54870
|
}
|
|
54840
|
-
|
|
54871
|
+
}
|
|
54872
|
+
/**
|
|
54873
|
+
* The hub's `motion` slice as a `MotionStatus`, or `null` when nobody has
|
|
54874
|
+
* published it since the hub started. Throws when the hub does not answer or
|
|
54875
|
+
* the slice is malformed — never a made-up "no motion".
|
|
54876
|
+
*/
|
|
54877
|
+
async function readHubMotionStatus(deviceState, deviceId) {
|
|
54878
|
+
const slice = await readSlice(deviceState, deviceId);
|
|
54879
|
+
if (slice === null) return null;
|
|
54880
|
+
const parsed = MotionStatusSchema.safeParse(slice);
|
|
54881
|
+
if (!parsed.success) throw new Error(`motion state of device ${deviceId} is malformed on the hub: ${parsed.error.message}`);
|
|
54882
|
+
return parsed.data;
|
|
54883
|
+
}
|
|
54884
|
+
async function readHubMotionDetected(deviceState, deviceId) {
|
|
54885
|
+
const detected = (await readSlice(deviceState, deviceId))?.["detected"];
|
|
54841
54886
|
if (typeof detected !== "boolean") throw new Error(`motion state of device ${deviceId} is not published yet (unknown)`);
|
|
54842
54887
|
return detected;
|
|
54843
54888
|
}
|
|
54889
|
+
/**
|
|
54890
|
+
* The native `motion` provider of a camera whose slice the orchestrator writes
|
|
54891
|
+
* (D696): `isDetected` AND `getStatus`, both from the hub. `owner` names the
|
|
54892
|
+
* provider class in the deviceId-mismatch error.
|
|
54893
|
+
*/
|
|
54894
|
+
function hubMotionNativeProvider(deviceState, deviceId, owner) {
|
|
54895
|
+
const ownDevice = (asked) => {
|
|
54896
|
+
if (asked !== deviceId) throw new Error(`${owner}: motion deviceId mismatch, expected ${deviceId}, got ${asked}`);
|
|
54897
|
+
};
|
|
54898
|
+
return {
|
|
54899
|
+
isDetected: async ({ deviceId: asked }) => {
|
|
54900
|
+
ownDevice(asked);
|
|
54901
|
+
return readHubMotionDetected(deviceState, deviceId);
|
|
54902
|
+
},
|
|
54903
|
+
getStatus: async ({ deviceId: asked }) => {
|
|
54904
|
+
ownDevice(asked);
|
|
54905
|
+
return readHubMotionStatus(deviceState, deviceId);
|
|
54906
|
+
}
|
|
54907
|
+
};
|
|
54908
|
+
}
|
|
54844
54909
|
MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
|
|
54845
54910
|
function providerMotionEvent(source, data) {
|
|
54846
54911
|
return createEvent(EventCategory.ProviderMotion, source, data);
|
|
@@ -166310,6 +166375,37 @@ function buildSyntheticRfc4571Sdp(input) {
|
|
|
166310
166375
|
function buildLazyRfc4571Url(camStreamId) {
|
|
166311
166376
|
return `lazy:rfc4571:${encodeURIComponent(camStreamId)}`;
|
|
166312
166377
|
}
|
|
166378
|
+
function createMotionSensorTracker(now = Date.now) {
|
|
166379
|
+
const latest = /* @__PURE__ */ new Map();
|
|
166380
|
+
return {
|
|
166381
|
+
observe(event) {
|
|
166382
|
+
if (event.type !== "motion") return;
|
|
166383
|
+
const sensor = event.motion.source;
|
|
166384
|
+
if (sensor === void 0) return;
|
|
166385
|
+
latest.set(event.channel, {
|
|
166386
|
+
sensor,
|
|
166387
|
+
at: now()
|
|
166388
|
+
});
|
|
166389
|
+
},
|
|
166390
|
+
take(channel) {
|
|
166391
|
+
const entry = latest.get(channel);
|
|
166392
|
+
if (!entry) return null;
|
|
166393
|
+
latest.delete(channel);
|
|
166394
|
+
return now() - entry.at <= 2e3 ? entry.sensor : null;
|
|
166395
|
+
}
|
|
166396
|
+
};
|
|
166397
|
+
}
|
|
166398
|
+
var isMotionSensor = (value) => value === "md" || value === "pir" || value === "unknown";
|
|
166399
|
+
/**
|
|
166400
|
+
* The sensor a simple event states itself, for a library version that forwards
|
|
166401
|
+
* `motion.source` on the simple event; `null` for the versions that do not.
|
|
166402
|
+
*/
|
|
166403
|
+
function motionSensorOnSimpleEvent(event) {
|
|
166404
|
+
if (!("motion" in event)) return null;
|
|
166405
|
+
const motion = event.motion;
|
|
166406
|
+
if (typeof motion !== "object" || motion === null || !("source" in motion)) return null;
|
|
166407
|
+
return isMotionSensor(motion.source) ? motion.source : null;
|
|
166408
|
+
}
|
|
166313
166409
|
//#endregion
|
|
166314
166410
|
//#region src/reolink-camera.ts
|
|
166315
166411
|
/**
|
|
@@ -166616,6 +166712,16 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
166616
166712
|
this.handleSimpleEvent(event);
|
|
166617
166713
|
};
|
|
166618
166714
|
/**
|
|
166715
|
+
* MD vs PIR of this camera's own motion pushes. The lib's simple event drops
|
|
166716
|
+
* it, so the raw client `event` is observed — PREPENDED, so the sensor is
|
|
166717
|
+
* recorded before the API maps the same emit into the simple event
|
|
166718
|
+
* (`motion-sensor-source.ts`). Hub children get theirs from the hub.
|
|
166719
|
+
*/
|
|
166720
|
+
motionSensors = createMotionSensorTracker();
|
|
166721
|
+
observeRawEventBound = (event) => {
|
|
166722
|
+
this.motionSensors.observe(event);
|
|
166723
|
+
};
|
|
166724
|
+
/**
|
|
166619
166725
|
* Stable bound handler for `api.onObjectDetections` — created ONCE so
|
|
166620
166726
|
* every `onObjectDetections` / `offObjectDetections` call uses the
|
|
166621
166727
|
* SAME function reference (same listener-Set dedup rationale as
|
|
@@ -166919,16 +167025,14 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
166919
167025
|
* `handleSimpleEvent` emits its raw push as `ProviderMotion` (`source:
|
|
166920
167026
|
* 'onboard'`, D673), the orchestrator's camera-sensors bridge turns it into
|
|
166921
167027
|
* the effective `MotionOnMotionChanged`, and the orchestrator is the slice's
|
|
166922
|
-
* only writer, with that effective value, on the hub. `isDetected`
|
|
166923
|
-
* therefore
|
|
166924
|
-
*
|
|
166925
|
-
*
|
|
167028
|
+
* only writer, with that effective value, on the hub. `isDetected` AND
|
|
167029
|
+
* `getStatus` therefore answer from the HUB's slice
|
|
167030
|
+
* (`hubMotionNativeProvider`); an unreadable hub fails both. Never `false`
|
|
167031
|
+
* for "unknown" (D49, D393).
|
|
166926
167032
|
*/
|
|
166927
167033
|
registerMotionCap() {
|
|
166928
|
-
this.ctx.
|
|
166929
|
-
|
|
166930
|
-
return readHubMotionDetected(this.ctx.api.deviceState, this.id);
|
|
166931
|
-
} });
|
|
167034
|
+
const motionProvider = hubMotionNativeProvider(this.ctx.api.deviceState, this.id, "ReolinkCamera");
|
|
167035
|
+
this.ctx.registerNativeCap(motionCapability, motionProvider);
|
|
166932
167036
|
}
|
|
166933
167037
|
registerNetworkLink() {
|
|
166934
167038
|
this.ctx.registerNativeCap(networkLinkCapability, { getStatus: async ({ deviceId }) => {
|
|
@@ -172017,6 +172121,11 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
172017
172121
|
this.ctx.logger.debug("Reolink event watchdog re-subscribe failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
172018
172122
|
}
|
|
172019
172123
|
}
|
|
172124
|
+
/** Off-then-prepend, so a re-attached api never carries two observers. */
|
|
172125
|
+
observeMotionSensors(api) {
|
|
172126
|
+
api.client.off("event", this.observeRawEventBound);
|
|
172127
|
+
api.client.prependListener("event", this.observeRawEventBound);
|
|
172128
|
+
}
|
|
172020
172129
|
/**
|
|
172021
172130
|
* Subscribe (or re-subscribe) `handleSimpleEventBound` against
|
|
172022
172131
|
* the Baichuan API. Single-source-of-truth for the listener
|
|
@@ -173611,6 +173720,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173611
173720
|
adoptApi(api) {
|
|
173612
173721
|
this.api = api;
|
|
173613
173722
|
this.reconnectAttempts = 0;
|
|
173723
|
+
this.observeMotionSensors(api);
|
|
173614
173724
|
api.client.on("close", () => this.scheduleReconnect("close"));
|
|
173615
173725
|
api.client.on("error", (err) => {
|
|
173616
173726
|
this.ctx.logger.warn("Baichuan client error — scheduling reconnect", {
|
|
@@ -173758,6 +173868,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173758
173868
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
173759
173869
|
});
|
|
173760
173870
|
});
|
|
173871
|
+
this.observeMotionSensors(api);
|
|
173761
173872
|
api.client.on("close", () => this.scheduleReconnect("close"));
|
|
173762
173873
|
api.client.on("error", (err) => {
|
|
173763
173874
|
this.ctx.logger.warn("Baichuan client error — scheduling reconnect", {
|
|
@@ -173827,8 +173938,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173827
173938
|
* when the camera lives under a Hub and inherits the parent's
|
|
173828
173939
|
* single subscription.
|
|
173829
173940
|
*/
|
|
173830
|
-
handleSimpleEvent(event) {
|
|
173941
|
+
handleSimpleEvent(event, routedMotionSensor = null) {
|
|
173831
173942
|
this.lastEventAt = Date.now();
|
|
173943
|
+
const motionSensor = event.type === "motion" ? motionSensorOnSimpleEvent(event) ?? routedMotionSensor ?? this.motionSensors.take(event.channel) : null;
|
|
173832
173944
|
this.consecutiveStaleHealthChecks = 0;
|
|
173833
173945
|
this.nextEventHealthCheckAt = 0;
|
|
173834
173946
|
this.markPassiveContact();
|
|
@@ -173847,7 +173959,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173847
173959
|
type: event.type,
|
|
173848
173960
|
channel: event.channel,
|
|
173849
173961
|
timestamp: event.timestamp,
|
|
173850
|
-
source: event.source
|
|
173962
|
+
source: event.source,
|
|
173963
|
+
...event.type === "motion" ? { motionSensor } : {}
|
|
173851
173964
|
} });
|
|
173852
173965
|
if (event.type === "motion") {
|
|
173853
173966
|
const now = Date.now();
|
|
@@ -174406,6 +174519,17 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
174406
174519
|
handleSimpleEventBound = (ev) => {
|
|
174407
174520
|
this.routeSimpleEvent(ev);
|
|
174408
174521
|
};
|
|
174522
|
+
/** MD vs PIR per channel, read off the raw client event the lib's simple
|
|
174523
|
+
* event drops (`motion-sensor-source.ts`) and handed to the child. */
|
|
174524
|
+
motionSensors = createMotionSensorTracker();
|
|
174525
|
+
observeRawEventBound = (ev) => {
|
|
174526
|
+
this.motionSensors.observe(ev);
|
|
174527
|
+
};
|
|
174528
|
+
/** Off-then-prepend: recorded before the API maps the same emit. */
|
|
174529
|
+
observeMotionSensors(api) {
|
|
174530
|
+
api.client.off("event", this.observeRawEventBound);
|
|
174531
|
+
api.client.prependListener("event", this.observeRawEventBound);
|
|
174532
|
+
}
|
|
174409
174533
|
constructor(ctx) {
|
|
174410
174534
|
super(ctx, reolinkHubSchema, { type: DeviceType.Hub });
|
|
174411
174535
|
this.registerDeviceDiscovery();
|
|
@@ -175169,6 +175293,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175169
175293
|
routeSimpleEvent(ev) {
|
|
175170
175294
|
const channel = ev?.channel;
|
|
175171
175295
|
if (typeof channel !== "number") return;
|
|
175296
|
+
const motionSensor = ev.type === "motion" ? this.motionSensors.take(channel) : null;
|
|
175172
175297
|
const deviceId = this.channelToDeviceId.get(channel);
|
|
175173
175298
|
if (deviceId === void 0) {
|
|
175174
175299
|
const last = this.unroutedChannelLoggedAt.get(channel) ?? 0;
|
|
@@ -175198,7 +175323,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175198
175323
|
deviceId: child.id,
|
|
175199
175324
|
name: child.name
|
|
175200
175325
|
} });
|
|
175201
|
-
child.handleSimpleEvent(ev);
|
|
175326
|
+
child.handleSimpleEvent(ev, motionSensor);
|
|
175202
175327
|
}).catch((err) => {
|
|
175203
175328
|
this.ctx.logger.debug("Hub simpleEvent dispatch failed", { meta: {
|
|
175204
175329
|
channel,
|
|
@@ -175242,6 +175367,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175242
175367
|
try {
|
|
175243
175368
|
await api.offSimpleEvent(this.handleSimpleEventBound);
|
|
175244
175369
|
} catch {}
|
|
175370
|
+
this.observeMotionSensors(api);
|
|
175245
175371
|
await api.onSimpleEvent(this.handleSimpleEventBound);
|
|
175246
175372
|
api.client.on("close", () => this.scheduleReconnect("socket-close"));
|
|
175247
175373
|
api.client.on("error", (err) => {
|
|
@@ -175300,6 +175426,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175300
175426
|
this.api = api;
|
|
175301
175427
|
this.reconnectAttempts = 0;
|
|
175302
175428
|
this.markOnline(true);
|
|
175429
|
+
this.observeMotionSensors(api);
|
|
175303
175430
|
api.client.on("close", () => this.scheduleReconnect("socket-close"));
|
|
175304
175431
|
api.client.on("error", (err) => {
|
|
175305
175432
|
this.ctx.logger.warn("Reolink Hub adopted socket error", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
package/dist/addon.mjs
CHANGED
|
@@ -11093,6 +11093,57 @@ var ClipSearchStrictnessSchema = _enum([
|
|
|
11093
11093
|
"balanced",
|
|
11094
11094
|
"strict"
|
|
11095
11095
|
]);
|
|
11096
|
+
new Set([
|
|
11097
|
+
{
|
|
11098
|
+
id: "person",
|
|
11099
|
+
name: "Person"
|
|
11100
|
+
},
|
|
11101
|
+
{
|
|
11102
|
+
id: "vehicle",
|
|
11103
|
+
name: "Vehicle"
|
|
11104
|
+
},
|
|
11105
|
+
{
|
|
11106
|
+
id: "animal",
|
|
11107
|
+
name: "Animal"
|
|
11108
|
+
},
|
|
11109
|
+
{
|
|
11110
|
+
id: "package",
|
|
11111
|
+
name: "Package"
|
|
11112
|
+
}
|
|
11113
|
+
].map((l) => l.id));
|
|
11114
|
+
var COCO_TO_MACRO = {
|
|
11115
|
+
mapping: {
|
|
11116
|
+
person: "person",
|
|
11117
|
+
bicycle: "vehicle",
|
|
11118
|
+
car: "vehicle",
|
|
11119
|
+
motorcycle: "vehicle",
|
|
11120
|
+
airplane: "vehicle",
|
|
11121
|
+
bus: "vehicle",
|
|
11122
|
+
train: "vehicle",
|
|
11123
|
+
truck: "vehicle",
|
|
11124
|
+
boat: "vehicle",
|
|
11125
|
+
bird: "animal",
|
|
11126
|
+
cat: "animal",
|
|
11127
|
+
dog: "animal",
|
|
11128
|
+
horse: "animal",
|
|
11129
|
+
sheep: "animal",
|
|
11130
|
+
cow: "animal",
|
|
11131
|
+
elephant: "animal",
|
|
11132
|
+
bear: "animal",
|
|
11133
|
+
zebra: "animal",
|
|
11134
|
+
giraffe: "animal",
|
|
11135
|
+
suitcase: "package",
|
|
11136
|
+
backpack: "package",
|
|
11137
|
+
handbag: "package"
|
|
11138
|
+
},
|
|
11139
|
+
preserveOriginal: false
|
|
11140
|
+
};
|
|
11141
|
+
/** Steps a root detector may declare it fills. */
|
|
11142
|
+
var FILLABLE_STEP_IDS = [
|
|
11143
|
+
"animal-classifier",
|
|
11144
|
+
"vehicle-classifier",
|
|
11145
|
+
"package-detection"
|
|
11146
|
+
];
|
|
11096
11147
|
var MODEL_FORMATS = [
|
|
11097
11148
|
"onnx",
|
|
11098
11149
|
"coreml",
|
|
@@ -11225,6 +11276,12 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
|
|
|
11225
11276
|
/** True when the weights or their data forbid commercial use. */
|
|
11226
11277
|
nonCommercial: boolean().optional()
|
|
11227
11278
|
}));
|
|
11279
|
+
/** One `fillsClassifierEffects` entry — see `ModelCatalogEntry.fillsClassifierEffects`. */
|
|
11280
|
+
var FillsClassifierEffectSchema = object({
|
|
11281
|
+
stepId: _enum(FILLABLE_STEP_IDS),
|
|
11282
|
+
labelToEffect: record(string(), string()),
|
|
11283
|
+
uncovered: array(string()).readonly()
|
|
11284
|
+
});
|
|
11228
11285
|
var ModelCatalogEntrySchema = object({
|
|
11229
11286
|
id: string(),
|
|
11230
11287
|
name: string(),
|
|
@@ -11351,6 +11408,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
11351
11408
|
*/
|
|
11352
11409
|
classMap: DetectionCatalogClassMapSchema.optional(),
|
|
11353
11410
|
/**
|
|
11411
|
+
* The pipeline steps this ROOT detector already does the work of (D715):
|
|
11412
|
+
* its labels name the effect the step exists to produce (`deer` for
|
|
11413
|
+
* `animal-classifier`, `package` for `package-detection`). Derived from the
|
|
11414
|
+
* labelmap by `deriveClassifierFills`, never hand-written; a COCO model
|
|
11415
|
+
* declares none. Whether a declaration is honoured is the step's own
|
|
11416
|
+
* `labelLink` policy (`resolveLabelLink`). `labelToEffect` values are in
|
|
11417
|
+
* `FILLABLE_STEP_VOCABULARIES` (guard `check-label-link-vocabulary`).
|
|
11418
|
+
*/
|
|
11419
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional(),
|
|
11420
|
+
/**
|
|
11354
11421
|
* The model's CLIP feature-space contract (text encoder, dimension, vector
|
|
11355
11422
|
* index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
|
|
11356
11423
|
* CLIP image encoder; its absence is what "not a CLIP model" means to the
|
|
@@ -18714,51 +18781,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
18714
18781
|
kind: "mutation",
|
|
18715
18782
|
auth: "admin"
|
|
18716
18783
|
});
|
|
18717
|
-
new Set([
|
|
18718
|
-
{
|
|
18719
|
-
id: "person",
|
|
18720
|
-
name: "Person"
|
|
18721
|
-
},
|
|
18722
|
-
{
|
|
18723
|
-
id: "vehicle",
|
|
18724
|
-
name: "Vehicle"
|
|
18725
|
-
},
|
|
18726
|
-
{
|
|
18727
|
-
id: "animal",
|
|
18728
|
-
name: "Animal"
|
|
18729
|
-
},
|
|
18730
|
-
{
|
|
18731
|
-
id: "package",
|
|
18732
|
-
name: "Package"
|
|
18733
|
-
}
|
|
18734
|
-
].map((l) => l.id));
|
|
18735
|
-
var COCO_TO_MACRO = {
|
|
18736
|
-
mapping: {
|
|
18737
|
-
person: "person",
|
|
18738
|
-
bicycle: "vehicle",
|
|
18739
|
-
car: "vehicle",
|
|
18740
|
-
motorcycle: "vehicle",
|
|
18741
|
-
airplane: "vehicle",
|
|
18742
|
-
bus: "vehicle",
|
|
18743
|
-
train: "vehicle",
|
|
18744
|
-
truck: "vehicle",
|
|
18745
|
-
boat: "vehicle",
|
|
18746
|
-
bird: "animal",
|
|
18747
|
-
cat: "animal",
|
|
18748
|
-
dog: "animal",
|
|
18749
|
-
horse: "animal",
|
|
18750
|
-
sheep: "animal",
|
|
18751
|
-
cow: "animal",
|
|
18752
|
-
elephant: "animal",
|
|
18753
|
-
bear: "animal",
|
|
18754
|
-
zebra: "animal",
|
|
18755
|
-
giraffe: "animal",
|
|
18756
|
-
suitcase: "package",
|
|
18757
|
-
backpack: "package",
|
|
18758
|
-
handbag: "package"
|
|
18759
|
-
},
|
|
18760
|
-
preserveOriginal: false
|
|
18761
|
-
};
|
|
18762
18784
|
var AUDIO_MACRO_LABELS = [
|
|
18763
18785
|
{
|
|
18764
18786
|
id: "speech",
|
|
@@ -19554,6 +19576,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
19554
19576
|
"stream-offline",
|
|
19555
19577
|
"node-online",
|
|
19556
19578
|
"node-offline",
|
|
19579
|
+
"node-restarting",
|
|
19580
|
+
"node-updating",
|
|
19557
19581
|
"node-inference-unavailable",
|
|
19558
19582
|
"detection-blind",
|
|
19559
19583
|
"addon-crash-loop",
|
|
@@ -24200,7 +24224,8 @@ var PipelineModelOptionSchema = object({
|
|
|
24200
24224
|
group: ModelVariantGroupSchema.optional(),
|
|
24201
24225
|
legacy: boolean().optional(),
|
|
24202
24226
|
provider: ModelProviderIdSchema.optional(),
|
|
24203
|
-
defaultConfidence: number().optional()
|
|
24227
|
+
defaultConfidence: number().optional(),
|
|
24228
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional()
|
|
24204
24229
|
});
|
|
24205
24230
|
var ConfigFieldBridge = custom();
|
|
24206
24231
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -33879,7 +33904,8 @@ var MotionStatusSchema = object({
|
|
|
33879
33904
|
* writes this slice (not even a boot seed), and neither does the runner's
|
|
33880
33905
|
* phase machine: its phase is "a session is attached" for every on-motion
|
|
33881
33906
|
* session, not motion, and it now has its own `DetectionPhaseTransition`.
|
|
33882
|
-
* A provider's `isDetected`
|
|
33907
|
+
* A provider's `isDetected` and `getStatus` answer from the hub's slice
|
|
33908
|
+
* (`hubMotionNativeProvider`).
|
|
33883
33909
|
* - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
|
|
33884
33910
|
* imports) — the provider pushes both edges itself, from the upstream
|
|
33885
33911
|
* system's own state change. That is the sensor's own fact.
|
|
@@ -54097,6 +54123,8 @@ new Map(Object.entries({
|
|
|
54097
54123
|
"detection-blind": "device-events",
|
|
54098
54124
|
"node-online": "system",
|
|
54099
54125
|
"node-offline": "system",
|
|
54126
|
+
"node-restarting": "system",
|
|
54127
|
+
"node-updating": "system",
|
|
54100
54128
|
"node-inference-unavailable": "system",
|
|
54101
54129
|
"addon-crash-loop": "system",
|
|
54102
54130
|
"addon-update-available": "system",
|
|
@@ -54174,6 +54202,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
54174
54202
|
kinds: [
|
|
54175
54203
|
"node-offline",
|
|
54176
54204
|
"node-online",
|
|
54205
|
+
"node-restarting",
|
|
54177
54206
|
"node-inference-unavailable"
|
|
54178
54207
|
]
|
|
54179
54208
|
},
|
|
@@ -54195,6 +54224,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
54195
54224
|
label: "Server & container updates",
|
|
54196
54225
|
kinds: [
|
|
54197
54226
|
"server-update-available",
|
|
54227
|
+
"node-updating",
|
|
54198
54228
|
"server-updated",
|
|
54199
54229
|
"wrapper-update-available"
|
|
54200
54230
|
]
|
|
@@ -54342,6 +54372,7 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
54342
54372
|
kinds: [
|
|
54343
54373
|
"node-offline",
|
|
54344
54374
|
"node-online",
|
|
54375
|
+
"node-restarting",
|
|
54345
54376
|
"node-inference-unavailable"
|
|
54346
54377
|
]
|
|
54347
54378
|
},
|
|
@@ -54363,6 +54394,7 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
54363
54394
|
example: "server 1.2.377 → 1.2.378",
|
|
54364
54395
|
kinds: [
|
|
54365
54396
|
"server-update-available",
|
|
54397
|
+
"node-updating",
|
|
54366
54398
|
"server-updated",
|
|
54367
54399
|
"wrapper-update-available"
|
|
54368
54400
|
]
|
|
@@ -54818,10 +54850,9 @@ TimelapseRuleInputSchema.extend({
|
|
|
54818
54850
|
createdAt: number(),
|
|
54819
54851
|
updatedAt: number()
|
|
54820
54852
|
});
|
|
54821
|
-
async function
|
|
54822
|
-
let slice;
|
|
54853
|
+
async function readSlice(deviceState, deviceId) {
|
|
54823
54854
|
try {
|
|
54824
|
-
|
|
54855
|
+
return await deviceState.getCapSlice.query({
|
|
54825
54856
|
deviceId,
|
|
54826
54857
|
capName: "motion"
|
|
54827
54858
|
});
|
|
@@ -54829,10 +54860,44 @@ async function readHubMotionDetected(deviceState, deviceId) {
|
|
|
54829
54860
|
const reason = err instanceof Error ? err.message : String(err);
|
|
54830
54861
|
throw new Error(`motion state of device ${deviceId} is unreadable: ${reason}`, { cause: err });
|
|
54831
54862
|
}
|
|
54832
|
-
|
|
54863
|
+
}
|
|
54864
|
+
/**
|
|
54865
|
+
* The hub's `motion` slice as a `MotionStatus`, or `null` when nobody has
|
|
54866
|
+
* published it since the hub started. Throws when the hub does not answer or
|
|
54867
|
+
* the slice is malformed — never a made-up "no motion".
|
|
54868
|
+
*/
|
|
54869
|
+
async function readHubMotionStatus(deviceState, deviceId) {
|
|
54870
|
+
const slice = await readSlice(deviceState, deviceId);
|
|
54871
|
+
if (slice === null) return null;
|
|
54872
|
+
const parsed = MotionStatusSchema.safeParse(slice);
|
|
54873
|
+
if (!parsed.success) throw new Error(`motion state of device ${deviceId} is malformed on the hub: ${parsed.error.message}`);
|
|
54874
|
+
return parsed.data;
|
|
54875
|
+
}
|
|
54876
|
+
async function readHubMotionDetected(deviceState, deviceId) {
|
|
54877
|
+
const detected = (await readSlice(deviceState, deviceId))?.["detected"];
|
|
54833
54878
|
if (typeof detected !== "boolean") throw new Error(`motion state of device ${deviceId} is not published yet (unknown)`);
|
|
54834
54879
|
return detected;
|
|
54835
54880
|
}
|
|
54881
|
+
/**
|
|
54882
|
+
* The native `motion` provider of a camera whose slice the orchestrator writes
|
|
54883
|
+
* (D696): `isDetected` AND `getStatus`, both from the hub. `owner` names the
|
|
54884
|
+
* provider class in the deviceId-mismatch error.
|
|
54885
|
+
*/
|
|
54886
|
+
function hubMotionNativeProvider(deviceState, deviceId, owner) {
|
|
54887
|
+
const ownDevice = (asked) => {
|
|
54888
|
+
if (asked !== deviceId) throw new Error(`${owner}: motion deviceId mismatch, expected ${deviceId}, got ${asked}`);
|
|
54889
|
+
};
|
|
54890
|
+
return {
|
|
54891
|
+
isDetected: async ({ deviceId: asked }) => {
|
|
54892
|
+
ownDevice(asked);
|
|
54893
|
+
return readHubMotionDetected(deviceState, deviceId);
|
|
54894
|
+
},
|
|
54895
|
+
getStatus: async ({ deviceId: asked }) => {
|
|
54896
|
+
ownDevice(asked);
|
|
54897
|
+
return readHubMotionStatus(deviceState, deviceId);
|
|
54898
|
+
}
|
|
54899
|
+
};
|
|
54900
|
+
}
|
|
54836
54901
|
MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
|
|
54837
54902
|
function providerMotionEvent(source, data) {
|
|
54838
54903
|
return createEvent(EventCategory.ProviderMotion, source, data);
|
|
@@ -166302,6 +166367,37 @@ function buildSyntheticRfc4571Sdp(input) {
|
|
|
166302
166367
|
function buildLazyRfc4571Url(camStreamId) {
|
|
166303
166368
|
return `lazy:rfc4571:${encodeURIComponent(camStreamId)}`;
|
|
166304
166369
|
}
|
|
166370
|
+
function createMotionSensorTracker(now = Date.now) {
|
|
166371
|
+
const latest = /* @__PURE__ */ new Map();
|
|
166372
|
+
return {
|
|
166373
|
+
observe(event) {
|
|
166374
|
+
if (event.type !== "motion") return;
|
|
166375
|
+
const sensor = event.motion.source;
|
|
166376
|
+
if (sensor === void 0) return;
|
|
166377
|
+
latest.set(event.channel, {
|
|
166378
|
+
sensor,
|
|
166379
|
+
at: now()
|
|
166380
|
+
});
|
|
166381
|
+
},
|
|
166382
|
+
take(channel) {
|
|
166383
|
+
const entry = latest.get(channel);
|
|
166384
|
+
if (!entry) return null;
|
|
166385
|
+
latest.delete(channel);
|
|
166386
|
+
return now() - entry.at <= 2e3 ? entry.sensor : null;
|
|
166387
|
+
}
|
|
166388
|
+
};
|
|
166389
|
+
}
|
|
166390
|
+
var isMotionSensor = (value) => value === "md" || value === "pir" || value === "unknown";
|
|
166391
|
+
/**
|
|
166392
|
+
* The sensor a simple event states itself, for a library version that forwards
|
|
166393
|
+
* `motion.source` on the simple event; `null` for the versions that do not.
|
|
166394
|
+
*/
|
|
166395
|
+
function motionSensorOnSimpleEvent(event) {
|
|
166396
|
+
if (!("motion" in event)) return null;
|
|
166397
|
+
const motion = event.motion;
|
|
166398
|
+
if (typeof motion !== "object" || motion === null || !("source" in motion)) return null;
|
|
166399
|
+
return isMotionSensor(motion.source) ? motion.source : null;
|
|
166400
|
+
}
|
|
166305
166401
|
//#endregion
|
|
166306
166402
|
//#region src/reolink-camera.ts
|
|
166307
166403
|
/**
|
|
@@ -166608,6 +166704,16 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
166608
166704
|
this.handleSimpleEvent(event);
|
|
166609
166705
|
};
|
|
166610
166706
|
/**
|
|
166707
|
+
* MD vs PIR of this camera's own motion pushes. The lib's simple event drops
|
|
166708
|
+
* it, so the raw client `event` is observed — PREPENDED, so the sensor is
|
|
166709
|
+
* recorded before the API maps the same emit into the simple event
|
|
166710
|
+
* (`motion-sensor-source.ts`). Hub children get theirs from the hub.
|
|
166711
|
+
*/
|
|
166712
|
+
motionSensors = createMotionSensorTracker();
|
|
166713
|
+
observeRawEventBound = (event) => {
|
|
166714
|
+
this.motionSensors.observe(event);
|
|
166715
|
+
};
|
|
166716
|
+
/**
|
|
166611
166717
|
* Stable bound handler for `api.onObjectDetections` — created ONCE so
|
|
166612
166718
|
* every `onObjectDetections` / `offObjectDetections` call uses the
|
|
166613
166719
|
* SAME function reference (same listener-Set dedup rationale as
|
|
@@ -166911,16 +167017,14 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
166911
167017
|
* `handleSimpleEvent` emits its raw push as `ProviderMotion` (`source:
|
|
166912
167018
|
* 'onboard'`, D673), the orchestrator's camera-sensors bridge turns it into
|
|
166913
167019
|
* the effective `MotionOnMotionChanged`, and the orchestrator is the slice's
|
|
166914
|
-
* only writer, with that effective value, on the hub. `isDetected`
|
|
166915
|
-
* therefore
|
|
166916
|
-
*
|
|
166917
|
-
*
|
|
167020
|
+
* only writer, with that effective value, on the hub. `isDetected` AND
|
|
167021
|
+
* `getStatus` therefore answer from the HUB's slice
|
|
167022
|
+
* (`hubMotionNativeProvider`); an unreadable hub fails both. Never `false`
|
|
167023
|
+
* for "unknown" (D49, D393).
|
|
166918
167024
|
*/
|
|
166919
167025
|
registerMotionCap() {
|
|
166920
|
-
this.ctx.
|
|
166921
|
-
|
|
166922
|
-
return readHubMotionDetected(this.ctx.api.deviceState, this.id);
|
|
166923
|
-
} });
|
|
167026
|
+
const motionProvider = hubMotionNativeProvider(this.ctx.api.deviceState, this.id, "ReolinkCamera");
|
|
167027
|
+
this.ctx.registerNativeCap(motionCapability, motionProvider);
|
|
166924
167028
|
}
|
|
166925
167029
|
registerNetworkLink() {
|
|
166926
167030
|
this.ctx.registerNativeCap(networkLinkCapability, { getStatus: async ({ deviceId }) => {
|
|
@@ -172009,6 +172113,11 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
172009
172113
|
this.ctx.logger.debug("Reolink event watchdog re-subscribe failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
172010
172114
|
}
|
|
172011
172115
|
}
|
|
172116
|
+
/** Off-then-prepend, so a re-attached api never carries two observers. */
|
|
172117
|
+
observeMotionSensors(api) {
|
|
172118
|
+
api.client.off("event", this.observeRawEventBound);
|
|
172119
|
+
api.client.prependListener("event", this.observeRawEventBound);
|
|
172120
|
+
}
|
|
172012
172121
|
/**
|
|
172013
172122
|
* Subscribe (or re-subscribe) `handleSimpleEventBound` against
|
|
172014
172123
|
* the Baichuan API. Single-source-of-truth for the listener
|
|
@@ -173603,6 +173712,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173603
173712
|
adoptApi(api) {
|
|
173604
173713
|
this.api = api;
|
|
173605
173714
|
this.reconnectAttempts = 0;
|
|
173715
|
+
this.observeMotionSensors(api);
|
|
173606
173716
|
api.client.on("close", () => this.scheduleReconnect("close"));
|
|
173607
173717
|
api.client.on("error", (err) => {
|
|
173608
173718
|
this.ctx.logger.warn("Baichuan client error — scheduling reconnect", {
|
|
@@ -173750,6 +173860,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173750
173860
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
173751
173861
|
});
|
|
173752
173862
|
});
|
|
173863
|
+
this.observeMotionSensors(api);
|
|
173753
173864
|
api.client.on("close", () => this.scheduleReconnect("close"));
|
|
173754
173865
|
api.client.on("error", (err) => {
|
|
173755
173866
|
this.ctx.logger.warn("Baichuan client error — scheduling reconnect", {
|
|
@@ -173819,8 +173930,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173819
173930
|
* when the camera lives under a Hub and inherits the parent's
|
|
173820
173931
|
* single subscription.
|
|
173821
173932
|
*/
|
|
173822
|
-
handleSimpleEvent(event) {
|
|
173933
|
+
handleSimpleEvent(event, routedMotionSensor = null) {
|
|
173823
173934
|
this.lastEventAt = Date.now();
|
|
173935
|
+
const motionSensor = event.type === "motion" ? motionSensorOnSimpleEvent(event) ?? routedMotionSensor ?? this.motionSensors.take(event.channel) : null;
|
|
173824
173936
|
this.consecutiveStaleHealthChecks = 0;
|
|
173825
173937
|
this.nextEventHealthCheckAt = 0;
|
|
173826
173938
|
this.markPassiveContact();
|
|
@@ -173839,7 +173951,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173839
173951
|
type: event.type,
|
|
173840
173952
|
channel: event.channel,
|
|
173841
173953
|
timestamp: event.timestamp,
|
|
173842
|
-
source: event.source
|
|
173954
|
+
source: event.source,
|
|
173955
|
+
...event.type === "motion" ? { motionSensor } : {}
|
|
173843
173956
|
} });
|
|
173844
173957
|
if (event.type === "motion") {
|
|
173845
173958
|
const now = Date.now();
|
|
@@ -174398,6 +174511,17 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
174398
174511
|
handleSimpleEventBound = (ev) => {
|
|
174399
174512
|
this.routeSimpleEvent(ev);
|
|
174400
174513
|
};
|
|
174514
|
+
/** MD vs PIR per channel, read off the raw client event the lib's simple
|
|
174515
|
+
* event drops (`motion-sensor-source.ts`) and handed to the child. */
|
|
174516
|
+
motionSensors = createMotionSensorTracker();
|
|
174517
|
+
observeRawEventBound = (ev) => {
|
|
174518
|
+
this.motionSensors.observe(ev);
|
|
174519
|
+
};
|
|
174520
|
+
/** Off-then-prepend: recorded before the API maps the same emit. */
|
|
174521
|
+
observeMotionSensors(api) {
|
|
174522
|
+
api.client.off("event", this.observeRawEventBound);
|
|
174523
|
+
api.client.prependListener("event", this.observeRawEventBound);
|
|
174524
|
+
}
|
|
174401
174525
|
constructor(ctx) {
|
|
174402
174526
|
super(ctx, reolinkHubSchema, { type: DeviceType.Hub });
|
|
174403
174527
|
this.registerDeviceDiscovery();
|
|
@@ -175161,6 +175285,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175161
175285
|
routeSimpleEvent(ev) {
|
|
175162
175286
|
const channel = ev?.channel;
|
|
175163
175287
|
if (typeof channel !== "number") return;
|
|
175288
|
+
const motionSensor = ev.type === "motion" ? this.motionSensors.take(channel) : null;
|
|
175164
175289
|
const deviceId = this.channelToDeviceId.get(channel);
|
|
175165
175290
|
if (deviceId === void 0) {
|
|
175166
175291
|
const last = this.unroutedChannelLoggedAt.get(channel) ?? 0;
|
|
@@ -175190,7 +175315,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175190
175315
|
deviceId: child.id,
|
|
175191
175316
|
name: child.name
|
|
175192
175317
|
} });
|
|
175193
|
-
child.handleSimpleEvent(ev);
|
|
175318
|
+
child.handleSimpleEvent(ev, motionSensor);
|
|
175194
175319
|
}).catch((err) => {
|
|
175195
175320
|
this.ctx.logger.debug("Hub simpleEvent dispatch failed", { meta: {
|
|
175196
175321
|
channel,
|
|
@@ -175234,6 +175359,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175234
175359
|
try {
|
|
175235
175360
|
await api.offSimpleEvent(this.handleSimpleEventBound);
|
|
175236
175361
|
} catch {}
|
|
175362
|
+
this.observeMotionSensors(api);
|
|
175237
175363
|
await api.onSimpleEvent(this.handleSimpleEventBound);
|
|
175238
175364
|
api.client.on("close", () => this.scheduleReconnect("socket-close"));
|
|
175239
175365
|
api.client.on("error", (err) => {
|
|
@@ -175292,6 +175418,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
175292
175418
|
this.api = api;
|
|
175293
175419
|
this.reconnectAttempts = 0;
|
|
175294
175420
|
this.markOnline(true);
|
|
175421
|
+
this.observeMotionSensors(api);
|
|
175295
175422
|
api.client.on("close", () => this.scheduleReconnect("socket-close"));
|
|
175296
175423
|
api.client.on("error", (err) => {
|
|
175297
175424
|
this.ctx.logger.warn("Reolink Hub adopted socket error", { meta: { error: err instanceof Error ? err.message : String(err) } });
|