@camstack/addon-provider-reolink 1.2.79 → 1.2.81

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 CHANGED
@@ -19806,7 +19806,20 @@ var RecentTracksQueryInput = object({
19806
19806
  projection: TrackProjectionSchema.optional(),
19807
19807
  /** Include stationary-promoted rows (parked objects). Default false: the
19808
19808
  * feed lists passages; parking records live on the stationary registry. */
19809
- includeStationary: boolean().optional()
19809
+ includeStationary: boolean().optional(),
19810
+ /**
19811
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
19812
+ *
19813
+ * The same filter `listTracks` takes, because the timeline's class chips must
19814
+ * mean the same thing whether the scope is one camera or twelve. Until this
19815
+ * existed the scoped feed downloaded a page and narrowed it on the phone
19816
+ * while the single-camera path narrowed the read — one filter, two costs.
19817
+ *
19818
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
19819
+ * whose class list is unreadable are kept, and the client's rule stays the
19820
+ * exact one.
19821
+ */
19822
+ classes: array(string()).optional()
19810
19823
  });
19811
19824
  var RecentTracksPageSchema = object({
19812
19825
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20193,7 +20206,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20193
20206
  /** Include stationary-promoted rows (parked objects handed to the
20194
20207
  * stationary registry). Default false: the timeline lists passages,
20195
20208
  * not parking records (operator decision, 2026-08-15). */
20196
- includeStationary: boolean().optional()
20209
+ includeStationary: boolean().optional(),
20210
+ /**
20211
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
20212
+ *
20213
+ * Added 2026-09-03: the timeline's class chips filtered in the CLIENT,
20214
+ * on a day of rows already downloaded. The zone filter had been moved
20215
+ * here for exactly that reason — and it is also what lets the rows come
20216
+ * back `slim` — but the class filter was left behind, so a narrow
20217
+ * selection was still a wide read followed by a wide discard.
20218
+ *
20219
+ * Empty is "show all", not "nothing matches": a chip row with nothing
20220
+ * selected is an operator who has not narrowed anything, and an empty
20221
+ * timeline would read as a camera that saw nothing.
20222
+ */
20223
+ classes: array(string()).optional()
20197
20224
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
20198
20225
  kind: "mutation",
20199
20226
  auth: "admin"
@@ -24066,8 +24093,22 @@ var automationControlCapability = {
24066
24093
  * threshold.
24067
24094
  */
24068
24095
  var BatteryStatusSchema = object({
24069
- /** 0..100 inclusive. Firmware-reported. */
24070
- percentage: number().min(0).max(100),
24096
+ /**
24097
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
24098
+ * provider has registered the capability but no reading has landed.
24099
+ *
24100
+ * It is nullable because it was not, and the only value a provider could
24101
+ * seed with was `0`. A battery camera behind an NVR therefore announced
24102
+ * itself at 0% on every start and corrected itself a moment later, which is
24103
+ * indistinguishable from a real flat battery: it fires the low-battery alert
24104
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
24105
+ * reading the difference is an alarm.
24106
+ *
24107
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
24108
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
24109
+ * declines to band a non-finite reading, which is the correct shape.
24110
+ */
24111
+ percentage: number().min(0).max(100).nullable(),
24071
24112
  /**
24072
24113
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
24073
24114
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -235452,7 +235493,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
235452
235493
  * tracker is more reliable.
235453
235494
  */
235454
235495
  mapBatteryInfo(info) {
235455
- const percentage = typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? 0;
235496
+ const percentage = typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? null;
235456
235497
  const adapter = (info.adapterStatus ?? "").toLowerCase();
235457
235498
  const charge = (info.chargeStatus ?? "").toLowerCase();
235458
235499
  const isCharging = charge === "charging" || charge === "chargecomplete";
@@ -235583,7 +235624,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
235583
235624
  }
235584
235625
  });
235585
235626
  if (this.getCapSlice(batteryCapability) === null) this.setCapSlice(batteryCapability, {
235586
- percentage: 0,
235627
+ percentage: null,
235587
235628
  charging: "none",
235588
235629
  sleeping: false,
235589
235630
  lastUpdated: Date.now()
package/dist/addon.mjs CHANGED
@@ -19801,7 +19801,20 @@ var RecentTracksQueryInput = object({
19801
19801
  projection: TrackProjectionSchema.optional(),
19802
19802
  /** Include stationary-promoted rows (parked objects). Default false: the
19803
19803
  * feed lists passages; parking records live on the stationary registry. */
19804
- includeStationary: boolean().optional()
19804
+ includeStationary: boolean().optional(),
19805
+ /**
19806
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
19807
+ *
19808
+ * The same filter `listTracks` takes, because the timeline's class chips must
19809
+ * mean the same thing whether the scope is one camera or twelve. Until this
19810
+ * existed the scoped feed downloaded a page and narrowed it on the phone
19811
+ * while the single-camera path narrowed the read — one filter, two costs.
19812
+ *
19813
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
19814
+ * whose class list is unreadable are kept, and the client's rule stays the
19815
+ * exact one.
19816
+ */
19817
+ classes: array(string()).optional()
19805
19818
  });
19806
19819
  var RecentTracksPageSchema = object({
19807
19820
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20188,7 +20201,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20188
20201
  /** Include stationary-promoted rows (parked objects handed to the
20189
20202
  * stationary registry). Default false: the timeline lists passages,
20190
20203
  * not parking records (operator decision, 2026-08-15). */
20191
- includeStationary: boolean().optional()
20204
+ includeStationary: boolean().optional(),
20205
+ /**
20206
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
20207
+ *
20208
+ * Added 2026-09-03: the timeline's class chips filtered in the CLIENT,
20209
+ * on a day of rows already downloaded. The zone filter had been moved
20210
+ * here for exactly that reason — and it is also what lets the rows come
20211
+ * back `slim` — but the class filter was left behind, so a narrow
20212
+ * selection was still a wide read followed by a wide discard.
20213
+ *
20214
+ * Empty is "show all", not "nothing matches": a chip row with nothing
20215
+ * selected is an operator who has not narrowed anything, and an empty
20216
+ * timeline would read as a camera that saw nothing.
20217
+ */
20218
+ classes: array(string()).optional()
20192
20219
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
20193
20220
  kind: "mutation",
20194
20221
  auth: "admin"
@@ -24061,8 +24088,22 @@ var automationControlCapability = {
24061
24088
  * threshold.
24062
24089
  */
24063
24090
  var BatteryStatusSchema = object({
24064
- /** 0..100 inclusive. Firmware-reported. */
24065
- percentage: number().min(0).max(100),
24091
+ /**
24092
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
24093
+ * provider has registered the capability but no reading has landed.
24094
+ *
24095
+ * It is nullable because it was not, and the only value a provider could
24096
+ * seed with was `0`. A battery camera behind an NVR therefore announced
24097
+ * itself at 0% on every start and corrected itself a moment later, which is
24098
+ * indistinguishable from a real flat battery: it fires the low-battery alert
24099
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
24100
+ * reading the difference is an alarm.
24101
+ *
24102
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
24103
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
24104
+ * declines to band a non-finite reading, which is the correct shape.
24105
+ */
24106
+ percentage: number().min(0).max(100).nullable(),
24066
24107
  /**
24067
24108
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
24068
24109
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -235432,7 +235473,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
235432
235473
  * tracker is more reliable.
235433
235474
  */
235434
235475
  mapBatteryInfo(info) {
235435
- const percentage = typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? 0;
235476
+ const percentage = typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? null;
235436
235477
  const adapter = (info.adapterStatus ?? "").toLowerCase();
235437
235478
  const charge = (info.chargeStatus ?? "").toLowerCase();
235438
235479
  const isCharging = charge === "charging" || charge === "chargecomplete";
@@ -235563,7 +235604,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
235563
235604
  }
235564
235605
  });
235565
235606
  if (this.getCapSlice(batteryCapability) === null) this.setCapSlice(batteryCapability, {
235566
- percentage: 0,
235607
+ percentage: null,
235567
235608
  charging: "none",
235568
235609
  sleeping: false,
235569
235610
  lastUpdated: Date.now()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.2.79",
3
+ "version": "1.2.81",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",