@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.
Files changed (3) hide show
  1. package/dist/addon.js +189 -62
  2. package/dist/addon.mjs +189 -62
  3. 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` answers from the hub's slice.
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 readHubMotionDetected(deviceState, deviceId) {
54830
- let slice;
54861
+ async function readSlice(deviceState, deviceId) {
54831
54862
  try {
54832
- slice = await deviceState.getCapSlice.query({
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
- const detected = slice?.["detected"];
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 answers from the HUB's slice; an unreadable hub, or a slice
166924
- * nobody has published since the hub started, fails the call. It never
166925
- * answers `false` for "unknown" (D49, D393).
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.registerNativeCap(motionCapability, { isDetected: async ({ deviceId }) => {
166929
- if (deviceId !== this.id) throw new Error(`ReolinkCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
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` answers from the hub's slice.
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 readHubMotionDetected(deviceState, deviceId) {
54822
- let slice;
54853
+ async function readSlice(deviceState, deviceId) {
54823
54854
  try {
54824
- slice = await deviceState.getCapSlice.query({
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
- const detected = slice?.["detected"];
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 answers from the HUB's slice; an unreadable hub, or a slice
166916
- * nobody has published since the hub started, fails the call. It never
166917
- * answers `false` for "unknown" (D49, D393).
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.registerNativeCap(motionCapability, { isDetected: async ({ deviceId }) => {
166921
- if (deviceId !== this.id) throw new Error(`ReolinkCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
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) } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.2.175",
3
+ "version": "1.2.177",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",