@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.
- package/README.md +116 -0
- package/dist/esm/BleProxyHandler.d.ts +39 -0
- package/dist/esm/BleProxyHandler.d.ts.map +1 -0
- package/dist/esm/BleProxyHandler.js +256 -0
- package/dist/esm/BleProxyHandler.js.map +6 -0
- package/dist/esm/BleProxyProtocol.d.ts +230 -0
- package/dist/esm/BleProxyProtocol.d.ts.map +1 -0
- package/dist/esm/BleProxyProtocol.js +82 -0
- package/dist/esm/BleProxyProtocol.js.map +6 -0
- package/dist/esm/ProxyBle.d.ts +24 -0
- package/dist/esm/ProxyBle.d.ts.map +1 -0
- package/dist/esm/ProxyBle.js +50 -0
- package/dist/esm/ProxyBle.js.map +6 -0
- package/dist/esm/ProxyBleChannel.d.ts +38 -0
- package/dist/esm/ProxyBleChannel.d.ts.map +1 -0
- package/dist/esm/ProxyBleChannel.js +314 -0
- package/dist/esm/ProxyBleChannel.js.map +6 -0
- package/dist/esm/ProxyBleClient.d.ts +32 -0
- package/dist/esm/ProxyBleClient.d.ts.map +1 -0
- package/dist/esm/ProxyBleClient.js +100 -0
- package/dist/esm/ProxyBleClient.js.map +6 -0
- package/dist/esm/ProxyBleScanner.d.ts +27 -0
- package/dist/esm/ProxyBleScanner.d.ts.map +1 -0
- package/dist/esm/ProxyBleScanner.js +23 -0
- package/dist/esm/ProxyBleScanner.js.map +6 -0
- package/dist/esm/example/NobleBleProxyClient.d.ts +12 -0
- package/dist/esm/example/NobleBleProxyClient.d.ts.map +1 -0
- package/dist/esm/example/NobleBleProxyClient.js +520 -0
- package/dist/esm/example/NobleBleProxyClient.js.map +6 -0
- package/dist/esm/example/noble-ble-proxy.d.ts +7 -0
- package/dist/esm/example/noble-ble-proxy.d.ts.map +1 -0
- package/dist/esm/example/noble-ble-proxy.js +45 -0
- package/dist/esm/example/noble-ble-proxy.js.map +6 -0
- package/dist/esm/index.d.ts +15 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/package.json +3 -0
- package/package.json +46 -0
- package/src/BleProxyHandler.ts +345 -0
- package/src/BleProxyProtocol.ts +287 -0
- package/src/ProxyBle.ts +64 -0
- package/src/ProxyBleChannel.ts +407 -0
- package/src/ProxyBleClient.ts +136 -0
- package/src/ProxyBleScanner.ts +38 -0
- package/src/example/NobleBleProxyClient.ts +679 -0
- package/src/example/noble-ble-proxy.ts +62 -0
- package/src/index.ts +16 -0
- package/src/tsconfig.json +15 -0
- package/src/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @matter-server/ble-proxy
|
|
2
|
+
|
|
3
|
+
BLE proxy support for the Matter server — enables BLE commissioning of Matter devices through a remote Bluetooth adapter over WebSocket.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Instead of requiring a Bluetooth adapter on the server host, a **proxy client** sits next to the BLE hardware and executes BLE commands on behalf of the server over a WebSocket connection:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[Matter Device] <-- BLE --> [Proxy Client] <-- WebSocket /ble --> [Matter Server]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The primary use case is Home Assistant (with local or ESPHome BLE proxies) acting as the proxy client. See the [BLE Proxy Protocol documentation](../../docs/ble-proxy-protocol.md) for the full protocol specification.
|
|
14
|
+
|
|
15
|
+
For a Python implementation of the protocol (Bleak-based CLI + reusable library used by the Home Assistant Matter integration), see [`python_ble_proxy/`](../../python_ble_proxy/) and the [`matter-ble-proxy`](https://pypi.org/project/matter-ble-proxy/) package on PyPI.
|
|
16
|
+
|
|
17
|
+
## Enabling BLE Proxy Mode on the Server
|
|
18
|
+
|
|
19
|
+
Start the Matter server with the `--ble-proxy` flag:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run server -- --ble-proxy
|
|
23
|
+
# or
|
|
24
|
+
BLE_PROXY=true npm run server
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This exposes a `/ble` WebSocket endpoint (e.g. `ws://localhost:5580/ble`) that proxy clients connect to.
|
|
28
|
+
|
|
29
|
+
> **Note:** `--ble-proxy` and `--bluetooth-adapter` are mutually exclusive.
|
|
30
|
+
|
|
31
|
+
## Noble Example Client
|
|
32
|
+
|
|
33
|
+
The package includes a reference proxy client that bridges a local Bluetooth adapter to the server using [Noble](https://github.com/stoprocent/noble). This is useful for:
|
|
34
|
+
- Testing BLE commissioning without Home Assistant
|
|
35
|
+
- Running the server on a machine without native BLE bindings
|
|
36
|
+
|
|
37
|
+
### Prerequisites
|
|
38
|
+
|
|
39
|
+
- A host with a Bluetooth adapter (Linux or macOS)
|
|
40
|
+
- Server running with `--ble-proxy`
|
|
41
|
+
- `@matter/nodejs-ble` optional dependency available (listed in `optionalDependencies`)
|
|
42
|
+
|
|
43
|
+
For Noble-specific setup (permissions, platform requirements, known limitations), see the
|
|
44
|
+
[Noble prerequisites section](https://github.com/matter-js/matter.js/tree/main/packages/nodejs-ble#prerequisites-and-limitations)
|
|
45
|
+
in the `@matter/nodejs-ble` README.
|
|
46
|
+
|
|
47
|
+
### Running
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Default: connect to ws://localhost:5580/ble
|
|
51
|
+
npm run noble-ble-proxy
|
|
52
|
+
|
|
53
|
+
# Custom server URL
|
|
54
|
+
npm run noble-ble-proxy -- --server ws://192.168.1.100:5580/ble
|
|
55
|
+
|
|
56
|
+
# Specific HCI adapter
|
|
57
|
+
npm run noble-ble-proxy -- --hci-id 1
|
|
58
|
+
|
|
59
|
+
# Help
|
|
60
|
+
npm run noble-ble-proxy -- --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Once connected, you will see:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
Connecting to ws://localhost:5580/ble...
|
|
67
|
+
BLE proxy handshake complete (protocol v1)
|
|
68
|
+
Connected. BLE proxy active. Press Ctrl+C to stop.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The proxy stays connected and handles BLE commissioning commands transparently whenever a commission-via-BLE is triggered.
|
|
72
|
+
|
|
73
|
+
## Python Example Client
|
|
74
|
+
|
|
75
|
+
The repository also ships a Python reference proxy client built on
|
|
76
|
+
[Bleak](https://github.com/hbldh/bleak), published to PyPI as
|
|
77
|
+
[`matter-ble-proxy`](https://pypi.org/project/matter-ble-proxy/). Same use
|
|
78
|
+
cases as the Noble client; pick whichever stack you already have set up. The
|
|
79
|
+
Home Assistant Matter integration consumes the library form of this package.
|
|
80
|
+
|
|
81
|
+
### Prerequisites
|
|
82
|
+
|
|
83
|
+
- Python 3.12+
|
|
84
|
+
- A host with a Bluetooth adapter (Linux, macOS, or Windows — Bleak supports all three)
|
|
85
|
+
- Server running with `--ble-proxy`
|
|
86
|
+
|
|
87
|
+
### Running from a release
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install matter-ble-proxy
|
|
91
|
+
matter-ble-proxy --server ws://localhost:5580/ble
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Running from this repository
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Default: connect to ws://localhost:5580/ble
|
|
98
|
+
npm run python-ble-proxy:run
|
|
99
|
+
|
|
100
|
+
# Custom server URL
|
|
101
|
+
npm run python-ble-proxy:run -- --server ws://192.168.1.100:5580/ble --log-level INFO
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Once connected, you will see:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
17:46:35.541 INFO matter_ble_proxy.cli: Connected. BLE proxy active. Press Ctrl+C to stop.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
See [`python_ble_proxy/`](../../python_ble_proxy/) for the library API
|
|
111
|
+
(`MatterBleProxy`, `BleScanSource`, `BleDeviceResolver`, ...) used by
|
|
112
|
+
integrators that bring their own BLE source.
|
|
113
|
+
|
|
114
|
+
## Home Assistant Integration
|
|
115
|
+
|
|
116
|
+
The Home Assistant Matter integration automatically connects to `/ble` when the server reports `ble_proxy_enabled: true`. Start the server with `--ble-proxy` (or `ble_proxy: true` in the add-on config). No further HA-side configuration is needed.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { WebServerHandler } from "@matter-server/ws-controller";
|
|
7
|
+
import { Observable } from "@matter/main";
|
|
8
|
+
import type { createServer } from "node:http";
|
|
9
|
+
import { type BinaryFrame, type BleProxyCommandMap, type BleProxyCommandName, type BleProxyEventName } from "./BleProxyProtocol.js";
|
|
10
|
+
type HttpServer = ReturnType<typeof createServer>;
|
|
11
|
+
/**
|
|
12
|
+
* WebSocket server handler for the /ble BLE proxy endpoint.
|
|
13
|
+
*
|
|
14
|
+
* Accepts a single client connection, performs the protocol handshake,
|
|
15
|
+
* and routes commands/events/binary frames between the proxy implementation
|
|
16
|
+
* and the connected BLE proxy client.
|
|
17
|
+
*/
|
|
18
|
+
export declare class BleProxyHandler implements WebServerHandler {
|
|
19
|
+
#private;
|
|
20
|
+
/** Emitted when a JSON event is received from the BLE proxy client. */
|
|
21
|
+
readonly eventReceived: Observable<[event: BleProxyEventName, data: Record<string, unknown>], void>;
|
|
22
|
+
/** Emitted when a binary frame is received from the BLE proxy client. */
|
|
23
|
+
readonly binaryFrameReceived: Observable<[frame: BinaryFrame], void>;
|
|
24
|
+
get connected(): boolean;
|
|
25
|
+
register(server: HttpServer): Promise<void>;
|
|
26
|
+
unregister(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Send a typed command to the BLE proxy client and wait for the response.
|
|
29
|
+
* The return type is inferred from the command name via BleProxyCommandMap.
|
|
30
|
+
* Rejects if no client is connected or the client disconnects before responding.
|
|
31
|
+
*/
|
|
32
|
+
sendCommand<C extends BleProxyCommandName>(command: C, ...rest: BleProxyCommandMap[C]["args"] extends undefined ? [] : [args: BleProxyCommandMap[C]["args"]]): Promise<BleProxyCommandMap[C]["result"]>;
|
|
33
|
+
/**
|
|
34
|
+
* Send a binary frame to the BLE proxy client.
|
|
35
|
+
*/
|
|
36
|
+
sendBinaryFrame(opcode: number, connectionHandle: number, payload: Uint8Array): void;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=BleProxyHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BleProxyHandler.d.ts","sourceRoot":"","sources":["../../src/BleProxyHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAyB,UAAU,EAAqC,MAAM,cAAc,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAIH,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAKzB,MAAM,uBAAuB,CAAC;AAE/B,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AAclD;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;;IAcpD,uEAAuE;IACvE,QAAQ,CAAC,aAAa,8EAA+E;IAErG,yEAAyE;IACzE,QAAQ,CAAC,mBAAmB,yCAA0C;IAEtE,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAuEhD;IAED,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAmC1B;IAED;;;;OAIG;IACG,WAAW,CAAC,CAAC,SAAS,mBAAmB,EAC3C,OAAO,EAAE,CAAC,EACV,GAAG,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,SAAS,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GACtG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CA0B1C;IAED;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAMnF;CAwHJ"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { createPromise, Logger, Observable, Seconds, Time, withTimeout } from "@matter/main";
|
|
7
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
8
|
+
import {
|
|
9
|
+
BLE_PROXY_PROTOCOL_VERSION,
|
|
10
|
+
decodeBinaryFrame,
|
|
11
|
+
encodeBinaryFrame
|
|
12
|
+
} from "./BleProxyProtocol.js";
|
|
13
|
+
const logger = Logger.get("BleProxyHandler");
|
|
14
|
+
const HANDSHAKE_TIMEOUT = Seconds(10);
|
|
15
|
+
const COMMAND_TIMEOUT = Seconds(60);
|
|
16
|
+
class BleProxyHandler {
|
|
17
|
+
#wss;
|
|
18
|
+
/** Upgrade listener removers, one per HTTP server `register()` call (multi-bind). */
|
|
19
|
+
#removeUpgradeListeners = [];
|
|
20
|
+
#client;
|
|
21
|
+
#handshakeComplete = false;
|
|
22
|
+
#pendingCommands = /* @__PURE__ */ new Map();
|
|
23
|
+
/** Command ID counter. Rolls over at 0xFFFF to stay in safe integer range. */
|
|
24
|
+
#nextCommandId = 0;
|
|
25
|
+
#closed = false;
|
|
26
|
+
/** Emitted when a JSON event is received from the BLE proxy client. */
|
|
27
|
+
eventReceived = new Observable();
|
|
28
|
+
/** Emitted when a binary frame is received from the BLE proxy client. */
|
|
29
|
+
binaryFrameReceived = new Observable();
|
|
30
|
+
get connected() {
|
|
31
|
+
return this.#handshakeComplete && this.#client?.readyState === WebSocket.OPEN;
|
|
32
|
+
}
|
|
33
|
+
async register(server) {
|
|
34
|
+
const wss = this.#wss = new WebSocketServer({ noServer: true });
|
|
35
|
+
const upgradeHandler = (req, socket, head) => {
|
|
36
|
+
logger.debug(`Upgrade request received: ${req.url}`);
|
|
37
|
+
const path = req.url?.split("?")[0];
|
|
38
|
+
if (path === "/ble") {
|
|
39
|
+
req._matterHandledUpgrade = true;
|
|
40
|
+
wss.handleUpgrade(
|
|
41
|
+
req,
|
|
42
|
+
socket,
|
|
43
|
+
head,
|
|
44
|
+
(ws) => wss.emit("connection", ws, req)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
server.on("upgrade", upgradeHandler);
|
|
49
|
+
this.#removeUpgradeListeners.push(() => server.removeListener("upgrade", upgradeHandler));
|
|
50
|
+
logger.info("BLE proxy WebSocket endpoint registered on /ble");
|
|
51
|
+
wss.on("connection", (ws) => {
|
|
52
|
+
if (this.#closed) {
|
|
53
|
+
ws.close();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (this.#client) {
|
|
57
|
+
logger.warn("Rejecting new BLE proxy client - only one connection allowed");
|
|
58
|
+
ws.close(4e3, "Only one BLE proxy client allowed");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
logger.info("BLE proxy client connected, waiting for handshake");
|
|
62
|
+
this.#client = ws;
|
|
63
|
+
this.#handshakeComplete = false;
|
|
64
|
+
const handshakeTimer = Time.getTimer("BLE proxy handshake timeout", HANDSHAKE_TIMEOUT, () => {
|
|
65
|
+
if (!this.#handshakeComplete) {
|
|
66
|
+
logger.warn("BLE proxy handshake timeout - closing connection");
|
|
67
|
+
ws.close(4001, "Handshake timeout");
|
|
68
|
+
this.#cleanupClient();
|
|
69
|
+
}
|
|
70
|
+
}).start();
|
|
71
|
+
ws.on("message", (data, isBinary) => {
|
|
72
|
+
if (isBinary) {
|
|
73
|
+
this.#handleBinaryMessage(data);
|
|
74
|
+
} else {
|
|
75
|
+
this.#handleTextMessage(data.toString(), handshakeTimer);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
ws.on("close", () => {
|
|
79
|
+
handshakeTimer.stop();
|
|
80
|
+
logger.info("BLE proxy client disconnected");
|
|
81
|
+
this.#cleanupClient();
|
|
82
|
+
});
|
|
83
|
+
ws.on("error", (err) => {
|
|
84
|
+
logger.error("BLE proxy WebSocket error:", err);
|
|
85
|
+
handshakeTimer.stop();
|
|
86
|
+
this.#cleanupClient();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
unregister() {
|
|
91
|
+
if (!this.#wss || this.#closed) {
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
}
|
|
94
|
+
this.#closed = true;
|
|
95
|
+
for (const remove of this.#removeUpgradeListeners) remove();
|
|
96
|
+
this.#removeUpgradeListeners = [];
|
|
97
|
+
if (this.#client && this.#client.readyState === WebSocket.OPEN) {
|
|
98
|
+
this.#client.close();
|
|
99
|
+
}
|
|
100
|
+
this.#cleanupClient();
|
|
101
|
+
const wss = this.#wss;
|
|
102
|
+
this.#wss = void 0;
|
|
103
|
+
wss.clients.forEach((client) => {
|
|
104
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
105
|
+
client.close();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
wss.close((err) => {
|
|
110
|
+
if (err) {
|
|
111
|
+
reject(err);
|
|
112
|
+
} else {
|
|
113
|
+
resolve();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Send a typed command to the BLE proxy client and wait for the response.
|
|
120
|
+
* The return type is inferred from the command name via BleProxyCommandMap.
|
|
121
|
+
* Rejects if no client is connected or the client disconnects before responding.
|
|
122
|
+
*/
|
|
123
|
+
async sendCommand(command, ...rest) {
|
|
124
|
+
if (!this.connected || !this.#client) {
|
|
125
|
+
throw new Error("BLE proxy client not connected");
|
|
126
|
+
}
|
|
127
|
+
const args = rest[0];
|
|
128
|
+
const id = this.#nextCommandId;
|
|
129
|
+
this.#nextCommandId = this.#nextCommandId + 1 & 65535;
|
|
130
|
+
const message = { id, command };
|
|
131
|
+
if (args !== void 0) {
|
|
132
|
+
message.args = args;
|
|
133
|
+
}
|
|
134
|
+
const { promise, resolver, rejecter } = createPromise();
|
|
135
|
+
this.#pendingCommands.set(id, { resolver, rejecter });
|
|
136
|
+
this.#client.send(JSON.stringify(message), (err) => {
|
|
137
|
+
if (err) {
|
|
138
|
+
this.#pendingCommands.delete(id);
|
|
139
|
+
rejecter(new Error(`Failed to send command ${command}: ${err.message}`));
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return withTimeout(COMMAND_TIMEOUT, promise, () => this.#pendingCommands.delete(id));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Send a binary frame to the BLE proxy client.
|
|
146
|
+
*/
|
|
147
|
+
sendBinaryFrame(opcode, connectionHandle, payload) {
|
|
148
|
+
if (!this.connected || !this.#client) {
|
|
149
|
+
throw new Error("BLE proxy client not connected");
|
|
150
|
+
}
|
|
151
|
+
const frame = encodeBinaryFrame(opcode, connectionHandle, payload);
|
|
152
|
+
this.#client.send(frame);
|
|
153
|
+
}
|
|
154
|
+
#handleTextMessage(raw, handshakeTimer) {
|
|
155
|
+
let parsed;
|
|
156
|
+
try {
|
|
157
|
+
parsed = JSON.parse(raw);
|
|
158
|
+
} catch {
|
|
159
|
+
logger.warn("Received invalid JSON from BLE proxy client");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const msg = parsed;
|
|
163
|
+
if (!this.#handshakeComplete) {
|
|
164
|
+
this.#handleHandshake(msg, handshakeTimer);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if ("id" in msg && "success" in msg) {
|
|
168
|
+
this.#handleResponse(msg);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if ("event" in msg && "data" in msg) {
|
|
172
|
+
this.#handleEvent(msg);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
logger.warn("Received unknown message from BLE proxy client:", msg);
|
|
176
|
+
}
|
|
177
|
+
#handleHandshake(msg, handshakeTimer) {
|
|
178
|
+
if (msg.type !== "hello") {
|
|
179
|
+
logger.warn(`Expected hello message, got: ${JSON.stringify(msg)}`);
|
|
180
|
+
this.#client?.close(4002, "Expected hello message");
|
|
181
|
+
this.#cleanupClient();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const hello = msg;
|
|
185
|
+
handshakeTimer.stop();
|
|
186
|
+
if (hello.version !== BLE_PROXY_PROTOCOL_VERSION) {
|
|
187
|
+
logger.warn(
|
|
188
|
+
`BLE proxy client version ${hello.version} not supported (server supports ${BLE_PROXY_PROTOCOL_VERSION})`
|
|
189
|
+
);
|
|
190
|
+
this.#client?.send(
|
|
191
|
+
JSON.stringify({
|
|
192
|
+
type: "hello_response",
|
|
193
|
+
version: BLE_PROXY_PROTOCOL_VERSION,
|
|
194
|
+
error: "unsupported_version",
|
|
195
|
+
message: `Server supports protocol version ${BLE_PROXY_PROTOCOL_VERSION}, client sent version ${hello.version}`
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
this.#client?.close(4003, "Unsupported protocol version");
|
|
199
|
+
this.#cleanupClient();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.#handshakeComplete = true;
|
|
203
|
+
this.#client?.send(
|
|
204
|
+
JSON.stringify({
|
|
205
|
+
type: "hello_response",
|
|
206
|
+
version: BLE_PROXY_PROTOCOL_VERSION
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
logger.info(`BLE proxy handshake complete (version ${BLE_PROXY_PROTOCOL_VERSION})`);
|
|
210
|
+
}
|
|
211
|
+
#handleResponse(msg) {
|
|
212
|
+
const pending = this.#pendingCommands.get(msg.id);
|
|
213
|
+
if (!pending) {
|
|
214
|
+
logger.warn(`Received response for unknown command id ${msg.id}`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.#pendingCommands.delete(msg.id);
|
|
218
|
+
if (msg.success) {
|
|
219
|
+
pending.resolver(msg.result);
|
|
220
|
+
} else {
|
|
221
|
+
pending.rejecter(new Error(`${msg.error}: ${msg.message}`));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
#handleEvent(msg) {
|
|
225
|
+
this.eventReceived.emit(msg.event, msg.data);
|
|
226
|
+
}
|
|
227
|
+
#handleBinaryMessage(data) {
|
|
228
|
+
if (!this.#handshakeComplete) {
|
|
229
|
+
logger.warn("Received binary frame before handshake");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const frame = decodeBinaryFrame(new Uint8Array(data));
|
|
234
|
+
this.binaryFrameReceived.emit(frame);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
logger.error("Failed to decode binary frame:", err);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
#cleanupClient() {
|
|
240
|
+
if (this.#client) {
|
|
241
|
+
if (this.#client.readyState === WebSocket.OPEN) {
|
|
242
|
+
this.#client.close();
|
|
243
|
+
}
|
|
244
|
+
this.#client = void 0;
|
|
245
|
+
}
|
|
246
|
+
this.#handshakeComplete = false;
|
|
247
|
+
for (const [id, pending] of this.#pendingCommands) {
|
|
248
|
+
pending.rejecter(new Error("BLE proxy client disconnected"));
|
|
249
|
+
this.#pendingCommands.delete(id);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
BleProxyHandler
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=BleProxyHandler.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/BleProxyHandler.ts"],
|
|
4
|
+
"mappings": "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",
|
|
5
|
+
"names": []
|
|
6
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* BLE Proxy Protocol constants, types, and codec.
|
|
8
|
+
* See docs/ble-proxy-protocol.md for the full specification.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BLE_PROXY_PROTOCOL_VERSION = 1;
|
|
11
|
+
export interface HelloMessage {
|
|
12
|
+
type: "hello";
|
|
13
|
+
version: number;
|
|
14
|
+
}
|
|
15
|
+
export interface HelloResponseMessage {
|
|
16
|
+
type: "hello_response";
|
|
17
|
+
version: number;
|
|
18
|
+
error?: string;
|
|
19
|
+
message?: string;
|
|
20
|
+
}
|
|
21
|
+
export type HandshakeMessage = HelloMessage | HelloResponseMessage;
|
|
22
|
+
export declare const BleProxyCommand: {
|
|
23
|
+
readonly StartScan: "start_scan";
|
|
24
|
+
readonly StopScan: "stop_scan";
|
|
25
|
+
readonly Connect: "connect";
|
|
26
|
+
readonly Disconnect: "disconnect";
|
|
27
|
+
readonly DiscoverServices: "discover_services";
|
|
28
|
+
readonly DiscoverCharacteristics: "discover_characteristics";
|
|
29
|
+
readonly ReadCharacteristic: "read_characteristic";
|
|
30
|
+
readonly WriteCharacteristic: "write_characteristic";
|
|
31
|
+
readonly SubscribeCharacteristic: "subscribe_characteristic";
|
|
32
|
+
readonly UnsubscribeCharacteristic: "unsubscribe_characteristic";
|
|
33
|
+
readonly RequestMtu: "request_mtu";
|
|
34
|
+
};
|
|
35
|
+
export type BleProxyCommandName = (typeof BleProxyCommand)[keyof typeof BleProxyCommand];
|
|
36
|
+
export declare const BleProxyEvent: {
|
|
37
|
+
readonly DeviceDiscovered: "device_discovered";
|
|
38
|
+
readonly Disconnected: "disconnected";
|
|
39
|
+
readonly ScanStopped: "scan_stopped";
|
|
40
|
+
readonly CharacteristicNotification: "characteristic_notification";
|
|
41
|
+
};
|
|
42
|
+
export type BleProxyEventName = (typeof BleProxyEvent)[keyof typeof BleProxyEvent];
|
|
43
|
+
export interface StartScanArgs {
|
|
44
|
+
service_uuids?: string[];
|
|
45
|
+
allow_duplicates?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface ConnectArgs {
|
|
48
|
+
address: string;
|
|
49
|
+
timeout?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface DisconnectArgs {
|
|
52
|
+
connection_handle: number;
|
|
53
|
+
}
|
|
54
|
+
export interface DiscoverServicesArgs {
|
|
55
|
+
connection_handle: number;
|
|
56
|
+
}
|
|
57
|
+
export interface DiscoverCharacteristicsArgs {
|
|
58
|
+
connection_handle: number;
|
|
59
|
+
service_uuid: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ReadCharacteristicArgs {
|
|
62
|
+
connection_handle: number;
|
|
63
|
+
characteristic_uuid: string;
|
|
64
|
+
}
|
|
65
|
+
export interface WriteCharacteristicArgs {
|
|
66
|
+
connection_handle: number;
|
|
67
|
+
characteristic_uuid: string;
|
|
68
|
+
value: string;
|
|
69
|
+
response?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface SubscribeCharacteristicArgs {
|
|
72
|
+
connection_handle: number;
|
|
73
|
+
characteristic_uuid: string;
|
|
74
|
+
}
|
|
75
|
+
export interface UnsubscribeCharacteristicArgs {
|
|
76
|
+
connection_handle: number;
|
|
77
|
+
characteristic_uuid: string;
|
|
78
|
+
}
|
|
79
|
+
export interface RequestMtuArgs {
|
|
80
|
+
connection_handle: number;
|
|
81
|
+
mtu: number;
|
|
82
|
+
}
|
|
83
|
+
export interface ConnectResult {
|
|
84
|
+
connection_handle: number;
|
|
85
|
+
mtu: number;
|
|
86
|
+
}
|
|
87
|
+
export interface DiscoverServicesResult {
|
|
88
|
+
services: Array<{
|
|
89
|
+
uuid: string;
|
|
90
|
+
}>;
|
|
91
|
+
}
|
|
92
|
+
export interface DiscoverCharacteristicsResult {
|
|
93
|
+
characteristics: Array<{
|
|
94
|
+
uuid: string;
|
|
95
|
+
properties: string[];
|
|
96
|
+
}>;
|
|
97
|
+
}
|
|
98
|
+
export interface ReadCharacteristicResult {
|
|
99
|
+
value: string;
|
|
100
|
+
}
|
|
101
|
+
export interface RequestMtuResult {
|
|
102
|
+
mtu: number;
|
|
103
|
+
}
|
|
104
|
+
export interface DeviceDiscoveredData {
|
|
105
|
+
address: string;
|
|
106
|
+
name?: string;
|
|
107
|
+
rssi?: number;
|
|
108
|
+
connectable: boolean;
|
|
109
|
+
service_data?: Record<string, string>;
|
|
110
|
+
manufacturer_data?: Record<string, string>;
|
|
111
|
+
service_uuids?: string[];
|
|
112
|
+
}
|
|
113
|
+
export interface DisconnectedData {
|
|
114
|
+
connection_handle: number;
|
|
115
|
+
reason?: string;
|
|
116
|
+
}
|
|
117
|
+
export interface ScanStoppedData {
|
|
118
|
+
reason: string;
|
|
119
|
+
}
|
|
120
|
+
export interface CharacteristicNotificationData {
|
|
121
|
+
connection_handle: number;
|
|
122
|
+
characteristic_uuid: string;
|
|
123
|
+
value: string;
|
|
124
|
+
}
|
|
125
|
+
/** Maps each command name to its args and result types for type-safe sendCommand. */
|
|
126
|
+
export interface BleProxyCommandMap {
|
|
127
|
+
[BleProxyCommand.StartScan]: {
|
|
128
|
+
args: StartScanArgs;
|
|
129
|
+
result: void;
|
|
130
|
+
};
|
|
131
|
+
[BleProxyCommand.StopScan]: {
|
|
132
|
+
args: undefined;
|
|
133
|
+
result: void;
|
|
134
|
+
};
|
|
135
|
+
[BleProxyCommand.Connect]: {
|
|
136
|
+
args: ConnectArgs;
|
|
137
|
+
result: ConnectResult;
|
|
138
|
+
};
|
|
139
|
+
[BleProxyCommand.Disconnect]: {
|
|
140
|
+
args: DisconnectArgs;
|
|
141
|
+
result: void;
|
|
142
|
+
};
|
|
143
|
+
[BleProxyCommand.DiscoverServices]: {
|
|
144
|
+
args: DiscoverServicesArgs;
|
|
145
|
+
result: DiscoverServicesResult;
|
|
146
|
+
};
|
|
147
|
+
[BleProxyCommand.DiscoverCharacteristics]: {
|
|
148
|
+
args: DiscoverCharacteristicsArgs;
|
|
149
|
+
result: DiscoverCharacteristicsResult;
|
|
150
|
+
};
|
|
151
|
+
[BleProxyCommand.ReadCharacteristic]: {
|
|
152
|
+
args: ReadCharacteristicArgs;
|
|
153
|
+
result: ReadCharacteristicResult;
|
|
154
|
+
};
|
|
155
|
+
[BleProxyCommand.WriteCharacteristic]: {
|
|
156
|
+
args: WriteCharacteristicArgs;
|
|
157
|
+
result: void;
|
|
158
|
+
};
|
|
159
|
+
[BleProxyCommand.SubscribeCharacteristic]: {
|
|
160
|
+
args: SubscribeCharacteristicArgs;
|
|
161
|
+
result: void;
|
|
162
|
+
};
|
|
163
|
+
[BleProxyCommand.UnsubscribeCharacteristic]: {
|
|
164
|
+
args: UnsubscribeCharacteristicArgs;
|
|
165
|
+
result: void;
|
|
166
|
+
};
|
|
167
|
+
[BleProxyCommand.RequestMtu]: {
|
|
168
|
+
args: RequestMtuArgs;
|
|
169
|
+
result: RequestMtuResult;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export interface CommandMessage {
|
|
173
|
+
id: number;
|
|
174
|
+
command: BleProxyCommandName;
|
|
175
|
+
args?: Record<string, unknown>;
|
|
176
|
+
}
|
|
177
|
+
export interface SuccessResponseMessage {
|
|
178
|
+
id: number;
|
|
179
|
+
success: true;
|
|
180
|
+
result?: Record<string, unknown>;
|
|
181
|
+
}
|
|
182
|
+
export interface ErrorResponseMessage {
|
|
183
|
+
id: number;
|
|
184
|
+
success: false;
|
|
185
|
+
error: string;
|
|
186
|
+
message: string;
|
|
187
|
+
}
|
|
188
|
+
export type ResponseMessage = SuccessResponseMessage | ErrorResponseMessage;
|
|
189
|
+
export interface EventMessage {
|
|
190
|
+
event: BleProxyEventName;
|
|
191
|
+
data: Record<string, unknown>;
|
|
192
|
+
}
|
|
193
|
+
export declare const BleProxyErrorCode: {
|
|
194
|
+
readonly BluetoothUnavailable: "bluetooth_unavailable";
|
|
195
|
+
readonly AlreadyScanning: "already_scanning";
|
|
196
|
+
readonly NotScanning: "not_scanning";
|
|
197
|
+
readonly DeviceNotFound: "device_not_found";
|
|
198
|
+
readonly ConnectionFailed: "connection_failed";
|
|
199
|
+
readonly AlreadyConnected: "already_connected";
|
|
200
|
+
readonly NotConnected: "not_connected";
|
|
201
|
+
readonly Timeout: "timeout";
|
|
202
|
+
readonly ServiceNotFound: "service_not_found";
|
|
203
|
+
readonly CharacteristicNotFound: "characteristic_not_found";
|
|
204
|
+
readonly ReadFailed: "read_failed";
|
|
205
|
+
readonly WriteFailed: "write_failed";
|
|
206
|
+
readonly SubscribeFailed: "subscribe_failed";
|
|
207
|
+
readonly NotSubscribed: "not_subscribed";
|
|
208
|
+
readonly NotifyNotSupported: "notify_not_supported";
|
|
209
|
+
readonly MtuRequestFailed: "mtu_request_failed";
|
|
210
|
+
readonly DiscoveryFailed: "discovery_failed";
|
|
211
|
+
readonly InternalError: "internal_error";
|
|
212
|
+
};
|
|
213
|
+
export type BleProxyErrorCodeValue = (typeof BleProxyErrorCode)[keyof typeof BleProxyErrorCode];
|
|
214
|
+
export declare const BinaryFrameOpcode: {
|
|
215
|
+
/** Server -> Client: write payload to the active write characteristic */
|
|
216
|
+
readonly WriteData: 1;
|
|
217
|
+
/** Client -> Server: notification data from a subscribed characteristic */
|
|
218
|
+
readonly Notification: 2;
|
|
219
|
+
/** Client -> Server: response data for a read_characteristic command */
|
|
220
|
+
readonly ReadResponse: 3;
|
|
221
|
+
};
|
|
222
|
+
export type BinaryFrameOpcodeValue = (typeof BinaryFrameOpcode)[keyof typeof BinaryFrameOpcode];
|
|
223
|
+
export interface BinaryFrame {
|
|
224
|
+
opcode: number;
|
|
225
|
+
connectionHandle: number;
|
|
226
|
+
payload: Uint8Array;
|
|
227
|
+
}
|
|
228
|
+
export declare function encodeBinaryFrame(opcode: number, connectionHandle: number, payload: Uint8Array): Uint8Array;
|
|
229
|
+
export declare function decodeBinaryFrame(data: Uint8Array): BinaryFrame;
|
|
230
|
+
//# sourceMappingURL=BleProxyProtocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BleProxyProtocol.d.ts","sourceRoot":"","sources":["../../src/BleProxyProtocol.ts"],"names":[],"mappings":"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"}
|