@apocaliss92/scrypted-reolink-native 0.5.22 → 0.5.24
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/.claude/scheduled_tasks.lock +1 -0
- package/dist/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +2 -2
- package/src/baichuan-base.ts +14 -0
- package/src/camera.ts +41 -0
- package/src/email-push-server-device.ts +10 -2
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apocaliss92/scrypted-reolink-native",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.24",
|
|
4
4
|
"description": "Use any reolink camera with Scrypted, even older/unsupported models without HTTP protocol support",
|
|
5
5
|
"author": "@apocaliss92",
|
|
6
6
|
"license": "Apache",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
]
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@apocaliss92/nodelink-js": "^0.4.
|
|
47
|
+
"@apocaliss92/nodelink-js": "^0.4.31",
|
|
48
48
|
"@scrypted/common": "file:../../scrypted/common",
|
|
49
49
|
"@scrypted/rtsp": "file:../../scrypted/plugins/rtsp",
|
|
50
50
|
"@scrypted/sdk": "^0.3.118"
|
package/src/baichuan-base.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
BaichuanClientOptions,
|
|
3
|
+
EmailPushEvent,
|
|
3
4
|
ReolinkBaichuanApi,
|
|
4
5
|
ReolinkSimpleEvent,
|
|
5
6
|
} from "@apocaliss92/nodelink-js" with { "resolution-mode": "import" };
|
|
@@ -32,6 +33,16 @@ export interface BaichuanConnectionCallbacks {
|
|
|
32
33
|
* leave undefined on NVR children where email-push isn't meaningful.
|
|
33
34
|
*/
|
|
34
35
|
emailPushCameraId?: () => string;
|
|
36
|
+
/**
|
|
37
|
+
* Optional. When the email-push event carries an image attachment
|
|
38
|
+
* (typical for `attachmentType=picture` on motion), the camera
|
|
39
|
+
* receives the full event so it can republish the snapshot —
|
|
40
|
+
* usually by updating its `lastPicture` cache so subsequent
|
|
41
|
+
* `takePicture()` calls return the fresh thumbnail without waking
|
|
42
|
+
* the camera. Invoked AFTER the simple-event dispatch so any motion
|
|
43
|
+
* listener has already fired.
|
|
44
|
+
*/
|
|
45
|
+
onEmailPushEvent?: (event: EmailPushEvent) => void;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
/**
|
|
@@ -908,6 +919,9 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
908
919
|
this.currentEmailPushOff = api.subscribeEmailPushEvents({
|
|
909
920
|
cameraId: callbacks.emailPushCameraId(),
|
|
910
921
|
channel: 0,
|
|
922
|
+
...(callbacks.onEmailPushEvent
|
|
923
|
+
? { onEvent: callbacks.onEmailPushEvent }
|
|
924
|
+
: {}),
|
|
911
925
|
});
|
|
912
926
|
logger.debug("Bridged email-push bus to onSimpleEvent");
|
|
913
927
|
} catch (e) {
|
package/src/camera.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
BatteryInfo,
|
|
3
3
|
DeviceCapabilities,
|
|
4
|
+
EmailPushEvent,
|
|
4
5
|
NativeVideoStreamVariant,
|
|
5
6
|
PtzCommand,
|
|
6
7
|
PtzPreset,
|
|
@@ -1614,6 +1615,14 @@ export class ReolinkCamera
|
|
|
1614
1615
|
// (singleton under the provider) maps `cam-<nativeId>@<domain>`
|
|
1615
1616
|
// RCPT addresses back to this `nativeId`.
|
|
1616
1617
|
emailPushCameraId: () => this.nativeId ?? "",
|
|
1618
|
+
// When the e-mail carries the snapshot Reolink sends with
|
|
1619
|
+
// `attachmentType=picture`, hand it to the camera's `lastPicture`
|
|
1620
|
+
// cache. Downstream consumers (Snapshot mixin → MQTT image
|
|
1621
|
+
// entity, Advanced Notifier, HA discovery) call `takePicture()`
|
|
1622
|
+
// on the back of the motion event; for battery cams that returns
|
|
1623
|
+
// the cached value, so dropping the fresh snapshot in here makes
|
|
1624
|
+
// the trigger frame surface in MQTT/HA without waking the camera.
|
|
1625
|
+
onEmailPushEvent: (event) => this.handleEmailPushSnapshot(event),
|
|
1617
1626
|
};
|
|
1618
1627
|
}
|
|
1619
1628
|
|
|
@@ -2734,6 +2743,38 @@ export class ReolinkCamera
|
|
|
2734
2743
|
}
|
|
2735
2744
|
}
|
|
2736
2745
|
|
|
2746
|
+
/**
|
|
2747
|
+
* Called from `baichuan-base.subscribeToEvents` when a per-camera
|
|
2748
|
+
* email-push event lands on the bus. We don't use the e-mail's image
|
|
2749
|
+
* attachment — Reolink firmwares vary on quality/encoding and we'd
|
|
2750
|
+
* be reimplementing the snapshot pipeline. Instead we leverage the
|
|
2751
|
+
* fact that the camera is *guaranteed awake* right now (it just
|
|
2752
|
+
* pushed an e-mail) and call the live Baichuan snapshot API: it
|
|
2753
|
+
* returns a fresh frame in milliseconds without forcing an extra
|
|
2754
|
+
* wake-up cycle. The result is stashed in `lastPicture` so the
|
|
2755
|
+
* Snapshot mixin → MQTT image entity (and any other consumer that
|
|
2756
|
+
* calls `takePicture()` on the back of the motion event) serves the
|
|
2757
|
+
* trigger frame instead of the previous cached one.
|
|
2758
|
+
*/
|
|
2759
|
+
private async handleEmailPushSnapshot(
|
|
2760
|
+
event: EmailPushEvent,
|
|
2761
|
+
): Promise<void> {
|
|
2762
|
+
const logger = this.getBaichuanLogger();
|
|
2763
|
+
try {
|
|
2764
|
+
const client = await this.ensureClient();
|
|
2765
|
+
const mo = await this.takePictureInternal(client);
|
|
2766
|
+
this.lastPicture = { mo, atMs: event.receivedAtMs };
|
|
2767
|
+
this.forceNewSnapshot = false;
|
|
2768
|
+
logger.log(
|
|
2769
|
+
`E-mail Push snapshot fetched live (type=${event.inferredType}) — next takePicture will serve it`,
|
|
2770
|
+
);
|
|
2771
|
+
} catch (e) {
|
|
2772
|
+
logger.warn(
|
|
2773
|
+
`E-mail Push: live snapshot fetch failed: ${e?.message || String(e)}`,
|
|
2774
|
+
);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2737
2778
|
async takePictureInternal(client: ReolinkBaichuanApi) {
|
|
2738
2779
|
const { rtspChannel, variantType } = this.storageSettings.values;
|
|
2739
2780
|
const logger = this.getBaichuanLogger();
|
|
@@ -214,7 +214,13 @@ export class EmailPushServerDevice
|
|
|
214
214
|
managerHost,
|
|
215
215
|
managerPort: cfg.port,
|
|
216
216
|
domain: cfg.domain,
|
|
217
|
-
|
|
217
|
+
// The lib's `resolveCameraIdFromRecipient` matches `^cam-(.+)$`
|
|
218
|
+
// and feeds the captured group to `cameraResolver`. We MUST send
|
|
219
|
+
// the recipient with the `cam-` prefix so the camera-side
|
|
220
|
+
// MAIL FROM produces `cam-<nativeId>@<domain>`; without it every
|
|
221
|
+
// delivery is rejected with `550 Unknown recipient` and motion
|
|
222
|
+
// events never reach Scrypted.
|
|
223
|
+
recipientLocalPart: `cam-${nativeId}`,
|
|
218
224
|
sendNickname: cam.name ?? nativeId,
|
|
219
225
|
...(cfg.requireAuth
|
|
220
226
|
? {
|
|
@@ -536,7 +542,9 @@ export class EmailPushServerDevice
|
|
|
536
542
|
managerHost,
|
|
537
543
|
managerPort: cfg.port,
|
|
538
544
|
domain: cfg.domain,
|
|
539
|
-
|
|
545
|
+
// See `getManagerSetupParamsForCamera` for why the `cam-`
|
|
546
|
+
// prefix is mandatory.
|
|
547
|
+
recipientLocalPart: `cam-${nativeId}`,
|
|
540
548
|
sendNickname: cam.name ?? nativeId,
|
|
541
549
|
attachmentType: "picture",
|
|
542
550
|
triggerTypes: ["MD", "people", "vehicle"],
|