@camstack/addon-provider-reolink 1.2.36 → 1.2.38
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 +86 -0
- package/dist/addon.mjs +86 -0
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -19675,6 +19675,39 @@ var snapshotCapability = {
|
|
|
19675
19675
|
etag: string().nullable()
|
|
19676
19676
|
}))),
|
|
19677
19677
|
/**
|
|
19678
|
+
* The full decision chain for ONE device, for the viewer's debug readout —
|
|
19679
|
+
* the answer to "why does this tile show what it shows" in a single poll:
|
|
19680
|
+
* the battery slice the state was derived from, the resolved state + its
|
|
19681
|
+
* reason, the cached frame's identity/age, whether a wake window is open,
|
|
19682
|
+
* and whether a fresh capture is in flight. Cache-only and capture-free:
|
|
19683
|
+
* a debug read must never wake a battery camera.
|
|
19684
|
+
*/
|
|
19685
|
+
getDebugState: systemMethod(object({ deviceId: number() }), object({
|
|
19686
|
+
/** The battery slice as read, or null when the device has none. */
|
|
19687
|
+
battery: object({
|
|
19688
|
+
sleeping: boolean(),
|
|
19689
|
+
lastUpdated: number(),
|
|
19690
|
+
lastContactAt: number().optional()
|
|
19691
|
+
}).nullable(),
|
|
19692
|
+
/** The resolved snapshot state (what the overlay decision used). */
|
|
19693
|
+
state: object({
|
|
19694
|
+
isBattery: boolean(),
|
|
19695
|
+
reason: _enum([
|
|
19696
|
+
"disabled",
|
|
19697
|
+
"sleeping",
|
|
19698
|
+
"unreachable",
|
|
19699
|
+
"waking"
|
|
19700
|
+
]).nullable()
|
|
19701
|
+
}),
|
|
19702
|
+
/** The cached frame behind the next paint. */
|
|
19703
|
+
frame: object({
|
|
19704
|
+
capturedAt: number().nullable(),
|
|
19705
|
+
ageMs: number().nullable()
|
|
19706
|
+
}),
|
|
19707
|
+
/** A wake window is currently open (the Waking overlay's source). */
|
|
19708
|
+
waking: boolean()
|
|
19709
|
+
})),
|
|
19710
|
+
/**
|
|
19678
19711
|
* Signed, expiring links to a CLIENT-SIZED frame — and the demand signal
|
|
19679
19712
|
* that makes those frames current.
|
|
19680
19713
|
*
|
|
@@ -34676,6 +34709,12 @@ Object.freeze({
|
|
|
34676
34709
|
addonId: null,
|
|
34677
34710
|
access: "view"
|
|
34678
34711
|
},
|
|
34712
|
+
"snapshot.getDebugState": {
|
|
34713
|
+
capName: "snapshot",
|
|
34714
|
+
capScope: "device",
|
|
34715
|
+
addonId: null,
|
|
34716
|
+
access: "view"
|
|
34717
|
+
},
|
|
34679
34718
|
"snapshot.getSnapshot": {
|
|
34680
34719
|
capName: "snapshot",
|
|
34681
34720
|
capScope: "device",
|
|
@@ -37273,6 +37312,11 @@ Object.freeze({
|
|
|
37273
37312
|
form: "single",
|
|
37274
37313
|
optional: false
|
|
37275
37314
|
}],
|
|
37315
|
+
"snapshot.getDebugState": [{
|
|
37316
|
+
name: "deviceId",
|
|
37317
|
+
form: "single",
|
|
37318
|
+
optional: false
|
|
37319
|
+
}],
|
|
37276
37320
|
"snapshot.getSnapshot": [{
|
|
37277
37321
|
name: "deviceId",
|
|
37278
37322
|
form: "single",
|
|
@@ -231012,6 +231056,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
231012
231056
|
durationMs: Date.now() - startedAt
|
|
231013
231057
|
};
|
|
231014
231058
|
}
|
|
231059
|
+
if (confirmed && !observedAwake && parent !== null) this.watchHubChildAwake(parent).catch(() => {});
|
|
231015
231060
|
return {
|
|
231016
231061
|
awoke: true,
|
|
231017
231062
|
durationMs: Date.now() - startedAt
|
|
@@ -231190,6 +231235,29 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
231190
231235
|
this.lastProactiveWakeAt = Date.now();
|
|
231191
231236
|
}
|
|
231192
231237
|
/**
|
|
231238
|
+
* Post-confirm watch for a hub child whose wake lands on the consumer's
|
|
231239
|
+
* relay dial: re-read the parent's channel summary a few times over the
|
|
231240
|
+
* dial's wake window and commit `sleeping: false` the moment the firmware
|
|
231241
|
+
* reports it. Ends EARLY once committed; bounded hard so a dead link can't
|
|
231242
|
+
* leak a timer. Costs the camera nothing — the summary is the hub's own
|
|
231243
|
+
* mains-powered read.
|
|
231244
|
+
*/
|
|
231245
|
+
async watchHubChildAwake(parent) {
|
|
231246
|
+
const deadline = Date.now() + 45e3;
|
|
231247
|
+
while (Date.now() < deadline) {
|
|
231248
|
+
await sleep$1(5e3);
|
|
231249
|
+
try {
|
|
231250
|
+
if ((await (await parent.getApi()).getNvrChannelsSummary({ channels: [this.getChannel()] })).devices.find((d) => d.channel === this.getChannel())?.sleeping === false) {
|
|
231251
|
+
if (this.commitSleepState(false, "hub-summary")) {
|
|
231252
|
+
this.ctx.logger.info("battery wakeForStream: hub child observed awake after the dial", { tags: { deviceId: this.id } });
|
|
231253
|
+
this.onWakeTransition("hub-summary").catch(() => {});
|
|
231254
|
+
}
|
|
231255
|
+
return;
|
|
231256
|
+
}
|
|
231257
|
+
} catch {}
|
|
231258
|
+
}
|
|
231259
|
+
}
|
|
231260
|
+
/**
|
|
231193
231261
|
* Close the transient "waking" window on a wake that did NOT produce a
|
|
231194
231262
|
* committed transition (ACK with no awake evidence, or a timeout).
|
|
231195
231263
|
* `BatteryOnWakeStarted` opens it; normally the `sleeping: false` commit
|
|
@@ -236651,6 +236719,14 @@ function computeHealthCheckBackoffMs(consecutive) {
|
|
|
236651
236719
|
//#endregion
|
|
236652
236720
|
//#region src/reolink-hub.ts
|
|
236653
236721
|
var HUB_DISCOVERY_REFRESH_TIMEOUT_MS = 8e3;
|
|
236722
|
+
/**
|
|
236723
|
+
* How often the hub re-reads its own channels summary to reconcile the
|
|
236724
|
+
* adopted children's sleep slices. 60 s: a naturally-waking camera (PIR,
|
|
236725
|
+
* schedule) is caught within a minute, and the play path has its own
|
|
236726
|
+
* faster confirm + post-dial watch, so this only ever carries background
|
|
236727
|
+
* drift. Never wakes a child — the summary is answered by the hub itself.
|
|
236728
|
+
*/
|
|
236729
|
+
var HUB_CHILD_SLEEP_RECONCILE_MS = 6e4;
|
|
236654
236730
|
var HUB_RECONNECT_BASE_MS = 2e3;
|
|
236655
236731
|
var HUB_RECONNECT_MAX_MS = 3e4;
|
|
236656
236732
|
/**
|
|
@@ -236684,6 +236760,8 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
236684
236760
|
connectInFlight = null;
|
|
236685
236761
|
reconnectAttempts = 0;
|
|
236686
236762
|
reconnectTimer = null;
|
|
236763
|
+
/** Periodic child-sleep reconcile timer (armed in `onActivate`). */
|
|
236764
|
+
sleepReconcileTimer = null;
|
|
236687
236765
|
shutdownRequested = false;
|
|
236688
236766
|
/** Diagnostic Sessions snapshot — same shape as the camera's. */
|
|
236689
236767
|
sessionsSnapshot = null;
|
|
@@ -236768,6 +236846,10 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
236768
236846
|
this.ctx.logger.info("Reolink Hub: child unregistered externally — refreshing discovery", cid !== null ? { tags: { deviceId: cid } } : {});
|
|
236769
236847
|
this.refreshDiscoveryFromCamera().catch(() => {});
|
|
236770
236848
|
}));
|
|
236849
|
+
this.sleepReconcileTimer = setInterval(() => {
|
|
236850
|
+
this.refreshDiscoveryFromCamera().catch(() => {});
|
|
236851
|
+
}, HUB_CHILD_SLEEP_RECONCILE_MS);
|
|
236852
|
+
if (typeof this.sleepReconcileTimer.unref === "function") this.sleepReconcileTimer.unref();
|
|
236771
236853
|
}
|
|
236772
236854
|
eventUnsubscribers = [];
|
|
236773
236855
|
/** Per-channel throttle for the unrouted-event warning (see
|
|
@@ -236779,6 +236861,10 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
236779
236861
|
clearTimeout(this.reconnectTimer);
|
|
236780
236862
|
this.reconnectTimer = null;
|
|
236781
236863
|
}
|
|
236864
|
+
if (this.sleepReconcileTimer) {
|
|
236865
|
+
clearInterval(this.sleepReconcileTimer);
|
|
236866
|
+
this.sleepReconcileTimer = null;
|
|
236867
|
+
}
|
|
236782
236868
|
for (const unsub of this.eventUnsubscribers) try {
|
|
236783
236869
|
unsub();
|
|
236784
236870
|
} catch {}
|
package/dist/addon.mjs
CHANGED
|
@@ -19670,6 +19670,39 @@ var snapshotCapability = {
|
|
|
19670
19670
|
etag: string().nullable()
|
|
19671
19671
|
}))),
|
|
19672
19672
|
/**
|
|
19673
|
+
* The full decision chain for ONE device, for the viewer's debug readout —
|
|
19674
|
+
* the answer to "why does this tile show what it shows" in a single poll:
|
|
19675
|
+
* the battery slice the state was derived from, the resolved state + its
|
|
19676
|
+
* reason, the cached frame's identity/age, whether a wake window is open,
|
|
19677
|
+
* and whether a fresh capture is in flight. Cache-only and capture-free:
|
|
19678
|
+
* a debug read must never wake a battery camera.
|
|
19679
|
+
*/
|
|
19680
|
+
getDebugState: systemMethod(object({ deviceId: number() }), object({
|
|
19681
|
+
/** The battery slice as read, or null when the device has none. */
|
|
19682
|
+
battery: object({
|
|
19683
|
+
sleeping: boolean(),
|
|
19684
|
+
lastUpdated: number(),
|
|
19685
|
+
lastContactAt: number().optional()
|
|
19686
|
+
}).nullable(),
|
|
19687
|
+
/** The resolved snapshot state (what the overlay decision used). */
|
|
19688
|
+
state: object({
|
|
19689
|
+
isBattery: boolean(),
|
|
19690
|
+
reason: _enum([
|
|
19691
|
+
"disabled",
|
|
19692
|
+
"sleeping",
|
|
19693
|
+
"unreachable",
|
|
19694
|
+
"waking"
|
|
19695
|
+
]).nullable()
|
|
19696
|
+
}),
|
|
19697
|
+
/** The cached frame behind the next paint. */
|
|
19698
|
+
frame: object({
|
|
19699
|
+
capturedAt: number().nullable(),
|
|
19700
|
+
ageMs: number().nullable()
|
|
19701
|
+
}),
|
|
19702
|
+
/** A wake window is currently open (the Waking overlay's source). */
|
|
19703
|
+
waking: boolean()
|
|
19704
|
+
})),
|
|
19705
|
+
/**
|
|
19673
19706
|
* Signed, expiring links to a CLIENT-SIZED frame — and the demand signal
|
|
19674
19707
|
* that makes those frames current.
|
|
19675
19708
|
*
|
|
@@ -34671,6 +34704,12 @@ Object.freeze({
|
|
|
34671
34704
|
addonId: null,
|
|
34672
34705
|
access: "view"
|
|
34673
34706
|
},
|
|
34707
|
+
"snapshot.getDebugState": {
|
|
34708
|
+
capName: "snapshot",
|
|
34709
|
+
capScope: "device",
|
|
34710
|
+
addonId: null,
|
|
34711
|
+
access: "view"
|
|
34712
|
+
},
|
|
34674
34713
|
"snapshot.getSnapshot": {
|
|
34675
34714
|
capName: "snapshot",
|
|
34676
34715
|
capScope: "device",
|
|
@@ -37268,6 +37307,11 @@ Object.freeze({
|
|
|
37268
37307
|
form: "single",
|
|
37269
37308
|
optional: false
|
|
37270
37309
|
}],
|
|
37310
|
+
"snapshot.getDebugState": [{
|
|
37311
|
+
name: "deviceId",
|
|
37312
|
+
form: "single",
|
|
37313
|
+
optional: false
|
|
37314
|
+
}],
|
|
37271
37315
|
"snapshot.getSnapshot": [{
|
|
37272
37316
|
name: "deviceId",
|
|
37273
37317
|
form: "single",
|
|
@@ -230992,6 +231036,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
230992
231036
|
durationMs: Date.now() - startedAt
|
|
230993
231037
|
};
|
|
230994
231038
|
}
|
|
231039
|
+
if (confirmed && !observedAwake && parent !== null) this.watchHubChildAwake(parent).catch(() => {});
|
|
230995
231040
|
return {
|
|
230996
231041
|
awoke: true,
|
|
230997
231042
|
durationMs: Date.now() - startedAt
|
|
@@ -231170,6 +231215,29 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
231170
231215
|
this.lastProactiveWakeAt = Date.now();
|
|
231171
231216
|
}
|
|
231172
231217
|
/**
|
|
231218
|
+
* Post-confirm watch for a hub child whose wake lands on the consumer's
|
|
231219
|
+
* relay dial: re-read the parent's channel summary a few times over the
|
|
231220
|
+
* dial's wake window and commit `sleeping: false` the moment the firmware
|
|
231221
|
+
* reports it. Ends EARLY once committed; bounded hard so a dead link can't
|
|
231222
|
+
* leak a timer. Costs the camera nothing — the summary is the hub's own
|
|
231223
|
+
* mains-powered read.
|
|
231224
|
+
*/
|
|
231225
|
+
async watchHubChildAwake(parent) {
|
|
231226
|
+
const deadline = Date.now() + 45e3;
|
|
231227
|
+
while (Date.now() < deadline) {
|
|
231228
|
+
await sleep$1(5e3);
|
|
231229
|
+
try {
|
|
231230
|
+
if ((await (await parent.getApi()).getNvrChannelsSummary({ channels: [this.getChannel()] })).devices.find((d) => d.channel === this.getChannel())?.sleeping === false) {
|
|
231231
|
+
if (this.commitSleepState(false, "hub-summary")) {
|
|
231232
|
+
this.ctx.logger.info("battery wakeForStream: hub child observed awake after the dial", { tags: { deviceId: this.id } });
|
|
231233
|
+
this.onWakeTransition("hub-summary").catch(() => {});
|
|
231234
|
+
}
|
|
231235
|
+
return;
|
|
231236
|
+
}
|
|
231237
|
+
} catch {}
|
|
231238
|
+
}
|
|
231239
|
+
}
|
|
231240
|
+
/**
|
|
231173
231241
|
* Close the transient "waking" window on a wake that did NOT produce a
|
|
231174
231242
|
* committed transition (ACK with no awake evidence, or a timeout).
|
|
231175
231243
|
* `BatteryOnWakeStarted` opens it; normally the `sleeping: false` commit
|
|
@@ -236631,6 +236699,14 @@ function computeHealthCheckBackoffMs(consecutive) {
|
|
|
236631
236699
|
//#endregion
|
|
236632
236700
|
//#region src/reolink-hub.ts
|
|
236633
236701
|
var HUB_DISCOVERY_REFRESH_TIMEOUT_MS = 8e3;
|
|
236702
|
+
/**
|
|
236703
|
+
* How often the hub re-reads its own channels summary to reconcile the
|
|
236704
|
+
* adopted children's sleep slices. 60 s: a naturally-waking camera (PIR,
|
|
236705
|
+
* schedule) is caught within a minute, and the play path has its own
|
|
236706
|
+
* faster confirm + post-dial watch, so this only ever carries background
|
|
236707
|
+
* drift. Never wakes a child — the summary is answered by the hub itself.
|
|
236708
|
+
*/
|
|
236709
|
+
var HUB_CHILD_SLEEP_RECONCILE_MS = 6e4;
|
|
236634
236710
|
var HUB_RECONNECT_BASE_MS = 2e3;
|
|
236635
236711
|
var HUB_RECONNECT_MAX_MS = 3e4;
|
|
236636
236712
|
/**
|
|
@@ -236664,6 +236740,8 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
236664
236740
|
connectInFlight = null;
|
|
236665
236741
|
reconnectAttempts = 0;
|
|
236666
236742
|
reconnectTimer = null;
|
|
236743
|
+
/** Periodic child-sleep reconcile timer (armed in `onActivate`). */
|
|
236744
|
+
sleepReconcileTimer = null;
|
|
236667
236745
|
shutdownRequested = false;
|
|
236668
236746
|
/** Diagnostic Sessions snapshot — same shape as the camera's. */
|
|
236669
236747
|
sessionsSnapshot = null;
|
|
@@ -236748,6 +236826,10 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
236748
236826
|
this.ctx.logger.info("Reolink Hub: child unregistered externally — refreshing discovery", cid !== null ? { tags: { deviceId: cid } } : {});
|
|
236749
236827
|
this.refreshDiscoveryFromCamera().catch(() => {});
|
|
236750
236828
|
}));
|
|
236829
|
+
this.sleepReconcileTimer = setInterval(() => {
|
|
236830
|
+
this.refreshDiscoveryFromCamera().catch(() => {});
|
|
236831
|
+
}, HUB_CHILD_SLEEP_RECONCILE_MS);
|
|
236832
|
+
if (typeof this.sleepReconcileTimer.unref === "function") this.sleepReconcileTimer.unref();
|
|
236751
236833
|
}
|
|
236752
236834
|
eventUnsubscribers = [];
|
|
236753
236835
|
/** Per-channel throttle for the unrouted-event warning (see
|
|
@@ -236759,6 +236841,10 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
236759
236841
|
clearTimeout(this.reconnectTimer);
|
|
236760
236842
|
this.reconnectTimer = null;
|
|
236761
236843
|
}
|
|
236844
|
+
if (this.sleepReconcileTimer) {
|
|
236845
|
+
clearInterval(this.sleepReconcileTimer);
|
|
236846
|
+
this.sleepReconcileTimer = null;
|
|
236847
|
+
}
|
|
236762
236848
|
for (const unsub of this.eventUnsubscribers) try {
|
|
236763
236849
|
unsub();
|
|
236764
236850
|
} catch {}
|