@apocaliss92/scrypted-reolink-native 0.4.43 → 0.4.45

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.43",
3
+ "version": "0.4.45",
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",
@@ -57,6 +57,31 @@ export class ReolinkCameraChime
57
57
  return this.storageSettings.values.wirelessChimeId ?? -1;
58
58
  }
59
59
 
60
+ /**
61
+ * Refresh chime ID from device when -1.
62
+ * Python lib: GetDingDongList uses deviceId; GetDingDongCfg uses ringId (skips if < 0).
63
+ * Returns true if we have a valid chimeId; false if still not discovered.
64
+ */
65
+ private async ensureChimeId(): Promise<boolean> {
66
+ if (this.wirelessChimeId >= 0) return true;
67
+ const channel = this.camera.storageSettings.values.rtspChannel;
68
+ const api = await this.camera.ensureClient();
69
+ const list = await api.getDingDongList(channel);
70
+ if (list.length > 0 && list[0].id >= 0) {
71
+ this.storageSettings.values.wirelessChimeId = list[0].id;
72
+ this.logger.log(`Chime: discovered chimeId=${list[0].id} from GetDingDongList`);
73
+ return true;
74
+ }
75
+ const configs = await api.getDingDongCfg(channel);
76
+ const cfg = configs.find(c => c.id >= 0);
77
+ if (cfg) {
78
+ this.storageSettings.values.wirelessChimeId = cfg.id;
79
+ this.logger.log(`Chime: discovered chimeId=${cfg.id} from GetDingDongCfg`);
80
+ return true;
81
+ }
82
+ return false;
83
+ }
84
+
60
85
  private async getChimeCfg(): Promise<ChimeCfg | undefined> {
61
86
  const channel = this.camera.storageSettings.values.rtspChannel;
62
87
  const chimeId = this.wirelessChimeId;
@@ -70,6 +95,7 @@ export class ReolinkCameraChime
70
95
  * Determine if the chime is active by checking if any event type is enabled.
71
96
  */
72
97
  async syncStateFromDevice(): Promise<boolean | undefined> {
98
+ await this.ensureChimeId();
73
99
  const cfg = await this.getChimeCfg();
74
100
  if (!cfg) return undefined;
75
101
  const eventTypes = Object.values(cfg.type);
@@ -78,12 +104,18 @@ export class ReolinkCameraChime
78
104
  }
79
105
 
80
106
  async turnOn(): Promise<void> {
81
- const channel = this.camera.storageSettings.values.rtspChannel;
82
- const chimeId = this.wirelessChimeId;
83
- this.logger.log(`Chime: enable all events (device=${this.nativeId}, chimeId=${chimeId})`);
84
107
  this.camera.auxDeviceCooldowns.chime = Date.now();
85
108
  try {
86
109
  await this.camera.withBaichuanRetry(async () => {
110
+ const discovered = await this.ensureChimeId();
111
+ if (!discovered) {
112
+ throw new Error(
113
+ "Wireless chime not discovered. Ensure a Reolink Chime is paired with the doorbell and try again.",
114
+ );
115
+ }
116
+ const channel = this.camera.storageSettings.values.rtspChannel;
117
+ const chimeId = this.wirelessChimeId;
118
+ this.logger.log(`Chime: enable all events (device=${this.nativeId}, chimeId=${chimeId})`);
87
119
  const cfg = await this.getChimeCfg();
88
120
  if (!cfg) throw new Error(`Chime config not found for chimeId=${chimeId}`);
89
121
  const api = await this.camera.ensureClient();
@@ -106,12 +138,18 @@ export class ReolinkCameraChime
106
138
  }
107
139
 
108
140
  async turnOff(): Promise<void> {
109
- const channel = this.camera.storageSettings.values.rtspChannel;
110
- const chimeId = this.wirelessChimeId;
111
- this.logger.log(`Chime: disable all events (device=${this.nativeId}, chimeId=${chimeId})`);
112
141
  this.camera.auxDeviceCooldowns.chime = Date.now();
113
142
  try {
114
143
  await this.camera.withBaichuanRetry(async () => {
144
+ const discovered = await this.ensureChimeId();
145
+ if (!discovered) {
146
+ throw new Error(
147
+ "Wireless chime not discovered. Ensure a Reolink Chime is paired with the doorbell and try again.",
148
+ );
149
+ }
150
+ const channel = this.camera.storageSettings.values.rtspChannel;
151
+ const chimeId = this.wirelessChimeId;
152
+ this.logger.log(`Chime: disable all events (device=${this.nativeId}, chimeId=${chimeId})`);
115
153
  const cfg = await this.getChimeCfg();
116
154
  if (!cfg) throw new Error(`Chime config not found for chimeId=${chimeId}`);
117
155
  const api = await this.camera.ensureClient();
package/src/camera.ts CHANGED
@@ -126,6 +126,30 @@ export class ReolinkCamera
126
126
  title: "Debug logs",
127
127
  type: "boolean",
128
128
  immediate: true,
129
+ onPut: async (ov, value) => {
130
+ if (ov === value) return;
131
+ const logger = this.getBaichuanLogger();
132
+ if (this.resetBaichuanClient) {
133
+ if (this.debugLogsResetTimeout) {
134
+ clearTimeout(this.debugLogsResetTimeout);
135
+ this.debugLogsResetTimeout = undefined;
136
+ }
137
+ this.debugLogsResetTimeout = setTimeout(async () => {
138
+ this.debugLogsResetTimeout = undefined;
139
+ try {
140
+ await this.resetBaichuanClient("debugLogs changed");
141
+ this.baichuanApi = undefined;
142
+ this.ensureClientPromise = undefined;
143
+ await this.ensureClient();
144
+ } catch (e) {
145
+ logger.warn(
146
+ "Failed to reset client after debug logs change",
147
+ e?.message || String(e),
148
+ );
149
+ }
150
+ }, 2000);
151
+ }
152
+ },
129
153
  },
130
154
  // Basic connection settings
131
155
  ipAddress: {
@@ -1674,10 +1698,11 @@ export class ReolinkCamera
1674
1698
  if (this.cachedCapabilities) return this.cachedCapabilities;
1675
1699
 
1676
1700
  const client = await this.ensureClient();
1677
- const { capabilities } = await client.getDeviceCapabilities(
1701
+ const { capabilities, debug } = await client.getDeviceCapabilities(
1678
1702
  this.storageSettings.values.rtspChannel ?? 0,
1679
1703
  );
1680
1704
  this.cachedCapabilities = capabilities;
1705
+ logger.debug("Device capabilities retrieved", debug);
1681
1706
  return capabilities;
1682
1707
  }
1683
1708
  } catch (e) {
@@ -2284,7 +2309,13 @@ export class ReolinkCamera
2284
2309
  const logger = this.getBaichuanLogger();
2285
2310
  logger.debug(`Reporting devices: ${JSON.stringify(abilities)}`);
2286
2311
 
2287
- const { hasSiren, hasFloodlight, hasPir, hasAutotracking, hasWirelessChime } = abilities;
2312
+ const {
2313
+ hasSiren,
2314
+ hasFloodlight,
2315
+ hasPir,
2316
+ hasAutotracking,
2317
+ hasWirelessChime,
2318
+ } = abilities;
2288
2319
 
2289
2320
  // Define native IDs for all sub-devices
2290
2321
  const motionSirenNativeId = `${this.nativeId}${motionSirenSuffix}`;
@@ -2670,8 +2701,13 @@ export class ReolinkCamera
2670
2701
  const api = await this.ensureClient();
2671
2702
 
2672
2703
  const channel = this.storageSettings.values.rtspChannel;
2673
- const { hasSiren, hasFloodlight, hasPir, hasAutotracking, hasWirelessChime } =
2674
- await this.getAbilities();
2704
+ const {
2705
+ hasSiren,
2706
+ hasFloodlight,
2707
+ hasPir,
2708
+ hasAutotracking,
2709
+ hasWirelessChime,
2710
+ } = await this.getAbilities();
2675
2711
 
2676
2712
  // Cooldown period: 15 seconds after a manual state change
2677
2713
  // Camera can take 10+ seconds to reflect state changes in its API response
@@ -2818,10 +2854,7 @@ export class ReolinkCamera
2818
2854
  }
2819
2855
  }
2820
2856
  } catch (e) {
2821
- logger.warn(
2822
- "Failed to align chime state",
2823
- e?.message || String(e),
2824
- );
2857
+ logger.warn("Failed to align chime state", e?.message || String(e));
2825
2858
  }
2826
2859
  }
2827
2860
  }
package/src/nvr.ts CHANGED
@@ -51,6 +51,26 @@ export class ReolinkNativeNvrDevice
51
51
  title: "Debug Events",
52
52
  type: "boolean",
53
53
  immediate: true,
54
+ onPut: async (ov, value) => {
55
+ if (ov === value) return;
56
+ const logger = this.getBaichuanLogger();
57
+ if (this.debugLogsResetTimeout) {
58
+ clearTimeout(this.debugLogsResetTimeout);
59
+ this.debugLogsResetTimeout = undefined;
60
+ }
61
+ this.debugLogsResetTimeout = setTimeout(async () => {
62
+ this.debugLogsResetTimeout = undefined;
63
+ try {
64
+ await this.cleanupBaichuanApi();
65
+ await this.ensureBaichuanClient();
66
+ } catch (e) {
67
+ logger.warn(
68
+ "Failed to reset client after debug logs change",
69
+ e?.message || String(e),
70
+ );
71
+ }
72
+ }, 2000);
73
+ },
54
74
  },
55
75
  // eventSource: {
56
76
  // title: 'Event Source',