@apocaliss92/scrypted-reolink-native 0.5.5 → 0.5.7

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/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.5",
3
+ "version": "0.5.7",
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",
@@ -13,7 +13,8 @@
13
13
  "scrypted-setup-project": "scrypted-setup-project",
14
14
  "prescrypted-setup-project": "scrypted-package-json",
15
15
  "build": "scrypted-webpack",
16
- "prepublishOnly": "NODE_ENV=production scrypted-webpack",
16
+ "prepublish-patch": "./prepublish-patch.sh",
17
+ "prepublishOnly": "./prepublish-patch.sh && NODE_ENV=production scrypted-webpack",
17
18
  "prescrypted-vscode-launch": "scrypted-webpack",
18
19
  "scrypted-vscode-launch": "scrypted-deploy-debug",
19
20
  "scrypted-deploy-debug": "scrypted-deploy-debug",
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ #
3
+ # Prepublish: build linked library, reinstall, bump plugin patch version.
4
+ # Runs automatically before npm publish via prepublishOnly.
5
+ #
6
+
7
+ set -e
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ LIB_DIR="${SCRIPT_DIR}/../reolink-baichuan-js"
11
+
12
+ # ── Step 1: Build the library ───────────────────────────────────
13
+ echo "Building reolink-baichuan-js..."
14
+ cd "${LIB_DIR}"
15
+ npm run build
16
+ echo "Library built."
17
+
18
+ # ── Step 2: Reinstall in plugin (picks up fresh dist/) ──────────
19
+ echo "Installing dependencies..."
20
+ cd "${SCRIPT_DIR}"
21
+ npm install
22
+ echo "Dependencies installed."
23
+
24
+ # ── Step 3: Bump plugin patch version ───────────────────────────
25
+ echo "Bumping plugin patch version..."
26
+ npm version patch --no-git-tag-version
27
+ NEW_VERSION=$(node -p "require('./package.json').version")
28
+ echo "Plugin version: ${NEW_VERSION}"
@@ -26,8 +26,6 @@ export class ReolinkCameraSiren extends ScryptedDeviceBase implements OnOff {
26
26
  await this.camera.withBaichuanRetry(async () => {
27
27
  const api = await this.camera.ensureClient();
28
28
  await api.setSiren(channel, false);
29
- const sirenState = await api.getSiren(channel);
30
- this.on = sirenState.enabled;
31
29
  });
32
30
  this.logger.log(`Siren toggle: turnOff ok (device=${this.nativeId})`);
33
31
  } catch (e: any) {
@@ -48,11 +46,10 @@ export class ReolinkCameraSiren extends ScryptedDeviceBase implements OnOff {
48
46
  await this.camera.withBaichuanRetry(async () => {
49
47
  const api = await this.camera.ensureClient();
50
48
  await api.setSiren(channel, true);
51
- const sirenState = await api.getSiren(channel);
52
- this.on = sirenState.enabled;
53
49
  });
54
50
  this.logger.log(`Siren toggle: turnOn ok (device=${this.nativeId})`);
55
51
  } catch (e: any) {
52
+ this.on = false;
56
53
  this.logger.error(
57
54
  `Siren toggle: turnOn failed (device=${this.nativeId})`,
58
55
  e?.message || String(e),
package/src/camera.ts CHANGED
@@ -1989,6 +1989,14 @@ export class ReolinkCamera
1989
1989
  });
1990
1990
  return;
1991
1991
 
1992
+ case "battery":
1993
+ if (ev.battery) {
1994
+ this.updateBatteryInfo(ev.battery as any).catch((e) => {
1995
+ logger.debug("Error updating battery from push", e?.message || String(e));
1996
+ });
1997
+ }
1998
+ return;
1999
+
1992
2000
  case "offline":
1993
2001
  case "online":
1994
2002
  this.updateOnlineState(ev.type === "online").catch((e) => {
@@ -2787,19 +2795,10 @@ export class ReolinkCamera
2787
2795
  }
2788
2796
  }
2789
2797
 
2790
- // Align siren state (direct control)
2791
- if (hasSiren && this.siren) {
2792
- if (isInCooldown(this.auxDeviceCooldowns.siren)) {
2793
- logger.log(`[alignAuxDevicesState] Skipping siren (in cooldown)`);
2794
- } else {
2795
- try {
2796
- const sirenState = await api.getSiren(channel);
2797
- this.siren.on = sirenState.enabled;
2798
- } catch (e) {
2799
- logger.warn("Failed to align siren state", e?.message || String(e));
2800
- }
2801
- }
2802
- }
2798
+ // Siren direct control: state is managed manually by turnOn/turnOff.
2799
+ // The camera reports siren as "off" immediately after triggering (fire-and-forget),
2800
+ // so polling would always reset the switch to OFF. We skip alignment entirely
2801
+ // and let the user control the switch: ON triggers the siren, OFF stops it.
2803
2802
 
2804
2803
  // Align motion-floodlight state
2805
2804
  if (hasFloodlight && this.motionFloodlight) {
@@ -3288,7 +3287,6 @@ export class ReolinkCamera
3288
3287
  );
3289
3288
  }
3290
3289
 
3291
- logger.log(`Refreshed device capabilities`);
3292
3290
  logger.log(
3293
3291
  `Refreshed device capabilities: ${JSON.stringify({ capabilities, abilities, support, presets, objects })}`,
3294
3292
  );
@@ -3539,6 +3537,7 @@ export class ReolinkCamera
3539
3537
  }
3540
3538
 
3541
3539
  if (wasSleeping) {
3540
+ this.updateBatteryInfo().catch(() => {});
3542
3541
  this.alignAuxDevicesState().catch(() => {});
3543
3542
  if (this.forceNewSnapshot) {
3544
3543
  this.takePicture().catch(() => {});
@@ -3620,42 +3619,26 @@ export class ReolinkCamera
3620
3619
  if (batteryInfo.batteryPercent !== undefined) {
3621
3620
  const oldLevel = this.batteryLevel;
3622
3621
  const oldChargeState = this.chargeState;
3623
- // adapterStatus: "adapter" | "solarPanel" | "none" sotto carica se adapter collegato (anche con chargeComplete)
3624
- const isSottoCarica =
3622
+ // adapterStatus: "adapter" | "solarPanel" = charging, "none" = not charging
3623
+ const isCharging =
3625
3624
  batteryInfo.adapterStatus === "adapter" ||
3626
3625
  batteryInfo.adapterStatus === "solarPanel";
3627
- const newChargeState =
3628
- batteryInfo.adapterStatus !== undefined
3629
- ? isSottoCarica
3630
- ? ChargeState.Charging
3631
- : ChargeState.NotCharging
3632
- : undefined;
3626
+ const newChargeState = isCharging
3627
+ ? ChargeState.Charging
3628
+ : ChargeState.NotCharging;
3633
3629
 
3634
3630
  this.batteryLevel = batteryInfo.batteryPercent;
3635
- if (newChargeState !== undefined) {
3636
- this.chargeState = newChargeState;
3637
- }
3631
+ this.chargeState = newChargeState;
3638
3632
 
3639
3633
  // Log only if battery level changed
3640
3634
  if (oldLevel !== batteryInfo.batteryPercent) {
3641
- if (batteryInfo.adapterStatus !== undefined) {
3642
- logger.log(
3643
- `Battery level changed: ${oldLevel}% → ${batteryInfo.batteryPercent}% (sotto carica: ${isSottoCarica}, adapterStatus: ${batteryInfo.adapterStatus}, chargeStatus: ${batteryInfo.chargeStatus ?? "—"})`,
3644
- );
3645
- } else {
3646
- logger.log(
3647
- `Battery level changed: ${oldLevel}% → ${batteryInfo.batteryPercent}%`,
3648
- );
3649
- }
3635
+ logger.log(
3636
+ `Battery level changed: ${oldLevel}% → ${batteryInfo.batteryPercent}% (charging: ${isCharging}, adapter: ${batteryInfo.adapterStatus ?? "unknown"}, status: ${batteryInfo.chargeStatus ?? "unknown"})`,
3637
+ );
3650
3638
  } else if (oldLevel === undefined) {
3651
- // First time setting battery level
3652
- if (batteryInfo.adapterStatus !== undefined) {
3653
- logger.log(
3654
- `Battery level set: ${batteryInfo.batteryPercent}% (sotto carica: ${isSottoCarica}, adapterStatus: ${batteryInfo.adapterStatus})`,
3655
- );
3656
- } else {
3657
- logger.log(`Battery level set: ${batteryInfo.batteryPercent}%`);
3658
- }
3639
+ logger.log(
3640
+ `Battery level set: ${batteryInfo.batteryPercent}% (charging: ${isCharging}, adapter: ${batteryInfo.adapterStatus ?? "unknown"})`,
3641
+ );
3659
3642
  }
3660
3643
 
3661
3644
  // Forward battery/charge state changes to Scrypted (plugin, HomeKit, etc.)
@@ -3703,49 +3686,16 @@ export class ReolinkCamera
3703
3686
  return;
3704
3687
  }
3705
3688
 
3706
- // Check current sleep status
3707
- let sleepStatus = api.getSleepStatus({ channel });
3689
+ // Check current sleep status — only update if already awake
3690
+ const sleepStatus = api.getSleepStatus({ channel });
3708
3691
 
3709
- // If camera is sleeping, wake it up
3710
3692
  if (sleepStatus.state === "sleeping") {
3711
- logger.log("Camera is sleeping, waking up for periodic update...");
3712
- try {
3713
- await api.wakeUp(channel, { waitAfterWakeMs: 2000 });
3714
- logger.log("Wake command sent, waiting for camera to wake up...");
3715
- } catch (wakeError) {
3716
- logger.error(
3717
- "Failed to wake up camera:",
3718
- wakeError?.message || String(wakeError),
3719
- );
3720
- return;
3721
- }
3722
-
3723
- // Poll until camera is awake (with timeout)
3724
- const wakeTimeoutMs = 30000; // 30 seconds max
3725
- const startWakePoll = Date.now();
3726
- let awake = false;
3727
-
3728
- while (Date.now() - startWakePoll < wakeTimeoutMs) {
3729
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Check every second
3730
- sleepStatus = api.getSleepStatus({ channel });
3731
- if (sleepStatus.state === "awake") {
3732
- awake = true;
3733
- logger.log("Camera is now awake");
3734
- this.sleeping = false;
3735
- break;
3736
- }
3737
- }
3738
-
3739
- if (!awake) {
3740
- logger.error(
3741
- "Camera did not wake up within timeout, skipping update",
3742
- );
3743
- return;
3744
- }
3745
- } else if (sleepStatus.state === "awake") {
3746
- this.sleeping = false;
3693
+ logger.debug("Camera is sleeping, skipping periodic battery update");
3694
+ return;
3747
3695
  }
3748
3696
 
3697
+ this.sleeping = false;
3698
+
3749
3699
  // Now that camera is awake, update all states
3750
3700
  // 1. Update battery info
3751
3701
  try {