@matter-server/ble-proxy 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.
Files changed (50) hide show
  1. package/README.md +116 -0
  2. package/dist/esm/BleProxyHandler.d.ts +39 -0
  3. package/dist/esm/BleProxyHandler.d.ts.map +1 -0
  4. package/dist/esm/BleProxyHandler.js +256 -0
  5. package/dist/esm/BleProxyHandler.js.map +6 -0
  6. package/dist/esm/BleProxyProtocol.d.ts +230 -0
  7. package/dist/esm/BleProxyProtocol.d.ts.map +1 -0
  8. package/dist/esm/BleProxyProtocol.js +82 -0
  9. package/dist/esm/BleProxyProtocol.js.map +6 -0
  10. package/dist/esm/ProxyBle.d.ts +24 -0
  11. package/dist/esm/ProxyBle.d.ts.map +1 -0
  12. package/dist/esm/ProxyBle.js +50 -0
  13. package/dist/esm/ProxyBle.js.map +6 -0
  14. package/dist/esm/ProxyBleChannel.d.ts +38 -0
  15. package/dist/esm/ProxyBleChannel.d.ts.map +1 -0
  16. package/dist/esm/ProxyBleChannel.js +314 -0
  17. package/dist/esm/ProxyBleChannel.js.map +6 -0
  18. package/dist/esm/ProxyBleClient.d.ts +32 -0
  19. package/dist/esm/ProxyBleClient.d.ts.map +1 -0
  20. package/dist/esm/ProxyBleClient.js +100 -0
  21. package/dist/esm/ProxyBleClient.js.map +6 -0
  22. package/dist/esm/ProxyBleScanner.d.ts +27 -0
  23. package/dist/esm/ProxyBleScanner.d.ts.map +1 -0
  24. package/dist/esm/ProxyBleScanner.js +23 -0
  25. package/dist/esm/ProxyBleScanner.js.map +6 -0
  26. package/dist/esm/example/NobleBleProxyClient.d.ts +12 -0
  27. package/dist/esm/example/NobleBleProxyClient.d.ts.map +1 -0
  28. package/dist/esm/example/NobleBleProxyClient.js +520 -0
  29. package/dist/esm/example/NobleBleProxyClient.js.map +6 -0
  30. package/dist/esm/example/noble-ble-proxy.d.ts +7 -0
  31. package/dist/esm/example/noble-ble-proxy.d.ts.map +1 -0
  32. package/dist/esm/example/noble-ble-proxy.js +45 -0
  33. package/dist/esm/example/noble-ble-proxy.js.map +6 -0
  34. package/dist/esm/index.d.ts +15 -0
  35. package/dist/esm/index.d.ts.map +1 -0
  36. package/dist/esm/index.js +12 -0
  37. package/dist/esm/index.js.map +6 -0
  38. package/dist/esm/package.json +3 -0
  39. package/package.json +46 -0
  40. package/src/BleProxyHandler.ts +345 -0
  41. package/src/BleProxyProtocol.ts +287 -0
  42. package/src/ProxyBle.ts +64 -0
  43. package/src/ProxyBleChannel.ts +407 -0
  44. package/src/ProxyBleClient.ts +136 -0
  45. package/src/ProxyBleScanner.ts +38 -0
  46. package/src/example/NobleBleProxyClient.ts +679 -0
  47. package/src/example/noble-ble-proxy.ts +62 -0
  48. package/src/index.ts +16 -0
  49. package/src/tsconfig.json +15 -0
  50. package/src/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,520 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { WebSocket } from "ws";
7
+ import {
8
+ BLE_PROXY_PROTOCOL_VERSION,
9
+ BinaryFrameOpcode,
10
+ decodeBinaryFrame,
11
+ encodeBinaryFrame
12
+ } from "../BleProxyProtocol.js";
13
+ function withTimeout(promise, ms, message) {
14
+ return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms))]);
15
+ }
16
+ function ts() {
17
+ const d = /* @__PURE__ */ new Date();
18
+ const pad = (n, w = 2) => n.toString().padStart(w, "0");
19
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`;
20
+ }
21
+ function log(...args) {
22
+ process.stdout.write(`${ts()} `);
23
+ console.log(...args);
24
+ }
25
+ function warn(...args) {
26
+ process.stdout.write(`${ts()} `);
27
+ console.warn(...args);
28
+ }
29
+ function error(...args) {
30
+ process.stdout.write(`${ts()} `);
31
+ console.error(...args);
32
+ }
33
+ class NobleBleProxyClient {
34
+ #serverUrl;
35
+ #hciId;
36
+ #ws;
37
+ #noble;
38
+ #connections = /* @__PURE__ */ new Map();
39
+ #nextHandle = 1;
40
+ #discoveredPeripherals = /* @__PURE__ */ new Map();
41
+ #lastDiscoverFingerprint = /* @__PURE__ */ new Map();
42
+ #commandHandlers = /* @__PURE__ */ new Map();
43
+ #closing = false;
44
+ constructor(serverUrl, hciId) {
45
+ this.#serverUrl = serverUrl;
46
+ this.#hciId = hciId;
47
+ this.#registerCommandHandlers();
48
+ }
49
+ async connect() {
50
+ await this.#loadNoble();
51
+ return new Promise((resolve, reject) => {
52
+ const ws = new WebSocket(this.#serverUrl);
53
+ this.#ws = ws;
54
+ ws.on("open", () => {
55
+ ws.send(JSON.stringify({ type: "hello", version: BLE_PROXY_PROTOCOL_VERSION }));
56
+ });
57
+ let handshakeComplete = false;
58
+ ws.on("message", (data, isBinary) => {
59
+ if (isBinary) {
60
+ this.#handleBinaryFrame(Buffer.from(data));
61
+ return;
62
+ }
63
+ const msg = JSON.parse(data.toString());
64
+ if (!handshakeComplete) {
65
+ if (msg.type === "hello_response") {
66
+ if (msg.error) {
67
+ reject(new Error(`Handshake failed: ${msg.error} - ${msg.message}`));
68
+ ws.close();
69
+ return;
70
+ }
71
+ handshakeComplete = true;
72
+ log(`BLE proxy handshake complete (protocol v${msg.version})`);
73
+ resolve();
74
+ }
75
+ return;
76
+ }
77
+ if ("id" in msg && "command" in msg) {
78
+ this.#handleCommand(msg).catch(
79
+ (err) => error(`Unhandled error in command handler: ${err.message}`)
80
+ );
81
+ }
82
+ });
83
+ ws.on("close", (code, reason) => {
84
+ if (!this.#closing) {
85
+ log(`Disconnected from server (code=${code}${reason.length ? `, reason=${reason}` : ""})`);
86
+ }
87
+ });
88
+ ws.on("unexpected-response", (_req, res) => {
89
+ let body = "";
90
+ res.on("data", (chunk) => body += chunk.toString());
91
+ res.on("end", () => {
92
+ const err = new Error(`Unexpected server response: ${res.statusCode}`);
93
+ error(`Server rejected WebSocket upgrade: HTTP ${res.statusCode}`, body ? `- Body: ${body}` : "");
94
+ error("Hint: Make sure the server is running with --ble-proxy (server must expose /ble endpoint)");
95
+ reject(err);
96
+ });
97
+ });
98
+ ws.on("error", (err) => {
99
+ if (!handshakeComplete) {
100
+ reject(err);
101
+ } else {
102
+ error("WebSocket error:", err);
103
+ }
104
+ });
105
+ });
106
+ }
107
+ close() {
108
+ this.#closing = true;
109
+ for (const [handle, conn] of this.#connections) {
110
+ if (conn.peripheral.state === "connected") {
111
+ conn.peripheral.disconnectAsync().catch(
112
+ (err) => warn(`[CONN] handle=${handle} disconnect during shutdown failed: ${err.message}`)
113
+ );
114
+ }
115
+ }
116
+ this.#connections.clear();
117
+ this.#noble?.stop();
118
+ this.#ws?.close();
119
+ }
120
+ async #loadNoble() {
121
+ if (this.#hciId !== void 0) {
122
+ process.env.NOBLE_HCI_DEVICE_ID = this.#hciId.toString();
123
+ }
124
+ const noble = (await import("@stoprocent/noble")).default;
125
+ const nobleAsUnknown = noble;
126
+ if (typeof nobleAsUnknown === "function") {
127
+ const factory = nobleAsUnknown;
128
+ this.#noble = factory({ extended: false });
129
+ } else {
130
+ this.#noble = noble;
131
+ }
132
+ this.#noble.on("warning", (message) => warn(`[NOBLE] warning: ${message}`));
133
+ this.#noble.on("stateChange", (state) => log(`[NOBLE] stateChange: ${state}`));
134
+ }
135
+ #registerCommandHandlers() {
136
+ this.#commandHandlers.set("start_scan", (id, _args) => this.#handleStartScan(id));
137
+ this.#commandHandlers.set("stop_scan", (id) => this.#handleStopScan(id));
138
+ this.#commandHandlers.set("connect", (id, args) => this.#handleConnect(id, args));
139
+ this.#commandHandlers.set(
140
+ "disconnect",
141
+ (id, args) => this.#handleDisconnect(id, args.connection_handle)
142
+ );
143
+ this.#commandHandlers.set(
144
+ "discover_services",
145
+ (id, args) => this.#handleDiscoverServices(id, args)
146
+ );
147
+ this.#commandHandlers.set(
148
+ "discover_characteristics",
149
+ (id, args) => this.#handleDiscoverCharacteristics(id, args)
150
+ );
151
+ this.#commandHandlers.set(
152
+ "read_characteristic",
153
+ (id, args) => this.#handleReadCharacteristic(id, args)
154
+ );
155
+ this.#commandHandlers.set(
156
+ "write_characteristic",
157
+ (id, args) => this.#handleWriteCharacteristic(id, args)
158
+ );
159
+ this.#commandHandlers.set(
160
+ "subscribe_characteristic",
161
+ (id, args) => this.#handleSubscribeCharacteristic(id, args)
162
+ );
163
+ this.#commandHandlers.set(
164
+ "unsubscribe_characteristic",
165
+ (id, args) => this.#handleUnsubscribeCharacteristic(id, args)
166
+ );
167
+ this.#commandHandlers.set(
168
+ "request_mtu",
169
+ (id, args) => this.#handleRequestMtu(
170
+ id,
171
+ args.connection_handle,
172
+ args.mtu
173
+ )
174
+ );
175
+ }
176
+ async #handleCommand(msg) {
177
+ const argsStr = msg.args ? JSON.stringify(msg.args) : "";
178
+ log(`[\u2190CMD] id=${msg.id} ${msg.command}${argsStr ? ` ${argsStr}` : ""}`);
179
+ const handler = this.#commandHandlers.get(msg.command);
180
+ if (!handler) {
181
+ error(`[\u2192ERR] id=${msg.id} Unknown command: ${msg.command}`);
182
+ this.#sendError(msg.id, "internal_error", `Unknown command: ${msg.command}`);
183
+ return;
184
+ }
185
+ try {
186
+ await handler(msg.id, msg.args ?? {});
187
+ } catch (err) {
188
+ error(`[\u2192ERR] id=${msg.id} ${msg.command} threw: ${err.message}`);
189
+ this.#sendError(msg.id, "internal_error", `${err.message}`);
190
+ }
191
+ }
192
+ // ─── Command Handlers ────────────────────────────────────────────────────
193
+ async #handleStartScan(id) {
194
+ if (!this.#noble) {
195
+ this.#sendError(id, "bluetooth_unavailable", "Noble not initialized");
196
+ return;
197
+ }
198
+ this.#lastDiscoverFingerprint.clear();
199
+ this.#noble.removeAllListeners("discover");
200
+ this.#noble.on("discover", (peripheral) => {
201
+ const address = peripheral.address || peripheral.id;
202
+ this.#discoveredPeripherals.set(address, peripheral);
203
+ const serviceData = {};
204
+ for (const sd of peripheral.advertisement.serviceData ?? []) {
205
+ serviceData[sd.uuid] = Buffer.from(sd.data).toString("base64");
206
+ }
207
+ const name = peripheral.advertisement.localName ?? "(unnamed)";
208
+ const connectable = peripheral.connectable ?? false;
209
+ const serviceUuids = peripheral.advertisement.serviceUuids ?? [];
210
+ const fingerprint = {
211
+ name,
212
+ connectable,
213
+ serviceUuids: serviceUuids.join(","),
214
+ serviceData: Object.entries(serviceData).map(([uuid, data]) => `${uuid}=${data}`).sort().join("|")
215
+ };
216
+ const prev = this.#lastDiscoverFingerprint.get(address);
217
+ const changed = !prev || prev.name !== fingerprint.name || prev.connectable !== fingerprint.connectable || prev.serviceUuids !== fingerprint.serviceUuids || prev.serviceData !== fingerprint.serviceData;
218
+ if (!changed) {
219
+ return;
220
+ }
221
+ this.#lastDiscoverFingerprint.set(address, fingerprint);
222
+ log(
223
+ `[EVT] device_discovered addr=${address} name="${name}" rssi=${peripheral.rssi} services=${JSON.stringify(serviceUuids)} serviceData=${JSON.stringify(Object.keys(serviceData))}`
224
+ );
225
+ const event = {
226
+ address,
227
+ name: peripheral.advertisement.localName,
228
+ rssi: peripheral.rssi,
229
+ connectable,
230
+ service_data: serviceData,
231
+ service_uuids: serviceUuids
232
+ };
233
+ this.#sendEvent("device_discovered", event);
234
+ });
235
+ await this.#noble.startScanningAsync(["fff6"], true);
236
+ log("[SCAN] BLE scan started (filter: fff6)");
237
+ this.#sendSuccess(id);
238
+ }
239
+ async #handleStopScan(id) {
240
+ await this.#noble?.stopScanningAsync();
241
+ log("[SCAN] BLE scan stopped");
242
+ this.#sendSuccess(id);
243
+ }
244
+ async #handleConnect(id, args) {
245
+ const peripheral = this.#discoveredPeripherals.get(args.address);
246
+ if (!peripheral) {
247
+ error(
248
+ `[CONN] No peripheral found for address "${args.address}". Known: ${[...this.#discoveredPeripherals.keys()].join(", ")}`
249
+ );
250
+ this.#sendError(id, "device_not_found", `No device found for address ${args.address}`);
251
+ return;
252
+ }
253
+ const handle = this.#nextHandle++;
254
+ const connState = {
255
+ peripheral,
256
+ services: /* @__PURE__ */ new Map(),
257
+ characteristics: /* @__PURE__ */ new Map(),
258
+ subscriptions: /* @__PURE__ */ new Map()
259
+ };
260
+ this.#connections.set(handle, connState);
261
+ let disconnectedReason;
262
+ const disconnectListener = () => {
263
+ disconnectedReason = `peripheral disconnected (state=${peripheral.state})`;
264
+ log(`[CONN] Peripheral handle=${handle} disconnected (state=${peripheral.state})`);
265
+ this.#connections.delete(handle);
266
+ this.#sendEvent("disconnected", { connection_handle: handle });
267
+ };
268
+ peripheral.once("disconnect", disconnectListener);
269
+ log(`[CONN] Connecting to "${args.address}" (state=${peripheral.state})...`);
270
+ try {
271
+ log(`[SCAN] pausing scan for connect+interview...`);
272
+ await this.#noble.stopScanningAsync();
273
+ await peripheral.connectAsync();
274
+ log(`[CONN] Connected handle=${handle} state=${peripheral.state} mtu=${peripheral.mtu ?? "?"}`);
275
+ log(`[GATT] handle=${handle} discoverServicesAsync(["fff6"])...`);
276
+ const services = await withTimeout(
277
+ peripheral.discoverServicesAsync(["fff6"]),
278
+ 3e4,
279
+ "discoverServices(fff6) timed out after 30s"
280
+ );
281
+ log(`[GATT] handle=${handle} services: ${services.map((s) => s.uuid).join(", ")} state=${peripheral.state}`);
282
+ for (const service of services) {
283
+ connState.services.set(service.uuid, service);
284
+ if (service.uuid !== "fff6") continue;
285
+ log(`[GATT] handle=${handle} discoverCharacteristicsAsync() on ${service.uuid}...`);
286
+ const chars = await withTimeout(
287
+ service.discoverCharacteristicsAsync(),
288
+ 3e4,
289
+ `discoverCharacteristics(${service.uuid}) timed out after 30s`
290
+ );
291
+ for (const char of chars) {
292
+ connState.characteristics.set(char.uuid, char);
293
+ }
294
+ log(
295
+ `[GATT] handle=${handle} chars on ${service.uuid}: ${chars.map((c) => c.uuid).join(", ")} state=${peripheral.state}`
296
+ );
297
+ }
298
+ const mtu = peripheral.mtu ?? 23;
299
+ log(`[GATT] handle=${handle} ready mtu=${mtu}`);
300
+ log(`[SCAN] resuming scan after connect+interview...`);
301
+ await this.#noble.startScanningAsync(["fff6"], true);
302
+ this.#sendSuccess(id, { connection_handle: handle, mtu });
303
+ } catch (err) {
304
+ const reason = disconnectedReason ?? err.message;
305
+ error(`[CONN] handle=${handle} failed: ${reason}`);
306
+ if (this.#connections.has(handle)) {
307
+ this.#connections.delete(handle);
308
+ }
309
+ peripheral.removeListener("disconnect", disconnectListener);
310
+ if (peripheral.state === "connected") {
311
+ peripheral.disconnectAsync().catch(
312
+ (disconnectErr) => warn(`[CONN] handle=${handle} cleanup disconnect failed: ${disconnectErr.message}`)
313
+ );
314
+ }
315
+ this.#noble.startScanningAsync(["fff6"], true).catch(
316
+ (scanErr) => warn(`[SCAN] failed to resume scanning after connect failure: ${scanErr.message}`)
317
+ );
318
+ this.#sendError(id, "internal_error", reason);
319
+ }
320
+ }
321
+ async #handleDisconnect(id, connectionHandle) {
322
+ const conn = this.#connections.get(connectionHandle);
323
+ if (!conn) {
324
+ this.#sendError(id, "not_connected", `No connection with handle ${connectionHandle}`);
325
+ return;
326
+ }
327
+ if (conn.peripheral.state === "connected") {
328
+ await conn.peripheral.disconnectAsync();
329
+ }
330
+ this.#connections.delete(connectionHandle);
331
+ this.#sendSuccess(id);
332
+ }
333
+ async #handleDiscoverServices(id, args) {
334
+ const conn = this.#connections.get(args.connection_handle);
335
+ if (!conn) {
336
+ this.#sendError(id, "not_connected", `No connection with handle ${args.connection_handle}`);
337
+ return;
338
+ }
339
+ if (conn.services.size > 0) {
340
+ const uuids = [...conn.services.keys()];
341
+ log(`[GATT] handle=${args.connection_handle} services from cache: ${uuids.join(", ")}`);
342
+ this.#sendSuccess(id, { services: uuids.map((uuid) => ({ uuid })) });
343
+ return;
344
+ }
345
+ log(`[GATT] handle=${args.connection_handle} discovering services (lazy)...`);
346
+ const services = await withTimeout(
347
+ conn.peripheral.discoverServicesAsync([]),
348
+ 1e4,
349
+ "discoverServices timed out after 10s"
350
+ );
351
+ for (const service of services) {
352
+ conn.services.set(service.uuid, service);
353
+ }
354
+ log(`[GATT] handle=${args.connection_handle} discovered services: ${services.map((s) => s.uuid).join(", ")}`);
355
+ this.#sendSuccess(id, {
356
+ services: services.map((s) => ({ uuid: s.uuid }))
357
+ });
358
+ }
359
+ async #handleDiscoverCharacteristics(id, args) {
360
+ const conn = this.#connections.get(args.connection_handle);
361
+ if (!conn) {
362
+ this.#sendError(id, "not_connected", `No connection with handle ${args.connection_handle}`);
363
+ return;
364
+ }
365
+ const service = conn.services.get(args.service_uuid);
366
+ if (!service) {
367
+ this.#sendError(id, "service_not_found", `Service ${args.service_uuid} not found`);
368
+ return;
369
+ }
370
+ const cachedChars = service.characteristics ?? [];
371
+ if (cachedChars.length > 0) {
372
+ log(
373
+ `[GATT] handle=${args.connection_handle} characteristics from cache for ${args.service_uuid}: ` + cachedChars.map((c) => `${c.uuid}[${c.properties.join(",")}]`).join(", ")
374
+ );
375
+ this.#sendSuccess(id, {
376
+ characteristics: cachedChars.map((c) => ({
377
+ uuid: c.uuid,
378
+ properties: c.properties
379
+ }))
380
+ });
381
+ return;
382
+ }
383
+ log(`[GATT] handle=${args.connection_handle} discovering characteristics for ${args.service_uuid} (lazy)...`);
384
+ const characteristics = await withTimeout(
385
+ service.discoverCharacteristicsAsync([]),
386
+ 1e4,
387
+ `discoverCharacteristics(${args.service_uuid}) timed out after 10s`
388
+ );
389
+ for (const char of characteristics) {
390
+ conn.characteristics.set(char.uuid, char);
391
+ }
392
+ log(
393
+ `[GATT] handle=${args.connection_handle} discovered chars for ${args.service_uuid}: ` + characteristics.map((c) => `${c.uuid}[${c.properties.join(",")}]`).join(", ")
394
+ );
395
+ this.#sendSuccess(id, {
396
+ characteristics: characteristics.map((c) => ({
397
+ uuid: c.uuid,
398
+ properties: c.properties
399
+ }))
400
+ });
401
+ }
402
+ async #handleReadCharacteristic(id, args) {
403
+ const conn = this.#connections.get(args.connection_handle);
404
+ if (!conn) {
405
+ this.#sendError(id, "not_connected", `No connection with handle ${args.connection_handle}`);
406
+ return;
407
+ }
408
+ const char = this.#findCharacteristic(conn, args.characteristic_uuid);
409
+ if (!char) {
410
+ this.#sendError(id, "characteristic_not_found", `Characteristic ${args.characteristic_uuid} not found`);
411
+ return;
412
+ }
413
+ const data = await char.readAsync();
414
+ log(`[GATT] read ${args.characteristic_uuid} \u2192 ${data.length} bytes`);
415
+ this.#sendSuccess(id, { value: Buffer.from(data).toString("base64") });
416
+ }
417
+ async #handleWriteCharacteristic(id, args) {
418
+ const conn = this.#connections.get(args.connection_handle);
419
+ if (!conn) {
420
+ this.#sendError(id, "not_connected", `No connection with handle ${args.connection_handle}`);
421
+ return;
422
+ }
423
+ const char = this.#findCharacteristic(conn, args.characteristic_uuid);
424
+ if (!char) {
425
+ this.#sendError(id, "characteristic_not_found", `Characteristic ${args.characteristic_uuid} not found`);
426
+ return;
427
+ }
428
+ const data = Buffer.from(args.value, "base64");
429
+ const withResponse = args.response ?? false;
430
+ log(`[GATT] write ${args.characteristic_uuid} ${data.length} bytes withResponse=${withResponse}`);
431
+ await char.writeAsync(data, !withResponse);
432
+ conn.lastWriteCharacteristic = char;
433
+ this.#sendSuccess(id);
434
+ }
435
+ async #handleSubscribeCharacteristic(id, args) {
436
+ const conn = this.#connections.get(args.connection_handle);
437
+ if (!conn) {
438
+ this.#sendError(id, "not_connected", `No connection with handle ${args.connection_handle}`);
439
+ return;
440
+ }
441
+ const char = this.#findCharacteristic(conn, args.characteristic_uuid);
442
+ if (!char) {
443
+ this.#sendError(id, "characteristic_not_found", `Characteristic ${args.characteristic_uuid} not found`);
444
+ return;
445
+ }
446
+ const handle = args.connection_handle;
447
+ char.on("data", (data) => {
448
+ log(`[GATT] notify ${args.characteristic_uuid} handle=${handle} ${data.length} bytes`);
449
+ this.#sendBinaryFrame(BinaryFrameOpcode.Notification, handle, new Uint8Array(data));
450
+ });
451
+ await char.subscribeAsync();
452
+ conn.subscriptions.set(args.characteristic_uuid, char);
453
+ log(`[GATT] subscribe ${args.characteristic_uuid} handle=${handle}`);
454
+ this.#sendSuccess(id);
455
+ }
456
+ async #handleUnsubscribeCharacteristic(id, args) {
457
+ const conn = this.#connections.get(args.connection_handle);
458
+ if (!conn) {
459
+ this.#sendError(id, "not_connected", `No connection with handle ${args.connection_handle}`);
460
+ return;
461
+ }
462
+ const char = conn.subscriptions.get(args.characteristic_uuid);
463
+ if (!char) {
464
+ this.#sendError(id, "not_subscribed", `Not subscribed to ${args.characteristic_uuid}`);
465
+ return;
466
+ }
467
+ await char.unsubscribeAsync();
468
+ char.removeAllListeners("data");
469
+ conn.subscriptions.delete(args.characteristic_uuid);
470
+ log(`[GATT] unsubscribe ${args.characteristic_uuid} handle=${args.connection_handle}`);
471
+ this.#sendSuccess(id);
472
+ }
473
+ async #handleRequestMtu(id, connectionHandle, mtu) {
474
+ const conn = this.#connections.get(connectionHandle);
475
+ if (!conn) {
476
+ this.#sendError(id, "not_connected", `No connection with handle ${connectionHandle}`);
477
+ return;
478
+ }
479
+ const actualMtu = conn.peripheral.mtu ?? mtu;
480
+ log(`[GATT] request_mtu handle=${connectionHandle} requested=${mtu} actual=${actualMtu}`);
481
+ this.#sendSuccess(id, { mtu: actualMtu });
482
+ }
483
+ // ─── Binary Frame Handling ───────────────────────────────────────────────
484
+ #handleBinaryFrame(data) {
485
+ const frame = decodeBinaryFrame(new Uint8Array(data));
486
+ log(`[\u2190BIN] opcode=${frame.opcode} handle=${frame.connectionHandle} payload=${frame.payload.length} bytes`);
487
+ if (frame.opcode === BinaryFrameOpcode.WriteData) {
488
+ const conn = this.#connections.get(frame.connectionHandle);
489
+ if (!conn?.lastWriteCharacteristic) {
490
+ warn(`[\u2190BIN] WriteData: no lastWriteCharacteristic for handle=${frame.connectionHandle}`);
491
+ return;
492
+ }
493
+ conn.lastWriteCharacteristic.writeAsync(Buffer.from(frame.payload), false).catch((err) => {
494
+ error("Binary write error:", err);
495
+ });
496
+ }
497
+ }
498
+ // ─── Helpers ─────────────────────────────────────────────────────────────
499
+ #findCharacteristic(conn, uuid) {
500
+ return conn.characteristics.get(uuid) ?? conn.characteristics.get(uuid.toLowerCase()) ?? conn.characteristics.get(uuid.toUpperCase().replace(/-/g, "").toLowerCase());
501
+ }
502
+ #sendSuccess(id, result) {
503
+ this.#ws?.send(JSON.stringify({ id, success: true, result: result ?? {} }));
504
+ }
505
+ #sendError(id, error2, message) {
506
+ this.#ws?.send(JSON.stringify({ id, success: false, error: error2, message }));
507
+ }
508
+ #sendEvent(event, data) {
509
+ this.#ws?.send(JSON.stringify({ event, data }));
510
+ }
511
+ #sendBinaryFrame(opcode, connectionHandle, payload) {
512
+ log(`[\u2192BIN] opcode=${opcode} handle=${connectionHandle} payload=${payload.length} bytes`);
513
+ const frame = encodeBinaryFrame(opcode, connectionHandle, payload);
514
+ this.#ws?.send(frame);
515
+ }
516
+ }
517
+ export {
518
+ NobleBleProxyClient
519
+ };
520
+ //# sourceMappingURL=NobleBleProxyClient.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/example/NobleBleProxyClient.ts"],
4
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,SAAS,iBAAiB;AAC1B;AAAA,EACI;AAAA,EACA;AAAA,EAWA;AAAA,EACA;AAAA,OACG;AAEP,SAAS,YAAe,SAAqB,IAAY,SAA6B;AAClF,SAAO,QAAQ,KAAK,CAAC,SAAS,IAAI,QAAe,CAAC,GAAG,WAAW,WAAW,MAAM,OAAO,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACtH;AAEA,SAAS,KAAa;AAClB,QAAM,IAAI,oBAAI,KAAK;AACnB,QAAM,MAAM,CAAC,GAAW,IAAI,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AAC9D,SAAO,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,IAAI,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,IAAI,EAAE,gBAAgB,GAAG,CAAC,CAAC;AAC5G;AAEA,SAAS,OAAO,MAAuB;AACnC,UAAQ,OAAO,MAAM,GAAG,GAAG,CAAC,GAAG;AAC/B,UAAQ,IAAI,GAAG,IAAI;AACvB;AAEA,SAAS,QAAQ,MAAuB;AACpC,UAAQ,OAAO,MAAM,GAAG,GAAG,CAAC,GAAG;AAC/B,UAAQ,KAAK,GAAG,IAAI;AACxB;AAEA,SAAS,SAAS,MAAuB;AACrC,UAAQ,OAAO,MAAM,GAAG,GAAG,CAAC,GAAG;AAC/B,UAAQ,MAAM,GAAG,IAAI;AACzB;AAwBO,MAAM,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA,eAAe,oBAAI,IAA6B;AAAA,EAChD,cAAc;AAAA,EACd,yBAAyB,oBAAI,IAAwB;AAAA,EACrD,2BAA2B,oBAAI,IAAiC;AAAA,EAChE,mBAAmB,oBAAI,IAAyC;AAAA,EAChE,WAAW;AAAA,EAEX,YAAY,WAAmB,OAAgB;AAC3C,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,SAAK,yBAAyB;AAAA,EAClC;AAAA,EAEA,MAAM,UAAyB;AAE3B,UAAM,KAAK,WAAW;AAEtB,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC1C,YAAM,KAAK,IAAI,UAAU,KAAK,UAAU;AACxC,WAAK,MAAM;AAEX,SAAG,GAAG,QAAQ,MAAM;AAEhB,WAAG,KAAK,KAAK,UAAU,EAAE,MAAM,SAAS,SAAS,2BAA2B,CAAC,CAAC;AAAA,MAClF,CAAC;AAED,UAAI,oBAAoB;AAExB,SAAG,GAAG,WAAW,CAAC,MAAM,aAAa;AACjC,YAAI,UAAU;AACV,eAAK,mBAAmB,OAAO,KAAK,IAAmB,CAAC;AACxD;AAAA,QACJ;AAEA,cAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AAEtC,YAAI,CAAC,mBAAmB;AACpB,cAAI,IAAI,SAAS,kBAAkB;AAC/B,gBAAI,IAAI,OAAO;AACX,qBAAO,IAAI,MAAM,qBAAqB,IAAI,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC;AACnE,iBAAG,MAAM;AACT;AAAA,YACJ;AACA,gCAAoB;AACpB,gBAAI,2CAA2C,IAAI,OAAO,GAAG;AAC7D,oBAAQ;AAAA,UACZ;AACA;AAAA,QACJ;AAGA,YAAI,QAAQ,OAAO,aAAa,KAAK;AACjC,eAAK,eAAe,GAAqB,EAAE;AAAA,YAAM,SAC7C,MAAM,uCAAwC,IAAc,OAAO,EAAE;AAAA,UACzE;AAAA,QACJ;AAAA,MACJ,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AAC7B,YAAI,CAAC,KAAK,UAAU;AAChB,cAAI,kCAAkC,IAAI,GAAG,OAAO,SAAS,YAAY,MAAM,KAAK,EAAE,GAAG;AAAA,QAC7F;AAAA,MACJ,CAAC;AAED,SAAG,GAAG,uBAAuB,CAAC,MAAM,QAAQ;AACxC,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAmB,QAAQ,MAAM,SAAS,CAAE;AAC5D,YAAI,GAAG,OAAO,MAAM;AAChB,gBAAM,MAAM,IAAI,MAAM,+BAA+B,IAAI,UAAU,EAAE;AACrE,gBAAM,2CAA2C,IAAI,UAAU,IAAI,OAAO,WAAW,IAAI,KAAK,EAAE;AAChG,gBAAM,2FAA2F;AACjG,iBAAO,GAAG;AAAA,QACd,CAAC;AAAA,MACL,CAAC;AAED,SAAG,GAAG,SAAS,SAAO;AAClB,YAAI,CAAC,mBAAmB;AACpB,iBAAO,GAAG;AAAA,QACd,OAAO;AACH,gBAAM,oBAAoB,GAAG;AAAA,QACjC;AAAA,MACJ,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA,EAEA,QAAc;AACV,SAAK,WAAW;AAEhB,eAAW,CAAC,QAAQ,IAAI,KAAK,KAAK,cAAc;AAC5C,UAAI,KAAK,WAAW,UAAU,aAAa;AACvC,aAAK,WACA,gBAAgB,EAChB;AAAA,UAAM,SACH,KAAK,iBAAiB,MAAM,uCAAwC,IAAc,OAAO,EAAE;AAAA,QAC/F;AAAA,MACR;AAAA,IACJ;AACA,SAAK,aAAa,MAAM;AACxB,SAAK,QAAQ,KAAK;AAClB,SAAK,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,MAAM,aAA4B;AAC9B,QAAI,KAAK,WAAW,QAAW;AAC3B,cAAQ,IAAI,sBAAsB,KAAK,OAAO,SAAS;AAAA,IAC3D;AAGA,UAAM,SAAS,MAAM,OAAO,mBAAmB,GAAG;AAClD,UAAM,iBAA0B;AAChC,QAAI,OAAO,mBAAmB,YAAY;AACtC,YAAM,UAAU;AAChB,WAAK,SAAS,QAAQ,EAAE,UAAU,MAAM,CAAC;AAAA,IAC7C,OAAO;AACH,WAAK,SAAS;AAAA,IAClB;AAIA,SAAK,OAAO,GAAG,WAAW,CAAC,YAAoB,KAAK,oBAAoB,OAAO,EAAE,CAAC;AAClF,SAAK,OAAO,GAAG,eAAe,CAAC,UAAkB,IAAI,wBAAwB,KAAK,EAAE,CAAC;AAAA,EACzF;AAAA,EAEA,2BAAiC;AAC7B,SAAK,iBAAiB,IAAI,cAAc,CAAC,IAAI,UAAU,KAAK,iBAAiB,EAAE,CAAC;AAChF,SAAK,iBAAiB,IAAI,aAAa,QAAM,KAAK,gBAAgB,EAAE,CAAC;AACrE,SAAK,iBAAiB,IAAI,WAAW,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,IAA8B,CAAC;AAC1G,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAAc,CAAC,IAAI,SACzC,KAAK,kBAAkB,IAAK,KAAuC,iBAAiB;AAAA,IACxF;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAAqB,CAAC,IAAI,SAChD,KAAK,wBAAwB,IAAI,IAAuC;AAAA,IAC5E;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAA4B,CAAC,IAAI,SACvD,KAAK,+BAA+B,IAAI,IAA8C;AAAA,IAC1F;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAAuB,CAAC,IAAI,SAClD,KAAK,0BAA0B,IAAI,IAAyC;AAAA,IAChF;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAAwB,CAAC,IAAI,SACnD,KAAK,2BAA2B,IAAI,IAA0C;AAAA,IAClF;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAA4B,CAAC,IAAI,SACvD,KAAK,+BAA+B,IAAI,IAA8C;AAAA,IAC1F;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAA8B,CAAC,IAAI,SACzD,KAAK,iCAAiC,IAAI,IAAgD;AAAA,IAC9F;AACA,SAAK,iBAAiB;AAAA,MAAI;AAAA,MAAe,CAAC,IAAI,SAC1C,KAAK;AAAA,QACD;AAAA,QACC,KAAoD;AAAA,QACpD,KAAyB;AAAA,MAC9B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,eAAe,KAAoC;AACrD,UAAM,UAAU,IAAI,OAAO,KAAK,UAAU,IAAI,IAAI,IAAI;AACtD,QAAI,kBAAa,IAAI,EAAE,IAAI,IAAI,OAAO,GAAG,UAAU,IAAI,OAAO,KAAK,EAAE,EAAE;AACvE,UAAM,UAAU,KAAK,iBAAiB,IAAI,IAAI,OAAO;AACrD,QAAI,CAAC,SAAS;AACV,YAAM,kBAAa,IAAI,EAAE,qBAAqB,IAAI,OAAO,EAAE;AAC3D,WAAK,WAAW,IAAI,IAAI,kBAAkB,oBAAoB,IAAI,OAAO,EAAE;AAC3E;AAAA,IACJ;AACA,QAAI;AACA,YAAM,QAAQ,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC;AAAA,IACxC,SAAS,KAAK;AACV,YAAM,kBAAa,IAAI,EAAE,IAAI,IAAI,OAAO,WAAY,IAAc,OAAO,EAAE;AAC3E,WAAK,WAAW,IAAI,IAAI,kBAAkB,GAAI,IAAc,OAAO,EAAE;AAAA,IACzE;AAAA,EACJ;AAAA;AAAA,EAIA,MAAM,iBAAiB,IAA2B;AAC9C,QAAI,CAAC,KAAK,QAAQ;AACd,WAAK,WAAW,IAAI,yBAAyB,uBAAuB;AACpE;AAAA,IACJ;AAEA,SAAK,yBAAyB,MAAM;AAEpC,SAAK,OAAO,mBAAmB,UAAU;AACzC,SAAK,OAAO,GAAG,YAAY,CAAC,eAA2B;AAEnD,YAAM,UAAU,WAAW,WAAW,WAAW;AACjD,WAAK,uBAAuB,IAAI,SAAS,UAAU;AAEnD,YAAM,cAAsC,CAAC;AAC7C,iBAAW,MAAM,WAAW,cAAc,eAAe,CAAC,GAAG;AACzD,oBAAY,GAAG,IAAI,IAAI,OAAO,KAAK,GAAG,IAAI,EAAE,SAAS,QAAQ;AAAA,MACjE;AAEA,YAAM,OAAO,WAAW,cAAc,aAAa;AACnD,YAAM,cAAc,WAAW,eAAe;AAC9C,YAAM,eAAe,WAAW,cAAc,gBAAgB,CAAC;AAE/D,YAAM,cAAmC;AAAA,QACrC;AAAA,QACA;AAAA,QACA,cAAc,aAAa,KAAK,GAAG;AAAA,QACnC,aAAa,OAAO,QAAQ,WAAW,EAClC,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE,EACvC,KAAK,EACL,KAAK,GAAG;AAAA,MACjB;AAEA,YAAM,OAAO,KAAK,yBAAyB,IAAI,OAAO;AACtD,YAAM,UACF,CAAC,QACD,KAAK,SAAS,YAAY,QAC1B,KAAK,gBAAgB,YAAY,eACjC,KAAK,iBAAiB,YAAY,gBAClC,KAAK,gBAAgB,YAAY;AAErC,UAAI,CAAC,SAAS;AACV;AAAA,MACJ;AACA,WAAK,yBAAyB,IAAI,SAAS,WAAW;AAEtD;AAAA,QACI,gCAAgC,OAAO,UAAU,IAAI,UAAU,WAAW,IAAI,aAC7D,KAAK,UAAU,YAAY,CAAC,gBACzB,KAAK,UAAU,OAAO,KAAK,WAAW,CAAC,CAAC;AAAA,MAChE;AAEA,YAAM,QAA8B;AAAA,QAChC;AAAA,QACA,MAAM,WAAW,cAAc;AAAA,QAC/B,MAAM,WAAW;AAAA,QACjB;AAAA,QACA,cAAc;AAAA,QACd,eAAe;AAAA,MACnB;AAEA,WAAK,WAAW,qBAAqB,KAA2C;AAAA,IACpF,CAAC;AAED,UAAM,KAAK,OAAO,mBAAmB,CAAC,MAAM,GAAG,IAAI;AACnD,QAAI,wCAAwC;AAC5C,SAAK,aAAa,EAAE;AAAA,EACxB;AAAA,EAEA,MAAM,gBAAgB,IAA2B;AAC7C,UAAM,KAAK,QAAQ,kBAAkB;AACrC,QAAI,yBAAyB;AAC7B,SAAK,aAAa,EAAE;AAAA,EACxB;AAAA,EAEA,MAAM,eAAe,IAAY,MAAkC;AAC/D,UAAM,aAAa,KAAK,uBAAuB,IAAI,KAAK,OAAO;AAC/D,QAAI,CAAC,YAAY;AACb;AAAA,QACI,2CAA2C,KAAK,OAAO,aAAa,CAAC,GAAG,KAAK,uBAAuB,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC1H;AACA,WAAK,WAAW,IAAI,oBAAoB,+BAA+B,KAAK,OAAO,EAAE;AACrF;AAAA,IACJ;AAEA,UAAM,SAAS,KAAK;AACpB,UAAM,YAA6B;AAAA,MAC/B;AAAA,MACA,UAAU,oBAAI,IAAI;AAAA,MAClB,iBAAiB,oBAAI,IAAI;AAAA,MACzB,eAAe,oBAAI,IAAI;AAAA,IAC3B;AACA,SAAK,aAAa,IAAI,QAAQ,SAAS;AAIvC,QAAI;AACJ,UAAM,qBAAqB,MAAM;AAC7B,2BAAqB,kCAAkC,WAAW,KAAK;AACvE,UAAI,4BAA4B,MAAM,wBAAwB,WAAW,KAAK,GAAG;AACjF,WAAK,aAAa,OAAO,MAAM;AAC/B,WAAK,WAAW,gBAAgB,EAAE,mBAAmB,OAAO,CAAC;AAAA,IACjE;AACA,eAAW,KAAK,cAAc,kBAAkB;AAEhD,QAAI,yBAAyB,KAAK,OAAO,YAAY,WAAW,KAAK,MAAM;AAC3E,QAAI;AAIA,UAAI,8CAA8C;AAClD,YAAM,KAAK,OAAQ,kBAAkB;AAErC,YAAM,WAAW,aAAa;AAC9B,UAAI,2BAA2B,MAAM,UAAU,WAAW,KAAK,QAAQ,WAAW,OAAO,GAAG,EAAE;AAE9F,UAAI,iBAAiB,MAAM,qCAAqC;AAChE,YAAM,WAAW,MAAM;AAAA,QACnB,WAAW,sBAAsB,CAAC,MAAM,CAAC;AAAA,QACzC;AAAA,QACA;AAAA,MACJ;AACA,UAAI,iBAAiB,MAAM,cAAc,SAAS,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,UAAU,WAAW,KAAK,EAAE;AAEzG,iBAAW,WAAW,UAAU;AAC5B,kBAAU,SAAS,IAAI,QAAQ,MAAM,OAAO;AAC5C,YAAI,QAAQ,SAAS,OAAQ;AAC7B,YAAI,iBAAiB,MAAM,sCAAsC,QAAQ,IAAI,KAAK;AAClF,cAAM,QAAQ,MAAM;AAAA,UAChB,QAAQ,6BAA6B;AAAA,UACrC;AAAA,UACA,2BAA2B,QAAQ,IAAI;AAAA,QAC3C;AACA,mBAAW,QAAQ,OAAO;AACtB,oBAAU,gBAAgB,IAAI,KAAK,MAAM,IAAI;AAAA,QACjD;AACA;AAAA,UACI,iBAAiB,MAAM,aAAa,QAAQ,IAAI,KAAK,MAAM,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,UAAU,WAAW,KAAK;AAAA,QACpH;AAAA,MACJ;AAEA,YAAM,MAAM,WAAW,OAAO;AAC9B,UAAI,iBAAiB,MAAM,cAAc,GAAG,EAAE;AAG9C,UAAI,iDAAiD;AACrD,YAAM,KAAK,OAAQ,mBAAmB,CAAC,MAAM,GAAG,IAAI;AAEpD,WAAK,aAAa,IAAI,EAAE,mBAAmB,QAAQ,IAAI,CAAC;AAAA,IAC5D,SAAS,KAAK;AACV,YAAM,SAAS,sBAAuB,IAAc;AACpD,YAAM,iBAAiB,MAAM,YAAY,MAAM,EAAE;AACjD,UAAI,KAAK,aAAa,IAAI,MAAM,GAAG;AAC/B,aAAK,aAAa,OAAO,MAAM;AAAA,MACnC;AACA,iBAAW,eAAe,cAAc,kBAAkB;AAC1D,UAAI,WAAW,UAAU,aAAa;AAClC,mBACK,gBAAgB,EAChB;AAAA,UAAM,mBACH,KAAK,iBAAiB,MAAM,+BAAgC,cAAwB,OAAO,EAAE;AAAA,QACjG;AAAA,MACR;AAEA,WAAK,OACA,mBAAmB,CAAC,MAAM,GAAG,IAAI,EACjC;AAAA,QAAM,aACH,KAAK,2DAA4D,QAAkB,OAAO,EAAE;AAAA,MAChG;AACJ,WAAK,WAAW,IAAI,kBAAkB,MAAM;AAAA,IAChD;AAAA,EACJ;AAAA,EAEA,MAAM,kBAAkB,IAAY,kBAAyC;AACzE,UAAM,OAAO,KAAK,aAAa,IAAI,gBAAgB;AACnD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,gBAAgB,EAAE;AACpF;AAAA,IACJ;AAEA,QAAI,KAAK,WAAW,UAAU,aAAa;AACvC,YAAM,KAAK,WAAW,gBAAgB;AAAA,IAC1C;AACA,SAAK,aAAa,OAAO,gBAAgB;AACzC,SAAK,aAAa,EAAE;AAAA,EACxB;AAAA,EAEA,MAAM,wBAAwB,IAAY,MAA2C;AACjF,UAAM,OAAO,KAAK,aAAa,IAAI,KAAK,iBAAiB;AACzD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,KAAK,iBAAiB,EAAE;AAC1F;AAAA,IACJ;AAGA,QAAI,KAAK,SAAS,OAAO,GAAG;AACxB,YAAM,QAAQ,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC;AACtC,UAAI,iBAAiB,KAAK,iBAAiB,yBAAyB,MAAM,KAAK,IAAI,CAAC,EAAE;AACtF,WAAK,aAAa,IAAI,EAAE,UAAU,MAAM,IAAI,WAAS,EAAE,KAAK,EAAE,EAAE,CAAC;AACjE;AAAA,IACJ;AAGA,QAAI,iBAAiB,KAAK,iBAAiB,iCAAiC;AAC5E,UAAM,WAAW,MAAM;AAAA,MACnB,KAAK,WAAW,sBAAsB,CAAC,CAAC;AAAA,MACxC;AAAA,MACA;AAAA,IACJ;AACA,eAAW,WAAW,UAAU;AAC5B,WAAK,SAAS,IAAI,QAAQ,MAAM,OAAO;AAAA,IAC3C;AACA,QAAI,iBAAiB,KAAK,iBAAiB,yBAAyB,SAAS,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAE1G,SAAK,aAAa,IAAI;AAAA,MAClB,UAAU,SAAS,IAAI,QAAM,EAAE,MAAM,EAAE,KAAK,EAAE;AAAA,IAClD,CAAC;AAAA,EACL;AAAA,EAEA,MAAM,+BAA+B,IAAY,MAAkD;AAC/F,UAAM,OAAO,KAAK,aAAa,IAAI,KAAK,iBAAiB;AACzD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,KAAK,iBAAiB,EAAE;AAC1F;AAAA,IACJ;AAEA,UAAM,UAAU,KAAK,SAAS,IAAI,KAAK,YAAY;AACnD,QAAI,CAAC,SAAS;AACV,WAAK,WAAW,IAAI,qBAAqB,WAAW,KAAK,YAAY,YAAY;AACjF;AAAA,IACJ;AAGA,UAAM,cAAc,QAAQ,mBAAmB,CAAC;AAChD,QAAI,YAAY,SAAS,GAAG;AACxB;AAAA,QACI,iBAAiB,KAAK,iBAAiB,mCAAmC,KAAK,YAAY,OACvF,YAAY,IAAI,OAAK,GAAG,EAAE,IAAI,IAAI,EAAE,WAAW,KAAK,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI;AAAA,MAC9E;AACA,WAAK,aAAa,IAAI;AAAA,QAClB,iBAAiB,YAAY,IAAI,QAAM;AAAA,UACnC,MAAM,EAAE;AAAA,UACR,YAAY,EAAE;AAAA,QAClB,EAAE;AAAA,MACN,CAAC;AACD;AAAA,IACJ;AAGA,QAAI,iBAAiB,KAAK,iBAAiB,oCAAoC,KAAK,YAAY,YAAY;AAC5G,UAAM,kBAAkB,MAAM;AAAA,MAC1B,QAAQ,6BAA6B,CAAC,CAAC;AAAA,MACvC;AAAA,MACA,2BAA2B,KAAK,YAAY;AAAA,IAChD;AACA,eAAW,QAAQ,iBAAiB;AAChC,WAAK,gBAAgB,IAAI,KAAK,MAAM,IAAI;AAAA,IAC5C;AACA;AAAA,MACI,iBAAiB,KAAK,iBAAiB,yBAAyB,KAAK,YAAY,OAC7E,gBAAgB,IAAI,OAAK,GAAG,EAAE,IAAI,IAAI,EAAE,WAAW,KAAK,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI;AAAA,IAClF;AAEA,SAAK,aAAa,IAAI;AAAA,MAClB,iBAAiB,gBAAgB,IAAI,QAAM;AAAA,QACvC,MAAM,EAAE;AAAA,QACR,YAAY,EAAE;AAAA,MAClB,EAAE;AAAA,IACN,CAAC;AAAA,EACL;AAAA,EAEA,MAAM,0BAA0B,IAAY,MAA6C;AACrF,UAAM,OAAO,KAAK,aAAa,IAAI,KAAK,iBAAiB;AACzD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,KAAK,iBAAiB,EAAE;AAC1F;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,oBAAoB,MAAM,KAAK,mBAAmB;AACpE,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,4BAA4B,kBAAkB,KAAK,mBAAmB,YAAY;AACtG;AAAA,IACJ;AAEA,UAAM,OAAO,MAAM,KAAK,UAAU;AAClC,QAAI,eAAe,KAAK,mBAAmB,WAAM,KAAK,MAAM,QAAQ;AACpE,SAAK,aAAa,IAAI,EAAE,OAAO,OAAO,KAAK,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;AAAA,EACzE;AAAA,EAEA,MAAM,2BAA2B,IAAY,MAA8C;AACvF,UAAM,OAAO,KAAK,aAAa,IAAI,KAAK,iBAAiB;AACzD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,KAAK,iBAAiB,EAAE;AAC1F;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,oBAAoB,MAAM,KAAK,mBAAmB;AACpE,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,4BAA4B,kBAAkB,KAAK,mBAAmB,YAAY;AACtG;AAAA,IACJ;AAEA,UAAM,OAAO,OAAO,KAAK,KAAK,OAAO,QAAQ;AAC7C,UAAM,eAAe,KAAK,YAAY;AACtC,QAAI,gBAAgB,KAAK,mBAAmB,IAAI,KAAK,MAAM,uBAAuB,YAAY,EAAE;AAChG,UAAM,KAAK,WAAW,MAAM,CAAC,YAAY;AACzC,SAAK,0BAA0B;AAC/B,SAAK,aAAa,EAAE;AAAA,EACxB;AAAA,EAEA,MAAM,+BAA+B,IAAY,MAAkD;AAC/F,UAAM,OAAO,KAAK,aAAa,IAAI,KAAK,iBAAiB;AACzD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,KAAK,iBAAiB,EAAE;AAC1F;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,oBAAoB,MAAM,KAAK,mBAAmB;AACpE,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,4BAA4B,kBAAkB,KAAK,mBAAmB,YAAY;AACtG;AAAA,IACJ;AAEA,UAAM,SAAS,KAAK;AACpB,SAAK,GAAG,QAAQ,CAAC,SAAiB;AAE9B,UAAI,iBAAiB,KAAK,mBAAmB,WAAW,MAAM,IAAI,KAAK,MAAM,QAAQ;AACrF,WAAK,iBAAiB,kBAAkB,cAAc,QAAQ,IAAI,WAAW,IAAI,CAAC;AAAA,IACtF,CAAC;AAED,UAAM,KAAK,eAAe;AAC1B,SAAK,cAAc,IAAI,KAAK,qBAAqB,IAAI;AACrD,QAAI,oBAAoB,KAAK,mBAAmB,WAAW,MAAM,EAAE;AACnE,SAAK,aAAa,EAAE;AAAA,EACxB;AAAA,EAEA,MAAM,iCAAiC,IAAY,MAAoD;AACnG,UAAM,OAAO,KAAK,aAAa,IAAI,KAAK,iBAAiB;AACzD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,KAAK,iBAAiB,EAAE;AAC1F;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,cAAc,IAAI,KAAK,mBAAmB;AAC5D,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,kBAAkB,qBAAqB,KAAK,mBAAmB,EAAE;AACrF;AAAA,IACJ;AAEA,UAAM,KAAK,iBAAiB;AAC5B,SAAK,mBAAmB,MAAM;AAC9B,SAAK,cAAc,OAAO,KAAK,mBAAmB;AAClD,QAAI,sBAAsB,KAAK,mBAAmB,WAAW,KAAK,iBAAiB,EAAE;AACrF,SAAK,aAAa,EAAE;AAAA,EACxB;AAAA,EAEA,MAAM,kBAAkB,IAAY,kBAA0B,KAA4B;AACtF,UAAM,OAAO,KAAK,aAAa,IAAI,gBAAgB;AACnD,QAAI,CAAC,MAAM;AACP,WAAK,WAAW,IAAI,iBAAiB,6BAA6B,gBAAgB,EAAE;AACpF;AAAA,IACJ;AAEA,UAAM,YAAY,KAAK,WAAW,OAAO;AACzC,QAAI,6BAA6B,gBAAgB,cAAc,GAAG,WAAW,SAAS,EAAE;AACxF,SAAK,aAAa,IAAI,EAAE,KAAK,UAAU,CAAC;AAAA,EAC5C;AAAA;AAAA,EAIA,mBAAmB,MAAoB;AACnC,UAAM,QAAQ,kBAAkB,IAAI,WAAW,IAAI,CAAC;AACpD,QAAI,sBAAiB,MAAM,MAAM,WAAW,MAAM,gBAAgB,YAAY,MAAM,QAAQ,MAAM,QAAQ;AAE1G,QAAI,MAAM,WAAW,kBAAkB,WAAW;AAC9C,YAAM,OAAO,KAAK,aAAa,IAAI,MAAM,gBAAgB;AACzD,UAAI,CAAC,MAAM,yBAAyB;AAChC,aAAK,gEAA2D,MAAM,gBAAgB,EAAE;AACxF;AAAA,MACJ;AAGA,WAAK,wBAAwB,WAAW,OAAO,KAAK,MAAM,OAAO,GAAG,KAAK,EAAE,MAAM,SAAO;AACpF,cAAM,uBAAuB,GAAG;AAAA,MACpC,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA,EAIA,oBAAoB,MAAuB,MAA0C;AAEjF,WACI,KAAK,gBAAgB,IAAI,IAAI,KAC7B,KAAK,gBAAgB,IAAI,KAAK,YAAY,CAAC,KAC3C,KAAK,gBAAgB,IAAI,KAAK,YAAY,EAAE,QAAQ,MAAM,EAAE,EAAE,YAAY,CAAC;AAAA,EAEnF;AAAA,EAEA,aAAa,IAAY,QAAwC;AAC7D,SAAK,KAAK,KAAK,KAAK,UAAU,EAAE,IAAI,SAAS,MAAM,QAAQ,UAAU,CAAC,EAAE,CAAC,CAAC;AAAA,EAC9E;AAAA,EAEA,WAAW,IAAYA,QAAe,SAAuB;AACzD,SAAK,KAAK,KAAK,KAAK,UAAU,EAAE,IAAI,SAAS,OAAO,OAAAA,QAAO,QAAQ,CAAC,CAAC;AAAA,EACzE;AAAA,EAEA,WAAW,OAAe,MAAqC;AAC3D,SAAK,KAAK,KAAK,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC,CAAC;AAAA,EAClD;AAAA,EAEA,iBAAiB,QAAgB,kBAA0B,SAA2B;AAClF,QAAI,sBAAiB,MAAM,WAAW,gBAAgB,YAAY,QAAQ,MAAM,QAAQ;AACxF,UAAM,QAAQ,kBAAkB,QAAQ,kBAAkB,OAAO;AACjE,SAAK,KAAK,KAAK,KAAK;AAAA,EACxB;AACJ;",
5
+ "names": ["error"]
6
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=noble-ble-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noble-ble-proxy.d.ts","sourceRoot":"","sources":["../../../src/example/noble-ble-proxy.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { NobleBleProxyClient } from "./NobleBleProxyClient.js";
7
+ const args = process.argv.slice(2);
8
+ let serverUrl = "ws://localhost:5580/ble";
9
+ let hciId;
10
+ for (let i = 0; i < args.length; i++) {
11
+ if (args[i] === "--server" && args[i + 1]) {
12
+ serverUrl = args[++i];
13
+ } else if (args[i] === "--hci-id" && args[i + 1]) {
14
+ hciId = parseInt(args[++i]);
15
+ } else if (args[i] === "--help" || args[i] === "-h") {
16
+ console.log("Noble BLE Proxy Client - Reference implementation");
17
+ console.log("");
18
+ console.log("Usage: noble-ble-proxy [options]");
19
+ console.log("");
20
+ console.log("Options:");
21
+ console.log(" --server <url> Matter server BLE proxy URL (default: ws://localhost:5580/ble)");
22
+ console.log(" --hci-id <id> Bluetooth adapter HCI ID (e.g., 0 for hci0)");
23
+ console.log(" --help, -h Show this help");
24
+ process.exit(0);
25
+ }
26
+ }
27
+ const client = new NobleBleProxyClient(serverUrl, hciId);
28
+ console.log(`Connecting to ${serverUrl}...`);
29
+ try {
30
+ await client.connect();
31
+ console.log("Connected. BLE proxy active. Press Ctrl+C to stop.");
32
+ } catch (err) {
33
+ console.error(`Failed to connect: ${err.message}`);
34
+ process.exit(1);
35
+ }
36
+ process.on("SIGINT", () => {
37
+ console.log("\nShutting down...");
38
+ client.close();
39
+ process.exit(0);
40
+ });
41
+ process.on("SIGTERM", () => {
42
+ client.close();
43
+ process.exit(0);
44
+ });
45
+ //# sourceMappingURL=noble-ble-proxy.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/example/noble-ble-proxy.ts"],
4
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,SAAS,2BAA2B;AAEpC,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AAEjC,IAAI,YAAY;AAChB,IAAI;AAEJ,SAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,MAAI,KAAK,CAAC,MAAM,cAAc,KAAK,IAAI,CAAC,GAAG;AACvC,gBAAY,KAAK,EAAE,CAAC;AAAA,EACxB,WAAW,KAAK,CAAC,MAAM,cAAc,KAAK,IAAI,CAAC,GAAG;AAC9C,YAAQ,SAAS,KAAK,EAAE,CAAC,CAAC;AAAA,EAC9B,WAAW,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AACjD,YAAQ,IAAI,mDAAmD;AAC/D,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,kCAAkC;AAC9C,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,UAAU;AACtB,YAAQ,IAAI,mFAAmF;AAC/F,YAAQ,IAAI,gEAAgE;AAC5E,YAAQ,IAAI,mCAAmC;AAC/C,YAAQ,KAAK,CAAC;AAAA,EAClB;AACJ;AAEA,MAAM,SAAS,IAAI,oBAAoB,WAAW,KAAK;AAEvD,QAAQ,IAAI,iBAAiB,SAAS,KAAK;AAC3C,IAAI;AACA,QAAM,OAAO,QAAQ;AACrB,UAAQ,IAAI,oDAAoD;AACpE,SAAS,KAAK;AACV,UAAQ,MAAM,sBAAuB,IAAc,OAAO,EAAE;AAC5D,UAAQ,KAAK,CAAC;AAClB;AAEA,QAAQ,GAAG,UAAU,MAAM;AACvB,UAAQ,IAAI,oBAAoB;AAChC,SAAO,MAAM;AACb,UAAQ,KAAK,CAAC;AAClB,CAAC;AAED,QAAQ,GAAG,WAAW,MAAM;AACxB,SAAO,MAAM;AACb,UAAQ,KAAK,CAAC;AAClB,CAAC;",
5
+ "names": []
6
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * @matter-server/ble-proxy - BLE proxy for matter.js over WebSocket
8
+ */
9
+ export * from "./BleProxyHandler.js";
10
+ export * from "./BleProxyProtocol.js";
11
+ export * from "./ProxyBle.js";
12
+ export * from "./ProxyBleChannel.js";
13
+ export * from "./ProxyBleClient.js";
14
+ export * from "./ProxyBleScanner.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export * from "./BleProxyHandler.js";
7
+ export * from "./BleProxyProtocol.js";
8
+ export * from "./ProxyBle.js";
9
+ export * from "./ProxyBleChannel.js";
10
+ export * from "./ProxyBleClient.js";
11
+ export * from "./ProxyBleScanner.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.ts"],
4
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
5
+ "names": []
6
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }