@apocaliss92/nodelink-js 0.4.31 → 0.4.33
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/README.md +34 -2
- package/dist/{chunk-XVFCEFM6.js → chunk-OZL6C2YJ.js} +50 -1
- package/dist/{chunk-XVFCEFM6.js.map → chunk-OZL6C2YJ.js.map} +1 -1
- package/dist/cli/rtsp-server.cjs +49 -0
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +49 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,14 +49,46 @@ Battery cameras (Argus, Go, …) can't reliably keep a TCP/ONVIF push subscripti
|
|
|
49
49
|
- **Auto** — manager: **Email Push** tab in the camera modal → *Auto-configure*. Scrypted: open the camera's Settings → **E-mail Push** group → *Auto-configure from Email Push Server*. Both call `setupEmailPushToManager` under the hood.
|
|
50
50
|
- **API** — `await api.setupEmailPushToManager({ managerHost, managerPort, recipientLocalPart, domain, authUsername, authPassword, triggerTypes, attachmentType }, channel)`. The lib auto-wraps a bare username as `<user@domain>` so MAIL FROM stays RFC 5321 compliant.
|
|
51
51
|
- **Manual** — fill the Reolink app form: server = manager LAN IP, port = `2525`, sender = `authUsername`, password = `authPassword`, TLS off, receiver = `cam-<id>@<domain>`.
|
|
52
|
-
4. On motion, the camera sends an e-mail. The intake parses it, classifies the trigger (`MD` / `people` / `vehicle`), and emits an `EmailPushEvent` on the shared bus.
|
|
52
|
+
4. On motion, the camera sends an e-mail. The intake parses it, classifies the trigger (`MD` / `people` / `vehicle`), and emits an `EmailPushEvent` on the shared bus. From there it lands on `api.onSimpleEvent` automatically — see "Unified event stream" below.
|
|
53
53
|
|
|
54
54
|
See [documentation/baichuan-api/email.md](./documentation/baichuan-api/email.md) for the full Baichuan API surface and [documentation/baichuan-api/time.md](./documentation/baichuan-api/time.md) for the related NTP / DST / system clock setters.
|
|
55
55
|
|
|
56
|
+
### Unified event stream (since 0.4.32)
|
|
57
|
+
|
|
58
|
+
Construct the api with `emailPushCameraId` (and optionally `emailPushChannel`) and the library wires the SMTP bus into the api's internal `simpleEventListeners` for you. Every consumer registered via `api.onSimpleEvent(...)` then receives native Baichuan push **and** SMTP-delivered motion through the same stream — no separate `onEmailPushEvent` subscription needed. The bridge survives TCP transient disconnects (it's a pure JS fan-out, not a network operation) and is released automatically by `close()`.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { ReolinkBaichuanApi } from "@apocaliss92/nodelink-js";
|
|
62
|
+
|
|
63
|
+
const api = new ReolinkBaichuanApi({
|
|
64
|
+
host: "192.168.1.100",
|
|
65
|
+
username: "admin",
|
|
66
|
+
password: "secret",
|
|
67
|
+
transport: "udp",
|
|
68
|
+
uid: "REOLINK-UID-HERE",
|
|
69
|
+
|
|
70
|
+
// Auto-bridge SMTP motion into api.onSimpleEvent. Match the same
|
|
71
|
+
// cameraId your `createEmailPushServer({ cameraResolver })` returns
|
|
72
|
+
// (typically the camera's nativeId / stable identifier).
|
|
73
|
+
emailPushCameraId: "my-battery-cam",
|
|
74
|
+
emailPushChannel: 0, // optional, default 0
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await api.login();
|
|
78
|
+
await api.onSimpleEvent((event) => {
|
|
79
|
+
// Fires for both native Baichuan push AND SMTP motion.
|
|
80
|
+
console.log(event.type, "on ch", event.channel, "@", event.timestamp);
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
For single-owner consumers that already manage their own bridge (e.g. a custom resolver scheme), the lower-level `api.subscribeEmailPushEvents({ cameraId | match, channel })` is still exposed.
|
|
85
|
+
|
|
56
86
|
**Library entry points**:
|
|
57
87
|
|
|
58
88
|
- `createEmailPushServer({ config, cameraResolver, logger, loadTls? })` — factory returning `{ start, stop, restart, updateConfig, getStatus }`
|
|
59
|
-
- `
|
|
89
|
+
- `new ReolinkBaichuanApi({ ..., emailPushCameraId, emailPushChannel? })` — auto-bridge into `onSimpleEvent` (recommended)
|
|
90
|
+
- `api.subscribeEmailPushEvents({ cameraId | match, channel? })` — manual per-api bridge with custom matcher
|
|
91
|
+
- `onEmailPushEvent(handler)` — raw global bus subscription (use when you need the full `EmailPushEvent` payload, not just the synthesised `ReolinkSimpleEvent`)
|
|
60
92
|
- `getRecentEmailPushEvents(limit)` — bounded in-memory ring buffer of accepted deliveries
|
|
61
93
|
- `setupEmailPushToManager(params, channel)` — orchestrator: `setEmail` + `setEmailTask` + optional `testEmail`
|
|
62
94
|
- `getEmail`, `setEmail`, `testEmail`, `getEmailTask`, `setEmailTask` — low-level Baichuan accessors
|
|
@@ -11439,6 +11439,13 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
11439
11439
|
* Once closed, the API instance should not be reused.
|
|
11440
11440
|
*/
|
|
11441
11441
|
_closed = false;
|
|
11442
|
+
/**
|
|
11443
|
+
* Off-handle for the auto-bridge between the global email-push bus
|
|
11444
|
+
* and this api's `simpleEventListeners`. Set in the constructor
|
|
11445
|
+
* when `emailPushCameraId` is provided; released in `close()`.
|
|
11446
|
+
* `undefined` means no bridge was requested for this api.
|
|
11447
|
+
*/
|
|
11448
|
+
emailPushAutoBridgeOff;
|
|
11442
11449
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
11443
11450
|
// SOCKET POOL - Tag-based socket management
|
|
11444
11451
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -11811,6 +11818,21 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
11811
11818
|
// check every 10s
|
|
11812
11819
|
simpleEventWatchdogSilenceThresholdMs = 5 * 6e4;
|
|
11813
11820
|
// 5 min without events
|
|
11821
|
+
/**
|
|
11822
|
+
* Whether the silence-based resubscribe path of the watchdog is
|
|
11823
|
+
* enabled. On UDP (battery cameras) silence is the *normal* state
|
|
11824
|
+
* while the device sleeps — firing `ensureSimpleEventSubscribed`
|
|
11825
|
+
* every 5 minutes wakes the camera on every tick, drains the
|
|
11826
|
+
* battery, and is observably wrong because the cam emits a
|
|
11827
|
+
* sleep/awake push when it actually wakes for motion.
|
|
11828
|
+
*
|
|
11829
|
+
* Defaults: `false` on UDP, `true` on TCP / `auto`. The subscription-
|
|
11830
|
+
* failed recovery path (Case 2) stays active regardless — it only
|
|
11831
|
+
* runs when the connection is alive, doesn't wake anyone, and is
|
|
11832
|
+
* useful on every transport when the initial subscribe call lost
|
|
11833
|
+
* the response packet.
|
|
11834
|
+
*/
|
|
11835
|
+
eventWatchdogSilenceResubscribeEnabled = true;
|
|
11814
11836
|
statePollingInterval;
|
|
11815
11837
|
udpSleepInferenceInterval;
|
|
11816
11838
|
udpLastInferredSleepStateByChannel = /* @__PURE__ */ new Map();
|
|
@@ -12768,6 +12790,12 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
12768
12790
|
} else {
|
|
12769
12791
|
this.eventResubscribeEnabled = opts.transport !== "udp";
|
|
12770
12792
|
}
|
|
12793
|
+
const explicitWatchdogResubscribe = opts.enableEventWatchdogSilenceResubscribe;
|
|
12794
|
+
if (typeof explicitWatchdogResubscribe === "boolean") {
|
|
12795
|
+
this.eventWatchdogSilenceResubscribeEnabled = explicitWatchdogResubscribe;
|
|
12796
|
+
} else {
|
|
12797
|
+
this.eventWatchdogSilenceResubscribeEnabled = opts.transport !== "udp";
|
|
12798
|
+
}
|
|
12771
12799
|
const maxSessions = opts.maxDedicatedSessionsBeforeReboot;
|
|
12772
12800
|
if (typeof maxSessions === "number" && Number.isFinite(maxSessions) && maxSessions > 0) {
|
|
12773
12801
|
this.maxDedicatedSessionsBeforeReboot = Math.floor(maxSessions);
|
|
@@ -12781,6 +12809,12 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
12781
12809
|
this.rebootAfterConsecutiveEconnreset = Math.floor(econnresetThreshold);
|
|
12782
12810
|
}
|
|
12783
12811
|
this.setupGeneralClientListeners();
|
|
12812
|
+
if (opts.emailPushCameraId) {
|
|
12813
|
+
this.emailPushAutoBridgeOff = this.subscribeEmailPushEvents({
|
|
12814
|
+
cameraId: opts.emailPushCameraId,
|
|
12815
|
+
channel: opts.emailPushChannel ?? 0
|
|
12816
|
+
});
|
|
12817
|
+
}
|
|
12784
12818
|
}
|
|
12785
12819
|
/**
|
|
12786
12820
|
* CGI forward: fetch RTSP URL for a channel via `GetRtspUrl`.
|
|
@@ -13584,6 +13618,14 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
13584
13618
|
if (this.simpleEventSubscribed && this.simpleEventLastReceivedAt > 0) {
|
|
13585
13619
|
const silence = now - this.simpleEventLastReceivedAt;
|
|
13586
13620
|
if (silence < this.simpleEventWatchdogSilenceThresholdMs) return;
|
|
13621
|
+
if (!this.eventWatchdogSilenceResubscribeEnabled) {
|
|
13622
|
+
this.logger.debug?.(
|
|
13623
|
+
`[ReolinkBaichuanApi] event watchdog: silence-based resubscribe disabled (UDP / battery), skipping`,
|
|
13624
|
+
{ host: this.host, silenceMs: silence }
|
|
13625
|
+
);
|
|
13626
|
+
this.simpleEventLastReceivedAt = now;
|
|
13627
|
+
return;
|
|
13628
|
+
}
|
|
13587
13629
|
(this.logger.warn ?? this.logger.log).call(
|
|
13588
13630
|
this.logger,
|
|
13589
13631
|
`[ReolinkBaichuanApi] event watchdog: no events for ${Math.round(silence / 6e4)} min, forcing resubscribe`,
|
|
@@ -13904,6 +13946,13 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
13904
13946
|
async close(options) {
|
|
13905
13947
|
if (this._closed) return;
|
|
13906
13948
|
this._closed = true;
|
|
13949
|
+
if (this.emailPushAutoBridgeOff) {
|
|
13950
|
+
try {
|
|
13951
|
+
this.emailPushAutoBridgeOff();
|
|
13952
|
+
} catch {
|
|
13953
|
+
}
|
|
13954
|
+
this.emailPushAutoBridgeOff = void 0;
|
|
13955
|
+
}
|
|
13907
13956
|
if (this.sessionGuardIntervalTimer) {
|
|
13908
13957
|
clearInterval(this.sessionGuardIntervalTimer);
|
|
13909
13958
|
this.sessionGuardIntervalTimer = void 0;
|
|
@@ -24733,4 +24782,4 @@ export {
|
|
|
24733
24782
|
isTcpFailureThatShouldFallbackToUdp,
|
|
24734
24783
|
autoDetectDeviceType
|
|
24735
24784
|
};
|
|
24736
|
-
//# sourceMappingURL=chunk-
|
|
24785
|
+
//# sourceMappingURL=chunk-OZL6C2YJ.js.map
|