node-red-contrib-ble-scanner 0.0.1
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/LICENSE +21 -0
- package/README.md +82 -0
- package/bin/ble-reader.js +11 -0
- package/dist/ble/backend.d.ts +82 -0
- package/dist/ble/backend.js +2 -0
- package/dist/ble/backend.js.map +1 -0
- package/dist/ble/bluez.d.ts +10 -0
- package/dist/ble/bluez.js +317 -0
- package/dist/ble/bluez.js.map +1 -0
- package/dist/ble/noble.d.ts +2 -0
- package/dist/ble/noble.js +327 -0
- package/dist/ble/noble.js.map +1 -0
- package/dist/ble/utils.d.ts +19 -0
- package/dist/ble/utils.js +57 -0
- package/dist/ble/utils.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +140 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/reader.d.ts +13 -0
- package/dist/reader.js +143 -0
- package/dist/reader.js.map +1 -0
- package/nodes/ble-reader-device.html +135 -0
- package/nodes/ble-reader-device.js +61 -0
- package/nodes/ble-reader-read.html +38 -0
- package/nodes/ble-reader-read.js +59 -0
- package/nodes/package.json +3 -0
- package/package.json +73 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { asError, matchesBleDevice, normalizeUuid, uniqueDevices } from './utils.js';
|
|
3
|
+
const DISCONNECT_TIMEOUT_MS = 3000;
|
|
4
|
+
const STOP_SCAN_TIMEOUT_MS = 250;
|
|
5
|
+
const requireOptional = createRequire(import.meta.url);
|
|
6
|
+
let nobleModule = null;
|
|
7
|
+
let activePeripheral = null;
|
|
8
|
+
const discoveredPeripherals = new Map();
|
|
9
|
+
export const nobleBackend = {
|
|
10
|
+
name: 'noble',
|
|
11
|
+
isAvailable: async () => true,
|
|
12
|
+
discover: discoverNobleDevices,
|
|
13
|
+
connect: connectNobleDevice,
|
|
14
|
+
shutdown: shutdownNoble
|
|
15
|
+
};
|
|
16
|
+
async function discoverNobleDevices(options) {
|
|
17
|
+
const noble = loadNoble();
|
|
18
|
+
await waitForPoweredOn(noble);
|
|
19
|
+
options.logger('Bluetooth adapter powered on.');
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const devices = [];
|
|
22
|
+
let settled = false;
|
|
23
|
+
const finish = async (error) => {
|
|
24
|
+
if (settled)
|
|
25
|
+
return;
|
|
26
|
+
settled = true;
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
noble.removeListener('discover', onDiscover);
|
|
29
|
+
await stopScanningAndWait(noble);
|
|
30
|
+
if (error)
|
|
31
|
+
reject(error);
|
|
32
|
+
else
|
|
33
|
+
resolve(uniqueDevices(devices));
|
|
34
|
+
};
|
|
35
|
+
const timeout = setTimeout(() => {
|
|
36
|
+
finish().catch(reject);
|
|
37
|
+
}, options.timeoutMs);
|
|
38
|
+
const onDiscover = (peripheral) => {
|
|
39
|
+
const name = peripheral.advertisement?.localName || null;
|
|
40
|
+
const serviceUuids = peripheral.advertisement?.serviceUuids || [];
|
|
41
|
+
if (!matchesBleDevice({ name, address: peripheral.address || null, serviceUuids }, { namePrefix: options.namePrefix, deviceName: options.deviceName, serviceUuid: options.matchServiceUuid })) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const device = toDiscoveredDevice(peripheral);
|
|
45
|
+
cachePeripheral(peripheral);
|
|
46
|
+
options.logger(`Discovered ${device.name || '<unnamed>'} (${device.address || device.id}, rssi=${device.rssi ?? 'unknown'}).`);
|
|
47
|
+
devices.push(device);
|
|
48
|
+
if (options.deviceName)
|
|
49
|
+
finish().catch(reject);
|
|
50
|
+
};
|
|
51
|
+
noble.on('discover', onDiscover);
|
|
52
|
+
noble.startScanning(options.scanServiceUuid ? [options.scanServiceUuid] : [], true, (error) => {
|
|
53
|
+
if (error)
|
|
54
|
+
finish(error).catch(reject);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function connectNobleDevice({ device, timeoutMs, connectTimeoutMs, notifyUuid, logger }) {
|
|
59
|
+
const noble = loadNoble();
|
|
60
|
+
await waitForPoweredOn(noble);
|
|
61
|
+
const peripheral = await connectCachedOrScan({ noble, device, scanTimeoutMs: timeoutMs, connectTimeoutMs, logger });
|
|
62
|
+
activePeripheral = peripheral;
|
|
63
|
+
logger('Discovering BLE services and characteristics.');
|
|
64
|
+
const characteristics = await discoverCharacteristics(peripheral, timeoutMs);
|
|
65
|
+
const byUuid = new Map(characteristics.map((characteristic) => [normalizeUuid(characteristic.uuid), characteristic]));
|
|
66
|
+
logger(`Discovered characteristic UUIDs: ${[...byUuid.keys()].sort().join(', ')}.`);
|
|
67
|
+
const notify = byUuid.get(normalizeUuid(notifyUuid));
|
|
68
|
+
if (!notify) {
|
|
69
|
+
throw new Error(`Missing BLE notify characteristic ${notifyUuid}. Found: ${[...byUuid.keys()].sort().join(', ')}`);
|
|
70
|
+
}
|
|
71
|
+
const wrappedNotify = wrapCharacteristic(notify);
|
|
72
|
+
await wrappedNotify.subscribe();
|
|
73
|
+
logger(`Subscribed to BLE notification characteristic ${wrappedNotify.uuid}.`);
|
|
74
|
+
return {
|
|
75
|
+
device: toDiscoveredDevice(peripheral),
|
|
76
|
+
notify: wrappedNotify,
|
|
77
|
+
disconnect: async () => {
|
|
78
|
+
await disconnectQuietly(peripheral);
|
|
79
|
+
if (activePeripheral === peripheral)
|
|
80
|
+
activePeripheral = null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async function connectCachedOrScan({ noble, device, scanTimeoutMs, connectTimeoutMs, logger }) {
|
|
85
|
+
const cached = findCachedPeripheral(device);
|
|
86
|
+
if (cached) {
|
|
87
|
+
logger(`Using recently discovered ${displayPeripheral(cached)}.`);
|
|
88
|
+
try {
|
|
89
|
+
await connectAsync(cached, connectTimeoutMs, logger);
|
|
90
|
+
return cached;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
logger(`Could not connect to cached peripheral: ${error instanceof Error ? error.message : String(error)}. Scanning again.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return scanAndConnect({ noble, device, scanTimeoutMs, connectTimeoutMs, logger });
|
|
97
|
+
}
|
|
98
|
+
function scanAndConnect({ noble, device, scanTimeoutMs, connectTimeoutMs, logger }) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
let connecting = false;
|
|
101
|
+
let settled = false;
|
|
102
|
+
const finish = async (error, peripheral) => {
|
|
103
|
+
if (settled)
|
|
104
|
+
return;
|
|
105
|
+
settled = true;
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
noble.removeListener('discover', onDiscover);
|
|
108
|
+
await stopScanningAndWait(noble);
|
|
109
|
+
if (error)
|
|
110
|
+
reject(error);
|
|
111
|
+
else if (peripheral)
|
|
112
|
+
resolve(peripheral);
|
|
113
|
+
else
|
|
114
|
+
reject(new Error('Connect completed without a peripheral.'));
|
|
115
|
+
};
|
|
116
|
+
const timeout = setTimeout(() => {
|
|
117
|
+
finish(new Error(`Timed out after ${scanTimeoutMs}ms scanning for ${device.name || device.address || device.id}.`)).catch(reject);
|
|
118
|
+
}, scanTimeoutMs);
|
|
119
|
+
const onDiscover = (peripheral) => {
|
|
120
|
+
if (connecting || !matchesKnownDevice(peripheral, device))
|
|
121
|
+
return;
|
|
122
|
+
connecting = true;
|
|
123
|
+
cachePeripheral(peripheral);
|
|
124
|
+
logger(`Found ${displayPeripheral(peripheral)}.`);
|
|
125
|
+
connectAsync(peripheral, connectTimeoutMs, logger)
|
|
126
|
+
.then(() => finish(null, peripheral))
|
|
127
|
+
.catch((error) => finish(asError(error)).catch(reject));
|
|
128
|
+
};
|
|
129
|
+
noble.on('discover', onDiscover);
|
|
130
|
+
noble.startScanning([], true, (error) => {
|
|
131
|
+
if (error)
|
|
132
|
+
finish(error).catch(reject);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function cachePeripheral(peripheral) {
|
|
137
|
+
const keys = [peripheral.id, peripheral.address, peripheral.advertisement?.localName].filter((key) => !!key);
|
|
138
|
+
for (const key of keys)
|
|
139
|
+
discoveredPeripherals.set(key.toLowerCase(), peripheral);
|
|
140
|
+
}
|
|
141
|
+
function findCachedPeripheral(device) {
|
|
142
|
+
const keys = [device.id, device.address, device.name].filter((key) => !!key);
|
|
143
|
+
for (const key of keys) {
|
|
144
|
+
const peripheral = discoveredPeripherals.get(key.toLowerCase());
|
|
145
|
+
if (peripheral)
|
|
146
|
+
return peripheral;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function matchesKnownDevice(peripheral, device) {
|
|
151
|
+
const name = peripheral.advertisement?.localName || null;
|
|
152
|
+
const address = peripheral.address || null;
|
|
153
|
+
return (!!device.name && name === device.name) || (!!device.address && address?.toLowerCase() === device.address.toLowerCase()) || peripheral.id === device.id;
|
|
154
|
+
}
|
|
155
|
+
function waitForPoweredOn(noble) {
|
|
156
|
+
if (noble.state === 'poweredOn')
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
if (noble.state === 'unsupported' || noble.state === 'unauthorized') {
|
|
159
|
+
return Promise.reject(new Error(`Bluetooth adapter state is ${noble.state}.`));
|
|
160
|
+
}
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
const onStateChange = (state) => {
|
|
163
|
+
if (state === 'poweredOn') {
|
|
164
|
+
noble.removeListener('stateChange', onStateChange);
|
|
165
|
+
resolve();
|
|
166
|
+
}
|
|
167
|
+
else if (state === 'unsupported' || state === 'unauthorized') {
|
|
168
|
+
noble.removeListener('stateChange', onStateChange);
|
|
169
|
+
reject(new Error(`Bluetooth adapter state is ${state}.`));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
noble.on('stateChange', onStateChange);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function connectAsync(peripheral, timeoutMs, logger) {
|
|
176
|
+
if (peripheral.state === 'connected')
|
|
177
|
+
return Promise.resolve();
|
|
178
|
+
return new Promise((resolve, reject) => {
|
|
179
|
+
let settled = false;
|
|
180
|
+
const startedAt = Date.now();
|
|
181
|
+
const finish = (error) => {
|
|
182
|
+
if (settled)
|
|
183
|
+
return;
|
|
184
|
+
settled = true;
|
|
185
|
+
cleanup();
|
|
186
|
+
const normalized = normalizeConnectError(error);
|
|
187
|
+
logger(`Connect finished after ${Date.now() - startedAt}ms with ${normalized ? `error: ${normalized.message}` : 'success'}.`);
|
|
188
|
+
if (normalized)
|
|
189
|
+
reject(normalized);
|
|
190
|
+
else
|
|
191
|
+
resolve();
|
|
192
|
+
};
|
|
193
|
+
const timeout = setTimeout(() => {
|
|
194
|
+
cancelPendingConnect(peripheral, logger);
|
|
195
|
+
finish(new Error(`Timed out after ${timeoutMs}ms while connecting.`));
|
|
196
|
+
}, timeoutMs);
|
|
197
|
+
const cleanup = () => {
|
|
198
|
+
clearTimeout(timeout);
|
|
199
|
+
peripheral.removeListener('connect', onConnect);
|
|
200
|
+
peripheral.removeListener('disconnect', onDisconnect);
|
|
201
|
+
};
|
|
202
|
+
const onConnect = (error) => finish(error);
|
|
203
|
+
const onDisconnect = () => finish(new Error('Peripheral disconnected before connect completed.'));
|
|
204
|
+
peripheral.once('connect', onConnect);
|
|
205
|
+
peripheral.once('disconnect', onDisconnect);
|
|
206
|
+
peripheral.connect((error) => finish(error));
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function cancelPendingConnect(peripheral, logger) {
|
|
210
|
+
if (peripheral.state !== 'connecting')
|
|
211
|
+
return;
|
|
212
|
+
try {
|
|
213
|
+
if (typeof peripheral.cancelConnect === 'function') {
|
|
214
|
+
peripheral.cancelConnect();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const noble = loadNoble();
|
|
218
|
+
if (typeof noble._bindings?.disconnect === 'function')
|
|
219
|
+
noble._bindings.disconnect(peripheral.id);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
logger(`Could not cancel pending connection: ${error instanceof Error ? error.message : String(error)}.`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function discoverCharacteristics(peripheral, timeoutMs) {
|
|
226
|
+
return new Promise((resolve, reject) => {
|
|
227
|
+
const timeout = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs}ms discovering BLE GATT services.`)), timeoutMs);
|
|
228
|
+
peripheral.discoverAllServicesAndCharacteristics((error, _services, characteristics) => {
|
|
229
|
+
clearTimeout(timeout);
|
|
230
|
+
if (error)
|
|
231
|
+
reject(error);
|
|
232
|
+
else
|
|
233
|
+
resolve(characteristics);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function wrapCharacteristic(characteristic) {
|
|
238
|
+
return {
|
|
239
|
+
uuid: normalizeUuid(characteristic.uuid),
|
|
240
|
+
properties: characteristic.properties || [],
|
|
241
|
+
write(data, withoutResponse = false) {
|
|
242
|
+
return new Promise((resolve, reject) => {
|
|
243
|
+
characteristic.write(data, withoutResponse, (error) => (error ? reject(error) : resolve()));
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
subscribe() {
|
|
247
|
+
return new Promise((resolve, reject) => {
|
|
248
|
+
characteristic.subscribe((error) => (error ? reject(error) : resolve()));
|
|
249
|
+
});
|
|
250
|
+
},
|
|
251
|
+
onData(listener) {
|
|
252
|
+
characteristic.on('data', listener);
|
|
253
|
+
},
|
|
254
|
+
removeDataListener(listener) {
|
|
255
|
+
characteristic.removeListener('data', listener);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function stopScanningAndWait(noble) {
|
|
260
|
+
return new Promise((resolve) => {
|
|
261
|
+
let settled = false;
|
|
262
|
+
const finish = () => {
|
|
263
|
+
if (settled)
|
|
264
|
+
return;
|
|
265
|
+
settled = true;
|
|
266
|
+
clearTimeout(timeout);
|
|
267
|
+
noble.removeListener('scanStop', finish);
|
|
268
|
+
resolve();
|
|
269
|
+
};
|
|
270
|
+
const timeout = setTimeout(finish, STOP_SCAN_TIMEOUT_MS);
|
|
271
|
+
noble.once('scanStop', finish);
|
|
272
|
+
noble.stopScanning();
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async function disconnectQuietly(peripheral) {
|
|
276
|
+
if (!peripheral || peripheral.state === 'disconnected')
|
|
277
|
+
return;
|
|
278
|
+
await new Promise((resolve) => {
|
|
279
|
+
let settled = false;
|
|
280
|
+
const finish = () => {
|
|
281
|
+
if (settled)
|
|
282
|
+
return;
|
|
283
|
+
settled = true;
|
|
284
|
+
clearTimeout(timeout);
|
|
285
|
+
peripheral.removeListener('disconnect', finish);
|
|
286
|
+
resolve();
|
|
287
|
+
};
|
|
288
|
+
const timeout = setTimeout(finish, DISCONNECT_TIMEOUT_MS);
|
|
289
|
+
peripheral.once('disconnect', finish);
|
|
290
|
+
peripheral.disconnect(() => finish());
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async function shutdownNoble() {
|
|
294
|
+
const noble = nobleModule;
|
|
295
|
+
if (!noble)
|
|
296
|
+
return;
|
|
297
|
+
noble.removeAllListeners('discover');
|
|
298
|
+
await stopScanningAndWait(noble);
|
|
299
|
+
await disconnectQuietly(activePeripheral);
|
|
300
|
+
activePeripheral = null;
|
|
301
|
+
discoveredPeripherals.clear();
|
|
302
|
+
}
|
|
303
|
+
function toDiscoveredDevice(peripheral) {
|
|
304
|
+
return {
|
|
305
|
+
id: peripheral.id,
|
|
306
|
+
address: peripheral.address || null,
|
|
307
|
+
addressType: peripheral.addressType || null,
|
|
308
|
+
name: peripheral.advertisement?.localName || null,
|
|
309
|
+
rssi: typeof peripheral.rssi === 'number' ? peripheral.rssi : null,
|
|
310
|
+
backend: 'noble'
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function displayPeripheral(peripheral) {
|
|
314
|
+
const name = peripheral.advertisement?.localName || '<unnamed>';
|
|
315
|
+
return `${name} (${peripheral.address || peripheral.id}, ${peripheral.addressType || 'unknown'}, rssi=${peripheral.rssi ?? 'unknown'})`;
|
|
316
|
+
}
|
|
317
|
+
function normalizeConnectError(error) {
|
|
318
|
+
if (!error)
|
|
319
|
+
return null;
|
|
320
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
321
|
+
}
|
|
322
|
+
function loadNoble() {
|
|
323
|
+
if (!nobleModule)
|
|
324
|
+
nobleModule = requireOptional('@abandonware/noble');
|
|
325
|
+
return nobleModule;
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=noble.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noble.js","sourceRoot":"","sources":["../../src/ble/noble.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BleDiscoveredDevice } from './backend.js';
|
|
2
|
+
export declare function normalizeUuid(uuid: string | null | undefined): string;
|
|
3
|
+
export declare function formatCanonicalUuid(uuid: string): string;
|
|
4
|
+
export declare function matchesBleDevice(candidate: {
|
|
5
|
+
name?: string | null;
|
|
6
|
+
address?: string | null;
|
|
7
|
+
serviceUuids?: string[] | null;
|
|
8
|
+
}, { namePrefix, deviceName, serviceUuid }?: {
|
|
9
|
+
namePrefix?: string;
|
|
10
|
+
deviceName?: string | null;
|
|
11
|
+
serviceUuid?: string | null;
|
|
12
|
+
}): boolean;
|
|
13
|
+
export declare function uniqueDevices(devices: BleDiscoveredDevice[]): BleDiscoveredDevice[];
|
|
14
|
+
export declare function displayDevice(device: BleDiscoveredDevice): string;
|
|
15
|
+
export declare function nullableString(value: unknown): string | null;
|
|
16
|
+
export declare function nullableNumber(value: unknown): number | null;
|
|
17
|
+
export declare function unboxBluezValue(value: unknown): unknown;
|
|
18
|
+
export declare function errorMessage(error: unknown): string;
|
|
19
|
+
export declare function asError(error: unknown): Error;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DEFAULTS } from '../constants.js';
|
|
2
|
+
export function normalizeUuid(uuid) {
|
|
3
|
+
const compact = String(uuid || '').toLowerCase().replaceAll('-', '');
|
|
4
|
+
if (compact.length === 32 && compact.startsWith('0000') && compact.endsWith('00001000800000805f9b34fb'))
|
|
5
|
+
return compact.slice(4, 8);
|
|
6
|
+
return compact;
|
|
7
|
+
}
|
|
8
|
+
export function formatCanonicalUuid(uuid) {
|
|
9
|
+
const compact = normalizeUuid(uuid);
|
|
10
|
+
if (compact.length === 4)
|
|
11
|
+
return `0000${compact}-0000-1000-8000-00805f9b34fb`;
|
|
12
|
+
if (compact.length !== 32)
|
|
13
|
+
return uuid.toLowerCase();
|
|
14
|
+
return `${compact.slice(0, 8)}-${compact.slice(8, 12)}-${compact.slice(12, 16)}-${compact.slice(16, 20)}-${compact.slice(20)}`;
|
|
15
|
+
}
|
|
16
|
+
export function matchesBleDevice(candidate, { namePrefix = DEFAULTS.namePrefix, deviceName = null, serviceUuid = null } = {}) {
|
|
17
|
+
const name = candidate.name || '';
|
|
18
|
+
if (deviceName)
|
|
19
|
+
return name === deviceName;
|
|
20
|
+
if (namePrefix && name.startsWith(namePrefix))
|
|
21
|
+
return true;
|
|
22
|
+
if (!serviceUuid)
|
|
23
|
+
return !namePrefix;
|
|
24
|
+
const expectedServiceUuid = normalizeUuid(serviceUuid);
|
|
25
|
+
return (candidate.serviceUuids || []).some((uuid) => normalizeUuid(uuid) === expectedServiceUuid);
|
|
26
|
+
}
|
|
27
|
+
export function uniqueDevices(devices) {
|
|
28
|
+
const byKey = new Map();
|
|
29
|
+
for (const device of devices) {
|
|
30
|
+
const key = device.name || device.address || `${device.backend}:${device.id}`;
|
|
31
|
+
const existing = byKey.get(key);
|
|
32
|
+
if (!existing || (device.rssi ?? -999) > (existing.rssi ?? -999))
|
|
33
|
+
byKey.set(key, device);
|
|
34
|
+
}
|
|
35
|
+
return [...byKey.values()].sort((left, right) => displayDevice(left).localeCompare(displayDevice(right)));
|
|
36
|
+
}
|
|
37
|
+
export function displayDevice(device) {
|
|
38
|
+
return device.name || device.address || device.id;
|
|
39
|
+
}
|
|
40
|
+
export function nullableString(value) {
|
|
41
|
+
return typeof value === 'string' && value ? value : null;
|
|
42
|
+
}
|
|
43
|
+
export function nullableNumber(value) {
|
|
44
|
+
return typeof value === 'number' ? value : null;
|
|
45
|
+
}
|
|
46
|
+
export function unboxBluezValue(value) {
|
|
47
|
+
if (value && typeof value === 'object' && 'value' in value)
|
|
48
|
+
return value.value;
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
export function errorMessage(error) {
|
|
52
|
+
return error instanceof Error ? error.message : String(error);
|
|
53
|
+
}
|
|
54
|
+
export function asError(error) {
|
|
55
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/ble/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type BluetoothBackendName, type ReadOptions } from './index.js';
|
|
2
|
+
interface GlobalCliOptions {
|
|
3
|
+
bluetooth: BluetoothBackendName;
|
|
4
|
+
namePrefix: string;
|
|
5
|
+
notifyUuid: string;
|
|
6
|
+
serviceUuid?: string;
|
|
7
|
+
listenMs: string;
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function main(argv?: string[]): Promise<void>;
|
|
11
|
+
export declare function parseIntervalMs(value: string | undefined): number | null;
|
|
12
|
+
export declare function parseListenMs(value: string | undefined): number;
|
|
13
|
+
export declare function toReadOptions(globals: GlobalCliOptions, deviceName?: string): ReadOptions;
|
|
14
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { DEFAULTS, connectReaders, discoverDevices, readDevices, shutdownBluetooth } from './index.js';
|
|
4
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
5
|
+
installSignalCleanup();
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('ble-reader')
|
|
9
|
+
.description('Discover BLE devices and read raw notification messages.')
|
|
10
|
+
.showHelpAfterError();
|
|
11
|
+
addSharedOptions(program);
|
|
12
|
+
addSharedOptions(program
|
|
13
|
+
.command('discover')
|
|
14
|
+
.description('Discover BLE devices.')).action(async (options, command) => {
|
|
15
|
+
const globals = globalOptions(command, options);
|
|
16
|
+
const result = await discoverDevices({
|
|
17
|
+
bluetooth: globals.bluetooth,
|
|
18
|
+
namePrefix: globals.namePrefix,
|
|
19
|
+
scanServiceUuid: globals.serviceUuid || null,
|
|
20
|
+
matchServiceUuid: globals.serviceUuid || null,
|
|
21
|
+
logger: loggerFor(globals)
|
|
22
|
+
});
|
|
23
|
+
await writeJson(result);
|
|
24
|
+
});
|
|
25
|
+
addSharedOptions(program
|
|
26
|
+
.command('read')
|
|
27
|
+
.description('Read raw notifications from one exact advertised device name, or all discovered matching devices.')
|
|
28
|
+
.argument('[deviceName]', 'exact advertised BLE device name')
|
|
29
|
+
.option('--interval <seconds>', 'read repeatedly every N seconds using persistent BLE sessions')).action(async (deviceName, options, command) => {
|
|
30
|
+
const globals = globalOptions(command, options);
|
|
31
|
+
const intervalMs = parseIntervalMs(options.interval);
|
|
32
|
+
if (intervalMs === null) {
|
|
33
|
+
const result = await readDevices(toReadOptions(globals, deviceName));
|
|
34
|
+
await writeJson(result);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await readAtInterval(globals, deviceName, intervalMs);
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
await program.parseAsync(argv, { from: 'user' });
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await shutdownBluetooth();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function parseIntervalMs(value) {
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
return null;
|
|
49
|
+
const seconds = Number(value);
|
|
50
|
+
if (!Number.isFinite(seconds) || seconds <= 0)
|
|
51
|
+
throw new Error('--interval must be a number greater than 0 seconds.');
|
|
52
|
+
return seconds * 1000;
|
|
53
|
+
}
|
|
54
|
+
export function parseListenMs(value) {
|
|
55
|
+
const ms = Number(value ?? DEFAULTS.listenMs);
|
|
56
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
57
|
+
throw new Error('--listen-ms must be a number greater than or equal to 0.');
|
|
58
|
+
return ms;
|
|
59
|
+
}
|
|
60
|
+
export function toReadOptions(globals, deviceName) {
|
|
61
|
+
return {
|
|
62
|
+
bluetooth: globals.bluetooth,
|
|
63
|
+
namePrefix: globals.namePrefix,
|
|
64
|
+
deviceName: deviceName || undefined,
|
|
65
|
+
notifyUuid: globals.notifyUuid,
|
|
66
|
+
listenMs: parseListenMs(globals.listenMs),
|
|
67
|
+
scanServiceUuid: globals.serviceUuid || null,
|
|
68
|
+
matchServiceUuid: globals.serviceUuid || null,
|
|
69
|
+
logger: loggerFor(globals)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function addSharedOptions(command) {
|
|
73
|
+
return command
|
|
74
|
+
.addOption(new Option('--bluetooth <backend>', 'Bluetooth backend').choices(['auto', 'bluez', 'noble']).default('auto'))
|
|
75
|
+
.option('--name-prefix <prefix>', 'BLE advertised name prefix; empty matches all devices', DEFAULTS.namePrefix)
|
|
76
|
+
.option('--service-uuid <uuid>', 'optional BLE service UUID used for discovery filtering')
|
|
77
|
+
.option('--notify-uuid <uuid>', 'notification characteristic UUID to subscribe to', DEFAULTS.notifyUuid)
|
|
78
|
+
.option('--listen-ms <ms>', 'milliseconds to collect notifications per read', String(DEFAULTS.listenMs))
|
|
79
|
+
.option('--debug', 'print Bluetooth diagnostics to stderr');
|
|
80
|
+
}
|
|
81
|
+
function globalOptions(command, options) {
|
|
82
|
+
return {
|
|
83
|
+
bluetooth: optionValue(command, options, 'bluetooth', 'auto'),
|
|
84
|
+
namePrefix: optionValue(command, options, 'namePrefix', DEFAULTS.namePrefix),
|
|
85
|
+
serviceUuid: optionValue(command, options, 'serviceUuid', ''),
|
|
86
|
+
notifyUuid: optionValue(command, options, 'notifyUuid', DEFAULTS.notifyUuid),
|
|
87
|
+
listenMs: optionValue(command, options, 'listenMs', String(DEFAULTS.listenMs)),
|
|
88
|
+
debug: optionValue(command, options, 'debug', false)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function optionValue(command, options, name, fallback) {
|
|
92
|
+
const parentOptions = command.parent?.opts() || {};
|
|
93
|
+
const localSource = command.getOptionValueSource(name);
|
|
94
|
+
if (localSource === 'default' && parentOptions[name] !== undefined)
|
|
95
|
+
return parentOptions[name];
|
|
96
|
+
return (options[name] ?? fallback);
|
|
97
|
+
}
|
|
98
|
+
function loggerFor({ debug }) {
|
|
99
|
+
return debug ? (message) => process.stderr.write(`[ble-reader-debug] ${message}\n`) : () => { };
|
|
100
|
+
}
|
|
101
|
+
async function writeJson(value) {
|
|
102
|
+
await new Promise((resolve, reject) => {
|
|
103
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`, (error) => (error ? reject(error) : resolve()));
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function readAtInterval(globals, deviceName, intervalMs) {
|
|
107
|
+
const readers = await connectReaders(toReadOptions(globals, deviceName));
|
|
108
|
+
if (readers.length === 0) {
|
|
109
|
+
await writeJson([]);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
while (true) {
|
|
114
|
+
await writeJson(await readPersistentReaders(readers, globals));
|
|
115
|
+
await delay(intervalMs);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await Promise.all(readers.map((reader) => reader.disconnect().catch(() => { })));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function readPersistentReaders(readers, globals) {
|
|
123
|
+
const readings = [];
|
|
124
|
+
for (const reader of readers)
|
|
125
|
+
readings.push(await reader.read(toReadOptions(globals)));
|
|
126
|
+
return readings;
|
|
127
|
+
}
|
|
128
|
+
function installSignalCleanup() {
|
|
129
|
+
let shuttingDown = false;
|
|
130
|
+
const handler = async () => {
|
|
131
|
+
if (shuttingDown)
|
|
132
|
+
return;
|
|
133
|
+
shuttingDown = true;
|
|
134
|
+
await shutdownBluetooth();
|
|
135
|
+
process.exit(130);
|
|
136
|
+
};
|
|
137
|
+
process.once('SIGINT', handler);
|
|
138
|
+
process.once('SIGTERM', handler);
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,KAAK;IAChB,gBAAgB,EAAE,KAAK;IACvB,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,EAAE;CACN,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { DEFAULTS } from './constants.js';
|
|
2
|
+
export { connectDevice, connectReaders, discoverDevices, readDevice, readDevices, readSession, shutdownBluetooth, type BleReader } from './reader.js';
|
|
3
|
+
export { matchesBleDevice } from './ble/utils.js';
|
|
4
|
+
export type { BleDiscoveredDevice, BleReading, BleSession, BluetoothBackendName, DiscoverOptions, RawBleMessage, ReadOptions } from './ble/backend.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAkB,MAAM,aAAa,CAAC;AACtJ,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/reader.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BleDiscoveredDevice, BleReading, BleSession, DiscoverOptions, ReadOptions } from './ble/backend.js';
|
|
2
|
+
export interface BleReader {
|
|
3
|
+
device: BleDiscoveredDevice;
|
|
4
|
+
read(options?: ReadOptions): Promise<BleReading>;
|
|
5
|
+
disconnect(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare function discoverDevices(options?: DiscoverOptions): Promise<BleDiscoveredDevice[]>;
|
|
8
|
+
export declare function readDevices(options?: ReadOptions): Promise<BleReading[]>;
|
|
9
|
+
export declare function readDevice(device: BleDiscoveredDevice, options?: ReadOptions): Promise<BleReading>;
|
|
10
|
+
export declare function connectReaders(options?: ReadOptions): Promise<BleReader[]>;
|
|
11
|
+
export declare function connectDevice(device: BleDiscoveredDevice, options?: ReadOptions): Promise<BleReader>;
|
|
12
|
+
export declare function readSession(session: BleSession, options?: ReadOptions): Promise<BleReading>;
|
|
13
|
+
export declare function shutdownBluetooth(): Promise<void>;
|