@apocaliss92/scrypted-reolink-native 0.4.41 → 0.4.43

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.4.41",
3
+ "version": "0.4.43",
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",
@@ -6,11 +6,18 @@ import {
6
6
  SettingValue,
7
7
  } from "@scrypted/sdk";
8
8
  import { StorageSettings } from "@scrypted/sdk/storage-settings";
9
+ import type { ChimeCfg } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
9
10
  import type { ReolinkCamera } from "../camera";
10
11
 
11
12
  /**
12
- * Chime: mute/unmute a paired wireless Reolink Chime receiver.
13
- * turnOn() unmutes (time=0); turnOff() silences for muteDurationSec seconds.
13
+ * Chime: enable/disable a paired wireless Reolink Chime receiver.
14
+ *
15
+ * Uses SetDingDongCfg (cmd 487) to enable/disable all event types on the chime,
16
+ * matching the approach used by Home Assistant / reolink_aio.
17
+ *
18
+ * turnOn() enables all event types (chime rings on events).
19
+ * turnOff() disables all event types (chime stays silent).
20
+ *
14
21
  * The chime ID is auto-synced from getDingDongList during alignAuxDevicesState.
15
22
  */
16
23
  export class ReolinkCameraChime
@@ -29,13 +36,6 @@ export class ReolinkCameraChime
29
36
  defaultValue: -1,
30
37
  readonly: true,
31
38
  },
32
- muteDurationSec: {
33
- title: "Mute Duration (seconds)",
34
- description:
35
- "How long to mute the wireless chime when turned off (default: 3600 = 1 hour).",
36
- type: "number",
37
- defaultValue: 3600,
38
- },
39
39
  });
40
40
 
41
41
  constructor(
@@ -53,27 +53,52 @@ export class ReolinkCameraChime
53
53
  await this.storageSettings.putSetting(key, value);
54
54
  }
55
55
 
56
- private get wirelessChimeId(): number {
56
+ get wirelessChimeId(): number {
57
57
  return this.storageSettings.values.wirelessChimeId ?? -1;
58
58
  }
59
59
 
60
+ private async getChimeCfg(): Promise<ChimeCfg | undefined> {
61
+ const channel = this.camera.storageSettings.values.rtspChannel;
62
+ const chimeId = this.wirelessChimeId;
63
+ if (chimeId < 0) return undefined;
64
+ const api = await this.camera.ensureClient();
65
+ const configs = await api.getDingDongCfg(channel);
66
+ return configs.find(c => c.id === chimeId);
67
+ }
68
+
69
+ /**
70
+ * Determine if the chime is active by checking if any event type is enabled.
71
+ */
72
+ async syncStateFromDevice(): Promise<boolean | undefined> {
73
+ const cfg = await this.getChimeCfg();
74
+ if (!cfg) return undefined;
75
+ const eventTypes = Object.values(cfg.type);
76
+ if (eventTypes.length === 0) return undefined;
77
+ return eventTypes.some(e => e.valid === 1);
78
+ }
79
+
60
80
  async turnOn(): Promise<void> {
61
81
  const channel = this.camera.storageSettings.values.rtspChannel;
62
82
  const chimeId = this.wirelessChimeId;
63
- this.logger.log(`Chime: unmute (device=${this.nativeId}, chimeId=${chimeId})`);
64
- this.on = true;
83
+ this.logger.log(`Chime: enable all events (device=${this.nativeId}, chimeId=${chimeId})`);
65
84
  this.camera.auxDeviceCooldowns.chime = Date.now();
66
85
  try {
67
86
  await this.camera.withBaichuanRetry(async () => {
87
+ const cfg = await this.getChimeCfg();
88
+ if (!cfg) throw new Error(`Chime config not found for chimeId=${chimeId}`);
68
89
  const api = await this.camera.ensureClient();
69
- const state = await api.setDingDongSilent(chimeId, 0, channel);
70
- this.on = state.active;
90
+ for (const [eventType, alarmCfg] of Object.entries(cfg.type)) {
91
+ if (alarmCfg.valid !== 1) {
92
+ const musicId = alarmCfg.musicId || 1;
93
+ await api.setDingDongCfg(chimeId, eventType, 1, musicId, channel);
94
+ }
95
+ }
71
96
  });
72
- this.logger.log(`Chime: unmute ok (device=${this.nativeId})`);
97
+ this.on = true;
98
+ this.logger.log(`Chime: enable ok (device=${this.nativeId})`);
73
99
  } catch (e: any) {
74
- this.on = false;
75
100
  this.logger.error(
76
- `Chime: unmute failed (device=${this.nativeId})`,
101
+ `Chime: enable failed (device=${this.nativeId})`,
77
102
  e?.message || String(e),
78
103
  );
79
104
  throw e;
@@ -83,21 +108,24 @@ export class ReolinkCameraChime
83
108
  async turnOff(): Promise<void> {
84
109
  const channel = this.camera.storageSettings.values.rtspChannel;
85
110
  const chimeId = this.wirelessChimeId;
86
- const muteSec = this.storageSettings.values.muteDurationSec ?? 3600;
87
- this.logger.log(`Chime: mute for ${muteSec}s (device=${this.nativeId}, chimeId=${chimeId})`);
88
- this.on = false;
111
+ this.logger.log(`Chime: disable all events (device=${this.nativeId}, chimeId=${chimeId})`);
89
112
  this.camera.auxDeviceCooldowns.chime = Date.now();
90
113
  try {
91
114
  await this.camera.withBaichuanRetry(async () => {
115
+ const cfg = await this.getChimeCfg();
116
+ if (!cfg) throw new Error(`Chime config not found for chimeId=${chimeId}`);
92
117
  const api = await this.camera.ensureClient();
93
- const state = await api.setDingDongSilent(chimeId, muteSec, channel);
94
- this.on = state.active;
118
+ for (const [eventType, alarmCfg] of Object.entries(cfg.type)) {
119
+ if (alarmCfg.valid !== 0) {
120
+ await api.setDingDongCfg(chimeId, eventType, 0, alarmCfg.musicId, channel);
121
+ }
122
+ }
95
123
  });
96
- this.logger.log(`Chime: mute ok (device=${this.nativeId})`);
124
+ this.on = false;
125
+ this.logger.log(`Chime: disable ok (device=${this.nativeId})`);
97
126
  } catch (e: any) {
98
- this.on = true;
99
127
  this.logger.error(
100
- `Chime: mute failed (device=${this.nativeId})`,
128
+ `Chime: disable failed (device=${this.nativeId})`,
101
129
  e?.message || String(e),
102
130
  );
103
131
  throw e;
package/src/camera.ts CHANGED
@@ -2802,7 +2802,7 @@ export class ReolinkCamera
2802
2802
  }
2803
2803
  }
2804
2804
 
2805
- // Align chime state (wireless only)
2805
+ // Align wireless chime state via getDingDongCfg (cmd 486)
2806
2806
  if (hasWirelessChime && this.chime) {
2807
2807
  if (isInCooldown(this.auxDeviceCooldowns.chime)) {
2808
2808
  logger.log(`[alignAuxDevicesState] Skipping chime (in cooldown)`);
@@ -2812,8 +2812,10 @@ export class ReolinkCamera
2812
2812
  if (wirelessChimes.length > 0) {
2813
2813
  const chimeId = wirelessChimes[0].id;
2814
2814
  this.chime.storageSettings.values.wirelessChimeId = chimeId;
2815
- const silentState = await api.getDingDongSilent(chimeId, channel);
2816
- this.chime.on = silentState.active;
2815
+ const isActive = await this.chime.syncStateFromDevice();
2816
+ if (isActive !== undefined) {
2817
+ this.chime.on = isActive;
2818
+ }
2817
2819
  }
2818
2820
  } catch (e) {
2819
2821
  logger.warn(