The main WebSocket bridge server class.
import { BridgeServer } from 'ble-mcp-test';
const server = new BridgeServer();
server.start(8080); // Start on port 8080
// Later...
server.stop(); // Graceful shutdownstart(port?: number)- Start the WebSocket server (default port: 8080)stop()- Stop the server and close all connections
Replaces the browser's navigator.bluetooth with a mock that communicates with the bridge server.
import { injectWebBluetoothMock } from 'ble-mcp-test';
// Basic usage
injectWebBluetoothMock('ws://localhost:8080');
// With device configuration
const url = new URL('ws://localhost:8080');
url.searchParams.set('device', 'MyDevice');
url.searchParams.set('service', '180f');
url.searchParams.set('write', '2a19');
url.searchParams.set('notify', '2a20');
injectWebBluetoothMock(url.toString());If you're not using a module bundler, include the pre-built bundle:
<script src="path/to/web-ble-mock.bundle.js"></script>
<script>
// Global WebBleMock object is available
WebBleMock.injectWebBluetoothMock('ws://localhost:8080');
</script>Pass device configuration via URL query parameters:
| Parameter | Description | Example |
|---|---|---|
device |
Device name prefix to search for | CS108 |
service |
BLE service UUID | 9800 or 00009800-0000-1000-8000-00805f9b34fb |
write |
Write characteristic UUID | 9900 |
notify |
Notify characteristic UUID | 9901 |
All messages are JSON objects with a type field.
Send data to BLE device:
{
"type": "data",
"data": [0xA7, 0xB3, 0x02, 0xD9, 0x82, 0x37, 0x00, 0x00, 0xA0, 0x00]
}Device connected:
{
"type": "connected",
"device": "CS108Reader2603A7"
}Data received from device:
{
"type": "data",
"data": [0xA7, 0xB3, 0x04, 0xD9, 0x82, 0x9E, 0xF7, 0xDD, 0xA0, 0x00, 0x0F, 0xF0]
}Error occurred:
{
"type": "error",
"error": "No device found"
}Device disconnected:
{
"type": "disconnected"
}The mock implements the following Web Bluetooth API methods:
- Supports
filterswithnamePrefix - Supports
optionalServices - Returns a
BluetoothDeviceobject
name- Device namegatt- GATT server interface
connect()- Connect to devicedisconnect()- Disconnect from devicegetPrimaryService(uuid)- Get a service
getCharacteristic(uuid)- Get a characteristic
writeValue(data)- Write data to characteristicstartNotifications()- Enable notificationsaddEventListener('characteristicvaluechanged', handler)- Listen for notifications
The bridge currently supports one BLE connection at a time. If a WebSocket client tries to connect while another client has an active BLE connection, it will receive an error:
{
"type": "error",
"error": "Another connection is active"
}This is by design to prevent race conditions and ensure reliable operation. See the Roadmap for planned multi-device support.
The bridge provides clear error messages:
"No device found"- No BLE device matching the criteria was found"Missing required parameters"- URL parameters are incomplete"Another connection is active"- Bridge is already connected to a device"Failed to connect to WebSocket server"- Can't reach the bridge server
import { test } from '@playwright/test';
test('communicate with BLE device', async ({ page }) => {
// Load your application
await page.goto('http://localhost:3000');
// Inject the mock
await page.addScriptTag({
path: 'node_modules/ble-mcp-test/dist/web-ble-mock.bundle.js'
});
// Configure and initialize
await page.evaluate(() => {
const url = new URL('ws://localhost:8080');
url.searchParams.set('device', 'CS108');
url.searchParams.set('service', '9800');
url.searchParams.set('write', '9900');
url.searchParams.set('notify', '9901');
WebBleMock.injectWebBluetoothMock(url.toString());
});
// Now your app can use navigator.bluetooth normally!
await page.click('#connect-button');
await page.waitForSelector('#connected-status');
});The bridge server exports several utility functions for working with BLE data and logging:
Formats binary data as uppercase hexadecimal with space separation.
import { formatHex } from 'ble-mcp-test';
const data = new Uint8Array([0xA7, 0xB3, 0xC2, 0x01]);
console.log(formatHex(data)); // "A7 B3 C2 01"
const buffer = Buffer.from([0x12, 0x34, 0x56, 0x78]);
console.log(formatHex(buffer)); // "12 34 56 78"Normalizes log level strings to a valid LogLevel type, with support for common aliases.
import { normalizeLogLevel } from 'ble-mcp-test';
normalizeLogLevel('debug'); // 'debug'
normalizeLogLevel('verbose'); // 'debug' (alias)
normalizeLogLevel('trace'); // 'debug' (alias)
normalizeLogLevel('info'); // 'info'
normalizeLogLevel('warn'); // 'info' (mapped to info)
normalizeLogLevel('warning'); // 'info' (alias)
normalizeLogLevel('error'); // 'error'
normalizeLogLevel(undefined); // 'debug' (default)
normalizeLogLevel('invalid'); // 'debug' (with console warning)Type definition for valid log levels:
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';import { BridgeServer, normalizeLogLevel } from 'ble-mcp-test';
const logLevel = normalizeLogLevel(process.env.LOG_LEVEL);
const server = new BridgeServer(logLevel);
await server.start();
// At debug level, you'll see [TX]/[RX] bytestream logs:
// [TX] A7 B3 C2 01 00 00 A0 00 B3 A7
// [RX] B3 A7 C2 01 00 00 00 00 A7 B3