@rhizomatics/signalk-bluetti-plugin 1.0.6-alpha → 1.0.8-alpha

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.
Files changed (3) hide show
  1. package/index.js +37 -1
  2. package/lib/device.js +12 -0
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -169,6 +169,17 @@ module.exports = function (app) {
169
169
  return;
170
170
  }
171
171
 
172
+ // Validate any explicitly-configured encryption CSV paths up front.
173
+ for (const cfg of devices) {
174
+ if (cfg.encryptionCsvPath) {
175
+ const err = validateEncryptionCsvPath(cfg.encryptionCsvPath, cfg.name);
176
+ if (err) {
177
+ app.setPluginError(err);
178
+ return;
179
+ }
180
+ }
181
+ }
182
+
172
183
  // Build address → cfg lookup (normalise to lowercase, no colons)
173
184
  const normalise = (addr) => addr.toLowerCase().replace(/:/g, '');
174
185
  const pending = new Map(devices.map(cfg => [normalise(cfg.address), cfg]));
@@ -180,7 +191,11 @@ module.exports = function (app) {
180
191
  if (cfg) {
181
192
  pending.delete(normalise(address));
182
193
  app.setPluginStatus(`Found ${name} [${address}] — connecting …`);
183
- startDevice(cfg, peripheral, name, deps);
194
+ // Noble cannot scan and connect simultaneously — stop scan first.
195
+ // A short delay lets the BlueZ adapter finish scanning before we connect.
196
+ // The scanComplete handler will restart scanning for any remaining pending devices.
197
+ scanner.stopScan();
198
+ setTimeout(() => startDevice(cfg, peripheral, name, deps), 500);
184
199
  } else {
185
200
  log(`Discovered unconfigured Bluetti device: ${name} [${address}]`);
186
201
  }
@@ -212,6 +227,27 @@ module.exports = function (app) {
212
227
  }
213
228
  }
214
229
 
230
+ function validateEncryptionCsvPath(csvPath, deviceName) {
231
+ try {
232
+ fs.accessSync(csvPath, fs.constants.R_OK);
233
+ } catch (_) {
234
+ return `[${deviceName}] Encryption CSV not found or not readable: ${csvPath}`;
235
+ }
236
+ let lines;
237
+ try {
238
+ lines = fs.readFileSync(csvPath, 'utf8').split('\n').map(l => l.trim()).filter(Boolean);
239
+ } catch (err) {
240
+ return `[${deviceName}] Could not read encryption CSV "${csvPath}": ${err.message}`;
241
+ }
242
+ if (lines[0] !== 'bluetti') {
243
+ return `[${deviceName}] Encryption CSV does not look like a Bluetti key file (expected first line "bluetti"): ${csvPath}`;
244
+ }
245
+ if (lines.length < 4) {
246
+ return `[${deviceName}] Encryption CSV is missing the key line (expected 4 lines): ${csvPath}`;
247
+ }
248
+ return null;
249
+ }
250
+
215
251
  function resolveRegisterMapPath(cfg) {
216
252
  const { builtinModel, csvPath } = cfg;
217
253
  if (!builtinModel || builtinModel === 'custom') {
package/lib/device.js CHANGED
@@ -13,6 +13,7 @@ const ALT_SERVICE_UUID = 'ffe0';
13
13
  const ALT_CHAR_UUID = 'ffe1';
14
14
 
15
15
  const RECONNECT_DELAYS = [5000, 10000, 20000, 30000, 60000]; // ms, capped at last value
16
+ const CONNECT_TIMEOUT_MS = 15000;
16
17
 
17
18
  class BluettiDevice extends EventEmitter {
18
19
  constructor({ address, name, peripheral, fields, pollIntervalMs = 10000, xorKey = null, log }) {
@@ -60,7 +61,18 @@ class BluettiDevice extends EventEmitter {
60
61
  if (this._stopped) return;
61
62
  this._log(`[${this._name}] Connecting to ${this._address} …`);
62
63
 
64
+ let settled = false;
65
+ const connectTimer = setTimeout(() => {
66
+ if (settled) return;
67
+ settled = true;
68
+ this._log(`[${this._name}] Connect timed out after ${CONNECT_TIMEOUT_MS / 1000}s — will retry`);
69
+ this._scheduleReconnect();
70
+ }, CONNECT_TIMEOUT_MS);
71
+
63
72
  this._peripheral.connect((err) => {
73
+ if (settled) return;
74
+ settled = true;
75
+ clearTimeout(connectTimer);
64
76
  if (err) {
65
77
  this._log(`[${this._name}] Connect error: ${err.message}`);
66
78
  this._scheduleReconnect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhizomatics/signalk-bluetti-plugin",
3
- "version": "1.0.6-alpha",
3
+ "version": "1.0.8-alpha",
4
4
  "signalk": {
5
5
  "displayName": "Bluetti Monitoring"
6
6
  },