@apocaliss92/scrypted-reolink-native 0.5.14 → 0.5.19

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.14",
3
+ "version": "0.5.19",
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,10 +44,10 @@
44
44
  ]
45
45
  },
46
46
  "dependencies": {
47
+ "@apocaliss92/nodelink-js": "^0.4.26",
47
48
  "@scrypted/common": "file:../../scrypted/common",
48
49
  "@scrypted/rtsp": "file:../../scrypted/plugins/rtsp",
49
- "@scrypted/sdk": "^0.3.118",
50
- "@apocaliss92/nodelink-js": "0.4.19"
50
+ "@scrypted/sdk": "^0.3.118"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "^22.0.2"
package/src/camera.ts CHANGED
@@ -2795,10 +2795,38 @@ export class ReolinkCamera
2795
2795
  }
2796
2796
  }
2797
2797
 
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.
2798
+ // Align siren direct-control state.
2799
+ // The siren has a built-in playback duration enforced by the camera firmware,
2800
+ // so once the audio finishes the device flips back to OFF on its own. Rely
2801
+ // on the cmd 547 (SirenStatusList) push to catch that transition; fall back
2802
+ // to an active getSirenStatus() request when no push has arrived yet so the
2803
+ // switch matches the device state after a plugin restart.
2804
+ if (hasSiren && this.siren) {
2805
+ if (isInCooldown(this.auxDeviceCooldowns.siren)) {
2806
+ logger.log(`[alignAuxDevicesState] Skipping siren (in cooldown)`);
2807
+ } else {
2808
+ try {
2809
+ const cached = api.getCachedSirenStatus(channel);
2810
+ if (cached) {
2811
+ this.siren.on =
2812
+ cached.value.status || cached.value.playing === true;
2813
+ } else {
2814
+ const status = await api.getSirenStatus({ timeoutMs: 2000 });
2815
+ const list = status?.body?.SirenStatusList;
2816
+ const playing =
2817
+ typeof list?.playing === "number" ? list.playing === 1 : false;
2818
+ const enabled =
2819
+ typeof list?.status === "number" ? list.status === 1 : false;
2820
+ this.siren.on = enabled || playing;
2821
+ }
2822
+ } catch (e) {
2823
+ logger.warn(
2824
+ "Failed to align siren state",
2825
+ e?.message || String(e),
2826
+ );
2827
+ }
2828
+ }
2829
+ }
2802
2830
 
2803
2831
  // Align motion-floodlight state
2804
2832
  if (hasFloodlight && this.motionFloodlight) {
@@ -2822,14 +2850,28 @@ export class ReolinkCamera
2822
2850
  }
2823
2851
  }
2824
2852
 
2825
- // Align floodlight state (direct control)
2853
+ // Align floodlight state (direct control).
2854
+ // The camera's cmd 289 returns FloodlightTask (the scheduled task config) and
2855
+ // does NOT reflect the current manual ON/OFF — `<enable>` there is the task
2856
+ // enable, which is 0 for any camera without a configured schedule. The only
2857
+ // reliable source for the actual current state is the cmd 291 push
2858
+ // (FloodlightStatusList) that the firmware emits on every transition,
2859
+ // including the auto-off after the FloodlightManual duration expires.
2860
+ //
2861
+ // Brightness is still safe to read from cmd 289 because <brightness_cur> is
2862
+ // the persisted brightness setting and matches what the camera will apply
2863
+ // on next turnOn.
2826
2864
  if (hasFloodlight && this.floodlight) {
2827
2865
  if (isInCooldown(this.auxDeviceCooldowns.floodlight)) {
2828
2866
  logger.log(`[alignAuxDevicesState] Skipping floodlight (in cooldown)`);
2829
2867
  } else {
2830
2868
  try {
2869
+ const cached = api.getCachedFloodlightStatus(channel);
2870
+ if (cached) {
2871
+ this.floodlight.on = cached.value.status;
2872
+ }
2873
+ // Brightness is still meaningful from the task config.
2831
2874
  const ledState = await api.getWhiteLedState(channel);
2832
- this.floodlight.on = ledState.enabled;
2833
2875
  if (ledState.brightness !== undefined) {
2834
2876
  this.floodlight.brightness = ledState.brightness;
2835
2877
  }