@rhizomatics/signalk-bluetti-plugin 1.1.0-alpha → 1.1.2-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.
- package/index.js +9 -7
- package/lib/device.js +24 -8
- package/lib/scanner.js +10 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -214,16 +214,18 @@ module.exports = function (app) {
|
|
|
214
214
|
scanner.startScan(30000);
|
|
215
215
|
};
|
|
216
216
|
|
|
217
|
-
// Search $HOME for
|
|
217
|
+
// Search $HOME for an encryption CSV for bleName.
|
|
218
|
+
// Priority: 1) <bleName>.csv 2) bluetti_device_licence.csv
|
|
218
219
|
function findEncryptionCsvInHome(bleName) {
|
|
219
220
|
const homeDir = os.homedir();
|
|
220
|
-
const target = `${bleName.toLowerCase()}.csv`;
|
|
221
221
|
try {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
const files = fs.readdirSync(homeDir);
|
|
223
|
+
const specific = files.find(f => f.toLowerCase() === `${bleName.toLowerCase()}.csv`);
|
|
224
|
+
if (specific) return path.join(homeDir, specific);
|
|
225
|
+
const generic = files.find(f => f.toLowerCase() === 'bluetti_device_licence.csv');
|
|
226
|
+
if (generic) return path.join(homeDir, generic);
|
|
227
|
+
} catch (_) {}
|
|
228
|
+
return null;
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
function validateEncryptionCsvPath(csvPath, deviceName) {
|
package/lib/device.js
CHANGED
|
@@ -12,8 +12,9 @@ const WRITE_UUID = 'ff02';
|
|
|
12
12
|
const ALT_SERVICE_UUID = 'ffe0';
|
|
13
13
|
const ALT_CHAR_UUID = 'ffe1';
|
|
14
14
|
|
|
15
|
-
const RECONNECT_DELAYS
|
|
16
|
-
const CONNECT_TIMEOUT_MS
|
|
15
|
+
const RECONNECT_DELAYS = [5000, 10000, 20000, 30000, 60000]; // ms, capped at last value
|
|
16
|
+
const CONNECT_TIMEOUT_MS = 30000;
|
|
17
|
+
const DISCOVER_TIMEOUT_MS = 20000;
|
|
17
18
|
|
|
18
19
|
class BluettiDevice extends EventEmitter {
|
|
19
20
|
constructor({ address, name, device, fields, pollIntervalMs = 10000, xorKey = null, log }) {
|
|
@@ -63,6 +64,11 @@ class BluettiDevice extends EventEmitter {
|
|
|
63
64
|
|
|
64
65
|
async _connect() {
|
|
65
66
|
if (this._stopped) return;
|
|
67
|
+
// Cancel any lingering BlueZ connection state (e.g. a previous attempt that
|
|
68
|
+
// timed out on our side but is still in progress inside BlueZ).
|
|
69
|
+
if (!this._connected) {
|
|
70
|
+
try { await this._device.disconnect(); } catch (_) {}
|
|
71
|
+
}
|
|
66
72
|
this._log(`[${this._name}] Connecting to ${this._address} …`);
|
|
67
73
|
|
|
68
74
|
// Clear any stale disconnect handler from a previous attempt.
|
|
@@ -110,16 +116,26 @@ class BluettiDevice extends EventEmitter {
|
|
|
110
116
|
};
|
|
111
117
|
this._device.once('disconnect', this._disconnectHandler);
|
|
112
118
|
|
|
113
|
-
|
|
119
|
+
let discoverSettled = false;
|
|
120
|
+
const discoverTimer = setTimeout(() => {
|
|
121
|
+
if (discoverSettled) return;
|
|
122
|
+
discoverSettled = true;
|
|
123
|
+
this._log(`[${this._name}] Service discovery timed out — will retry`);
|
|
124
|
+
this._scheduleReconnect();
|
|
125
|
+
}, DISCOVER_TIMEOUT_MS);
|
|
126
|
+
|
|
127
|
+
await this._discoverServices(() => !discoverSettled);
|
|
128
|
+
discoverSettled = true;
|
|
129
|
+
clearTimeout(discoverTimer);
|
|
114
130
|
}
|
|
115
131
|
|
|
116
|
-
async _discoverServices() {
|
|
132
|
+
async _discoverServices(isActive) {
|
|
117
133
|
let gatt;
|
|
118
134
|
try {
|
|
119
135
|
gatt = await this._device.gatt();
|
|
120
136
|
} catch (err) {
|
|
121
137
|
this._log(`[${this._name}] GATT error: ${err.message}`);
|
|
122
|
-
this._scheduleReconnect();
|
|
138
|
+
if (isActive()) this._scheduleReconnect();
|
|
123
139
|
return;
|
|
124
140
|
}
|
|
125
141
|
|
|
@@ -132,7 +148,7 @@ class BluettiDevice extends EventEmitter {
|
|
|
132
148
|
}
|
|
133
149
|
if (!service) {
|
|
134
150
|
this._log(`[${this._name}] Could not find BLE service (tried ${SERVICE_UUID}, ${ALT_SERVICE_UUID})`);
|
|
135
|
-
this._scheduleReconnect();
|
|
151
|
+
if (isActive()) this._scheduleReconnect();
|
|
136
152
|
return;
|
|
137
153
|
}
|
|
138
154
|
|
|
@@ -148,7 +164,7 @@ class BluettiDevice extends EventEmitter {
|
|
|
148
164
|
|
|
149
165
|
if (!notifyChar || !writeChar) {
|
|
150
166
|
this._log(`[${this._name}] Could not find required GATT characteristics`);
|
|
151
|
-
this._scheduleReconnect();
|
|
167
|
+
if (isActive()) this._scheduleReconnect();
|
|
152
168
|
return;
|
|
153
169
|
}
|
|
154
170
|
|
|
@@ -156,7 +172,7 @@ class BluettiDevice extends EventEmitter {
|
|
|
156
172
|
await notifyChar.startNotifications();
|
|
157
173
|
} catch (err) {
|
|
158
174
|
this._log(`[${this._name}] Subscribe error: ${err.message}`);
|
|
159
|
-
this._scheduleReconnect();
|
|
175
|
+
if (isActive()) this._scheduleReconnect();
|
|
160
176
|
return;
|
|
161
177
|
}
|
|
162
178
|
|
package/lib/scanner.js
CHANGED
|
@@ -70,17 +70,22 @@ class Scanner extends EventEmitter {
|
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
for (const addr of addresses) {
|
|
73
|
+
if (!this._scanning) break; // scanner stopped while we were iterating
|
|
73
74
|
const normAddr = addr.toLowerCase().replace(/:/g, '');
|
|
74
75
|
if (this._found.has(normAddr)) continue;
|
|
76
|
+
// Reserve the slot immediately to prevent concurrent _poll() calls from
|
|
77
|
+
// both processing the same address before either stores the device.
|
|
78
|
+
this._found.set(normAddr, null);
|
|
75
79
|
try {
|
|
76
80
|
const device = await this._adapter.getDevice(addr);
|
|
77
81
|
const name = await Promise.resolve(device.getName()).catch(() => '') || '';
|
|
78
|
-
if (!
|
|
82
|
+
if (!this._scanning) break;
|
|
83
|
+
if (!isBluettiDevice(name)) { this._found.delete(normAddr); continue; }
|
|
79
84
|
this._found.set(normAddr, { address: addr, name, device });
|
|
80
85
|
this._log(`Discovered Bluetti device: ${name} [${addr}]`);
|
|
81
86
|
this.emit('discovered', { address: addr, name, device });
|
|
82
87
|
} catch (_) {
|
|
83
|
-
//
|
|
88
|
+
this._found.delete(normAddr); // allow retry on transient D-Bus error
|
|
84
89
|
}
|
|
85
90
|
}
|
|
86
91
|
}
|
|
@@ -92,8 +97,9 @@ class Scanner extends EventEmitter {
|
|
|
92
97
|
this._scanning = false;
|
|
93
98
|
// Decrement BlueZ discovery reference count — other plugins' sessions keep scanning.
|
|
94
99
|
if (this._adapter) this._adapter.stopDiscovery().catch(() => {});
|
|
95
|
-
|
|
96
|
-
this.
|
|
100
|
+
const found = [...this._found.values()].filter(v => v !== null);
|
|
101
|
+
this._log(`Scan complete. Found ${found.length} Bluetti device(s).`);
|
|
102
|
+
this.emit('scanComplete', found.map(({ address, name }) => ({ address, name })));
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
stopAll() {
|