# ![noble](assets/noble-logo.png)

[![npm version](https://badgen.net/npm/v/@stoprocent/noble)](https://www.npmjs.com/package/@stoprocent/noble)
[![npm downloads](https://badgen.net/npm/dt/@stoprocent/noble)](https://www.npmjs.com/package/@stoprocent/noble)


A Node.js BLE (Bluetooth Low Energy) central module.

Want to implement a peripheral? Check out [@stoprocent/bleno](https://github.com/stoprocent/bleno).

> **Note:** Currently, running both noble (central) and bleno (peripheral) together only works with macOS bindings or when using separate HCI/UART dongles. Support for running both on a single HCI adapter (e.g., on Linux systems) will be added in future releases.

## About This Fork

This fork of `noble` was created to introduce several key improvements and new features:

1. **Flexible Bluetooth Driver Selection**:
   - This library enables flexible selection of Bluetooth drivers through the new `withBindings()` API. Use native platform bindings (Mac, Windows) or HCI bindings with UART/serial support for hardware dongles, allowing Bluetooth connectivity across various platforms and hardware setups.
   
2. **Native Bindings Improvements**: 
   - Fixed and optimized native bindings for macOS, ensuring better compatibility and performance on Apple devices
   - Overhauled Windows native bindings with support for `Service Data` from advertisements
   - Aligned behavior across different bindings (macOS, Windows, HCI) for consistent behavior 

3. **Modern JavaScript Support**:
   - Added full Promise-based API with async/await support throughout the library
   - Implemented async iterators for device discovery with `for await...of` syntax
   - Refactored codebase to use modern JavaScript patterns and best practices

4. **Enhanced Testing and Reliability**:
   - Migrated tests to Jest for improved coverage and reliability
   - Added comprehensive TypeScript type definitions
   - Fixed numerous edge cases and stability issues

5. **New Features**: 
   - A `setAddress(...)` function to set the MAC address of the central device
   - Direct device connection with `connect(...)/connectAsync(...)` without requiring a prior scan
   - `waitForPoweredOnAsync(...)` function to simplify async workflows
   - Support for multiple adapter configurations through the new `withBindings()` API
   - Extended debugging capabilities and error handling
   - Additionally, I plan to add raw L2CAP channel support, enhancing low-level Bluetooth communication capabilities


If you appreciate these enhancements and the continued development of this project, please consider supporting my work. 

[![Buy me a coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/stoprocent)

## Install

```sh
npm install @stoprocent/noble
```

## Usage

### TypeScript (Recommended)

```typescript
// Auto-select based on platform
import noble from '@stoprocent/noble';
// or
import { withBindings } from '@stoprocent/noble';
// Auto-select based on platform
const noble = withBindings('default'); // 'hci', 'win', 'mac'
```

For more detailed examples and API documentation, see [Binding Types](#Binding-Types) below.

### JavaScript

```javascript
const noble = require('@stoprocent/noble');
// or 
const { withBindings } = require('@stoprocent/noble');
const noble = withBindings('default'); // 'hci', 'win', 'mac'
```


## Quick Start Example

### TypeScript Example (Modern Async/Await)

#### Basic Scan

```typescript
import noble from '@stoprocent/noble';

// Discover peripherals as an async generator
try {
  // Wait for Adapter poweredOn state
  await noble.waitForPoweredOnAsync();

  // discoverAsync starts and owns the scanning session
  for await (const peripheral of noble.discoverAsync()) {
    console.log(`Found device: ${peripheral.advertisement.localName || 'Unknown'}`);
    // Process the peripheral as needed
    
    // Optional: stop scanning when a specific device is found
    if (peripheral.advertisement.localName === 'MyDevice') {
      break;
    }
  }
  
} catch (error) {
  console.error('Discovery error:', error);
  await noble.stopScanningAsync();
}
```

`discoverAsync()` resumes scanning after temporary binding-level pauses, such
as the Linux HCI binding stopping scan while it connects. Break the loop or
call `stopScanning()` / `stopScanningAsync()` to end the discovery session.

For a more detailed example, please check out [examples/peripheral-explorer.ts](examples/peripheral-explorer.ts)

Alternatively, you can still use the legacy event-based API:

``` javascript
const noble = require('@stoprocent/noble');

// State change event is emitted when adapter state changes
noble.on('stateChange', function (state) {
  if (state === 'poweredOn') {
    // Start scanning when adapter is ready
    noble.startScanning();
  } else {
    // Stop scanning if adapter becomes unavailable
    noble.stopScanning();
  }
});

// Discover event is emitted when a peripheral is found
noble.on('discover', peripheral => {
  console.log(peripheral);
  // From here you can work with the peripheral:
  // - Connect to it: peripheral.connect()
  // - Check advertisement data: peripheral.advertisement
  // - See signal strength: peripheral.rssi
});
```

#### Connecting to the device

``` typescript
// Stop scan
await noble.stopScanningAsync();
// Connect
await peripheral.connectAsync();
// Discover
const { services, characteristics } = await peripheral.discoverAllServicesAndCharacteristicsAsync();
```

#### Working with Services and Characteristics

```typescript
async function exploreServices(peripheral) {
  // Discover all services and characteristics at once
  const { services } = await peripheral.discoverAllServicesAndCharacteristicsAsync();
  
  const results = [];
  
  for (const service of services) {
    const serviceInfo = {
      uuid: service.uuid,
      characteristics: []
    };
    
    for (const characteristic of service.characteristics) {
      const characteristicInfo = {
        uuid: characteristic.uuid,
        properties: characteristic.properties
      };
      
      // Read the characteristic if it's readable
      if (characteristic.properties.includes('read')) {
        characteristicInfo.value = await characteristic.readAsync();
      }
      
      serviceInfo.characteristics.push(characteristicInfo);
    }
    
    results.push(serviceInfo);
  }
  
  return results;
}
```

#### Reading and Writing Data

```typescript
async function readBatteryLevel(peripheral) {
  // Get battery service (0x180F is the standard UUID for Battery Service)
  const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(
    ['180f'], // Battery Service
    ['2a19']  // Battery Level Characteristic
  );
  
  if (characteristics.length > 0) {
    const data = await characteristics[0].readAsync();
    return data[0]; // Battery percentage
  }
  
  return null;
}

async function writeCharacteristic(peripheral, serviceUuid, characteristicUuid, data) {
  const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(
    [serviceUuid], 
    [characteristicUuid]
  );
  
  if (characteristics.length > 0) {
    // false = with response, true = without response
    const requiresResponse = !characteristics[0].properties.includes('writeWithoutResponse');
    await characteristics[0].writeAsync(data, !requiresResponse);
    return true;
  }
  
  return false;
}
```

### JavaScript Example (Battery Level)

```javascript
const { withBindings } = require('@stoprocent/noble');

// Read the battery level of the first found peripheral exposing the Battery Level characteristic
async function readBatteryLevel() {
  const noble = withBindings('default');

  try {
    await noble.waitForPoweredOnAsync();
    await noble.startScanningAsync(['180f'], false);
    
    noble.on('discover', async (peripheral) => {
      await noble.stopScanningAsync();
      await peripheral.connectAsync();
      
      const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(['180f'], ['2a19']);
      const batteryLevel = (await characteristics[0].readAsync())[0];

      console.log(`${peripheral.address} (${peripheral.advertisement.localName}): ${batteryLevel}%`);

      await peripheral.disconnectAsync();
      process.exit(0);
    });
  } catch (error) {
    console.error(error);
  }
}

readBatteryLevel();
```

## API Overview

Noble provides both callback-based and Promise-based (Async) APIs:

### Binding Types

```typescript
// Default binding (automatically selects based on platform)
import noble from '@stoprocent/noble';
// or
import { withBindings } from '@stoprocent/noble';
const noble = withBindings('default');

// Specific bindings
const nobleHci = withBindings('hci');   // HCI socket binding
const nobleDbus = withBindings('dbus'); // BlueZ D-Bus binding (Linux desktop)
const nobleMac = withBindings('mac');   // macOS binding
const nobleWin = withBindings('win');   // Windows binding

// Custom options for HCI binding (Using UART HCI Dongle)
const nobleCustom = withBindings('hci', { 
  hciDriver: 'uart',
  bindParams: {
    uart: {
      port: '/dev/ttyUSB0',
      baudRate: 1000000
    }
  }
});

// Custom options for HCI binding (Native)
const nobleCustom = withBindings('hci', { 
  hciDriver: 'native',
  deviceId: 0 // This could be also set by env.NOBLE_HCI_DEVICE_ID=0
});

// D-Bus / BlueZ binding (Linux desktop). Talks to bluetoothd over org.bluez,
// so it coexists with the system Bluetooth stack and does not need root /
// CAP_NET_ADMIN. Requires the `dbus-next` package to be installed in the
// host project — it is not bundled, since it is only useful on Linux:
//
//   npm install dbus-next
//
// Supports basic GATT: scan, connect, service/characteristic/descriptor
// discovery, read, write, notify/indicate. Does not support raw HCI handle
// I/O, custom scan parameters, or vendor-specific commands.
const nobleDbus = withBindings('dbus', {
  adapterId: 'hci0' // optional; defaults to the first BlueZ adapter
});

// Equivalent to withBindings('dbus') without code changes:
//   NOBLE_BINDINGS=dbus node app.js
```

### Core Methods

```typescript
// Wait for adapter to be powered on
await noble.waitForPoweredOnAsync(timeout?: number);

// Start scanning
await noble.startScanningAsync(serviceUUIDs?: string[], allowDuplicates?: boolean);

// Stop scanning
await noble.stopScanningAsync();

// Discover peripherals as an async generator. This starts scanning and
// resumes temporary binding-level scan pauses until the loop is stopped.
for await (const peripheral of noble.discoverAsync()) {
  // handle each discovered peripheral
}

// Connect directly to a peripheral by ID or address
const peripheral = await noble.connectAsync(idOrAddress, options?);

// Pair with a peripheral by ID or address (Windows only — see "Pairing" below)
await noble.pairAsync(idOrAddress);

// Set adapter address (HCI only on supported devices)
noble.setAddress('00:11:22:33:44:55');

// Reset adapter
noble.reset();

// Stop noble
noble.stop();
```

### Peripheral Methods

```typescript
// Connect to peripheral
await peripheral.connectAsync();

// Disconnect from peripheral
await peripheral.disconnectAsync();

// Pair with peripheral (Windows only — see "Pairing" below)
await peripheral.pairAsync();

// Update RSSI
const rssi = await peripheral.updateRssiAsync();

// Discover services
const services = await peripheral.discoverServicesAsync(['180f']); // Optional service UUIDs

// Discover all services and characteristics
const { services, characteristics } = await peripheral.discoverAllServicesAndCharacteristicsAsync();

// Discover specific services and characteristics
const { services, characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(
  ['180f'], ['2a19']
);

// Read and write handles
const data = await peripheral.readHandleAsync(handle);
await peripheral.writeHandleAsync(handle, data, withoutResponse);
```

The Linux HCI binding reports controller disconnect reasons as numeric HCI
status codes. Other bindings and library cleanup paths may report a string.
Use `hciStatusMessage` when a human-readable HCI message is needed:

```typescript
import noble, { hciStatusMessage } from '@stoprocent/noble';

peripheral.on('disconnect', reason => {
  const message = typeof reason === 'number'
    ? hciStatusMessage(reason)
    : reason;
  console.log(`Disconnected: ${message}`);
});
```

### Pairing

```typescript
import noble, {
  DevicePairingKinds,
  DevicePairingProtectionLevel,
} from '@stoprocent/noble';

// Pair at the Noble level (by ID or address) or on a Peripheral instance.
await noble.pairAsync(idOrAddress);
// or
await peripheral.pairAsync();

// Optionally choose pairing ceremony + protection level (Windows only).
await noble.pairAsync(
  idOrAddress,
  DevicePairingKinds.ConfirmOnly,
  DevicePairingProtectionLevel.EncryptionAndAuthentication
);

// Callback form is also available on both.
noble.pair(idOrAddress, error => { /* ... */ });
peripheral.pair(error => { /* ... */ });

// The pairing outcome is emitted on the peripheral, and (subject, error)
// on Noble, mirroring other events.
peripheral.on('pair', error => { /* error is null on success */ });
noble.on('pair', (peripheral, error) => { /* ... */ });
```

**Platform support and limitations — please read before using pairing:**

- **Windows only.** Pairing is implemented solely by the Windows (WinRT)
  binding. On macOS, Linux, and other bindings `pair`/`pairAsync` does not
  attempt a native call — it rejects/returns a deterministic
  `'Pairing is not supported on this platform'` error so callers do not hang.
- **A connected device is required.** The peripheral must already be
  discovered and connected (present in Noble's peripheral map) before pairing
  is requested. Pairing an unknown/untracked id fails rather than hanging.
- **Windows pairing options.** `kind` defaults to `ConfirmOnly` and
  `protectionLevel` defaults to `Encryption`. You can pass a
  `DevicePairingKinds` bitmask (single or OR-ed values) and (optionally)
  `DevicePairingProtectionLevel`.
  `ConfirmOnly`, `DisplayPin`, and `ConfirmPinMatch` use Windows' native UI;
  PIN/password kinds (`ProvidePin`, `ProvidePassword`, `ConfirmPassword`) are
  rejected because this library does not collect secrets to pass into WinRT.

### Service Methods

```typescript
// Discover included services
const includedServiceUuids = await service.discoverIncludedServicesAsync([serviceUUIDs]);

// Discover characteristics
const characteristics = await service.discoverCharacteristicsAsync([characteristicUUIDs]);
```

### Characteristic Methods

> **Note:** The `data` event is the primary event for handling both read responses and notifications. When using the event-based approach, you can differentiate between read responses and notifications using the `isNotification` parameter. The previously used `read` event **has been deprecated and removed**. Instead, use the `data` event with `isNotification=false` to identify read responses.

```typescript
// Read characteristic value
const data = await characteristic.readAsync();

// Write characteristic value
await characteristic.writeAsync(data, withoutResponse);

// Subscribe to notifications
await characteristic.subscribeAsync();

// Unsubscribe from notifications
await characteristic.unsubscribeAsync();

// Receive notifications using async iterator
for await (const data of characteristic.notificationsAsync()) {
  console.log(`Received notification: ${data}`);
}

// Discover descriptors
const descriptors = await characteristic.discoverDescriptorsAsync();
```

### Characteristic Events

```typescript
// Receive data (both read responses and notifications)
characteristic.on('data', (data: Buffer, isNotification: boolean) => {
  console.log(`Received ${isNotification ? 'notification' : 'read response'}: ${data}`);
});

// Write completion 
characteristic.on('write', (error: Error | undefined) => {
  console.log('Write completed');
});

// Descriptor discovery
characteristic.on('descriptorsDiscover', (descriptors: Descriptor[]) => {
  console.log('Descriptors discovered');
});
```

### Descriptor Methods

```typescript
// Read descriptor value
const value = await descriptor.readValueAsync();

// Write descriptor value
await descriptor.writeValueAsync(data);
```

## Installation

* [Prerequisites](#prerequisites)
  * [UART](#uart)
  * [OS X](#os-x)
  * [Linux](#linux)
    * [Ubuntu, Debian, Raspbian](#ubuntu-debian-raspbian)
    * [Fedora and other RPM-based distributions](#fedora-and-other-rpm-based-distributions)
    * [Intel Edison](#intel-edison)
  * [FreeBSD](#freebsd)
  * [Windows](#windows)
  * [Docker](#docker)
* [Installing and using the package](#installing-and-using-the-package)

### Prerequisites

#### UART (Any OS)

Please refer to [https://github.com/stoprocent/node-bluetooth-hci-socket#uartserial-any-os](https://github.com/stoprocent/node-bluetooth-hci-socket#uartserial-any-os)

__NOTE:__ While environmental variables are still supported for backward compatibility, the recommended approach is to specify driver options directly in the `withBindings()` call as shown below:

##### Recommended Approach (UART port specified in `bindParams`)

```typescript
import { withBindings } from '@stoprocent/noble';
const noble = withBindings('hci', { 
  hciDriver: 'uart',
  bindParams: {
    uart: {
      port: '/dev/ttyUSB0',
      baudRate: 1000000
    }
  }
});
```

##### Legacy Approach (Using environmental variables - not recommended for new implementations)

```bash
$ export BLUETOOTH_HCI_SOCKET_UART_PORT=/dev/tty...
$ export BLUETOOTH_HCI_SOCKET_UART_BAUDRATE=1000000
```

__NOTE:__ `BLUETOOTH_HCI_SOCKET_UART_BAUDRATE` defaults to `1000000` so only needed if different.

```typescript
import noble from '@stoprocent/noble';
```

#### OS X

 * Install [Xcode](https://itunes.apple.com/ca/app/xcode/id497799835?mt=12)
 * On newer versions of OSX, allow bluetooth access on the terminal app: "System Preferences" —> "Security & Privacy" —> "Bluetooth" -> Add terminal app (see [Sandboxed terminal](#sandboxed-terminal))

#### Linux

 * Kernel version 3.6 or above
 * `libbluetooth-dev` needs to be installed. For instructions for specific distributions, see below.
 * To set the necessary privileges to run without sudo, [see this section](#running-without-rootsudo-linux-specific). This is required for all distributions (Raspbian, Ubuntu, Fedora, etc). You will not get any errors if running without sudo, but nothing will happen.

##### Ubuntu, Debian, Raspbian

See the [generic Linux notes above](#linux) first.

```sh
sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
```

Make sure `node` is on your `PATH`. If it's not, some options:
 * Symlink `nodejs` to `node`: `sudo ln -s /usr/bin/nodejs /usr/bin/node`
 * [Install Node.js using the NodeSource package](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions)

If you are having trouble connecting to BLE devices on a Raspberry Pi, you should disable the `pnat` plugin. Add the following line at the bottom of `/etc/bluetooth/main.conf`:

```
DisablePlugins=pnat
```

Then restart the system.

See [Issue #425 · OpenWonderLabs/homebridge-switchbot](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/425#issuecomment-1190864279).

##### Fedora and other RPM-based distributions

See the [generic Linux notes above](#linux) first.

```sh
sudo yum install bluez bluez-libs bluez-libs-devel
```

##### Intel Edison

See the [generic Linux notes above](#linux) first.

See [Configure Intel Edison for Bluetooth LE (Smart) Development](http://rexstjohn.com/configure-intel-edison-for-bluetooth-le-smart-development/).

#### FreeBSD

Make sure you have GNU Make:

```sh
sudo pkg install gmake
```

Disable automatic loading of the default Bluetooth stack by putting [no-ubt.conf](https://gist.github.com/myfreeweb/44f4f3e791a057bc4f3619a166a03b87) into `/usr/local/etc/devd/no-ubt.conf` and restarting devd (`sudo service devd restart`).

Unload `ng_ubt` kernel module if already loaded:

```sh
sudo kldunload ng_ubt
```

Make sure you have read and write permissions on the `/dev/usb/*` device that corresponds to your Bluetooth adapter.

#### Windows

[node-gyp requirements for Windows](https://github.com/TooTallNate/node-gyp#installation)

Install the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) from an elevated PowerShell or cmd.exe (run as Administrator).

```cmd
npm install --global --production windows-build-tools
```

[node-bluetooth-hci-socket prerequisites](#windows)
   * Compatible Bluetooth 5.0 Zephyr HCI-USB adapter (you need to add BLUETOOTH_HCI_SOCKET_USB_VID and BLUETOOTH_HCI_SOCKET_USB_PID to the process env)
   * Compatible Bluetooth 4.0 USB adapter
   * [WinUSB](https://msdn.microsoft.com/en-ca/library/windows/hardware/ff540196(v=vs.85).aspx) driver setup for Bluetooth 4.0 USB adapter, using [Zadig tool](http://zadig.akeo.ie/)

See [@don](https://github.com/don)'s setup guide on [Bluetooth LE with Node.js and Noble on Windows](https://www.youtube.com/watch?v=mL9B8wuEdms&feature=youtu.be&t=1m46s)

#### Docker

Make sure your container runs with `--network=host` options and all specific environment prerequisites are verified.

### Choosing a Linux Bluetooth access mode

Noble supports three materially different Linux setups:

- **D-Bus binding:** `withBindings('dbus')` uses the system BlueZ service and
  is the best default when noble should coexist with desktop or system
  Bluetooth clients.
- **Raw HCI binding:** `withBindings('hci')` is the default on Linux. It shares
  the controller with the kernel Bluetooth stack and BlueZ. It requires raw
  socket privileges and gives noble direct access to HCI traffic.
- **HCI user channel:** `withBindings('hci', { userChannel: true })` gives noble
  exclusive controller access. Use it for a dedicated adapter when another
  Bluetooth stack must not operate that controller.

The user channel requires `CAP_NET_ADMIN` (running with `sudo` is the simplest
setup) and the adapter must be down before noble binds it:

```sh
sudo hciconfig hci1 down
sudo NOBLE_HCI_DEVICE_ID=1 HCI_CHANNEL_USER=1 node app.js
```

The equivalent code configuration is:

```typescript
const noble = withBindings('hci', {
  hciDriver: 'native',
  deviceId: 1,
  userChannel: true
});
```

Binding the user channel to an adapter that is still up fails with `EBUSY`.
Conversely, raw mode does not power up a down adapter; run
`sudo hciconfig hciX up` first when using raw mode.

When diagnosing raw-mode traffic with `btmon`, an unlabeled HCI command only
shows that the kernel sent it. It is not, by itself, proof that `bluetoothd`
started an unrelated operation: noble's native HCI dependency also uses kernel
L2CAP sockets for connection bookkeeping. Use a dedicated adapter with the
user channel when command ownership must be unambiguous.

### Running without root/sudo (Linux-specific)

Run the following command:

```sh
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)
```

This grants the `node` binary `cap_net_raw` privileges, so it can start/stop BLE advertising.

__Note:__ The above command requires `setcap` to be installed.
It can be installed the following way:

 * apt: `sudo apt-get install libcap2-bin`
 * yum: `su -c \'yum install libcap2-bin\'`

### Multiple Adapters (Linux-specific)

When no adapter is specified, raw mode selects the first adapter that is up,
while the user channel selects the first adapter that is down. Specify the
adapter explicitly whenever more than one is present.

You can specify which HCI adapter to use in two ways:

#### 1. Using `withBindings` (Recommended)

```typescript
import { withBindings } from '@stoprocent/noble';

// Specify HCI adapter in code
const noble = withBindings('hci', { 
  hciDriver: 'native',
  deviceId: 1 // Using hci1
});
```

#### 2. Using environment variable

To override using environment variables, set the `NOBLE_HCI_DEVICE_ID` environment variable to the interface number.

For example, to specify `hci1`:

```sh
sudo NOBLE_HCI_DEVICE_ID=1 node <your file>.js
```

If you are using multiple HCI devices in one setup you can run two instances of noble with different binding configurations by initializing them seperatly in code:

``` typescript
import { withBindings } from '@stoprocent/noble';

// Create two noble instances with different HCI adapters
const nobleAdapter0 = withBindings('hci', { 
  hciDriver: 'native',
  deviceId: 0 // Using hci0
});

const nobleAdapter1 = withBindings('hci', { 
  hciDriver: 'native',
  deviceId: 1 // Using hci1
});
```


### Reporting all HCI events (Linux-specific)

By default, noble waits for both the advertisement data and scan response data for each Bluetooth address. If your device does not use scan response, the `NOBLE_REPORT_ALL_HCI_EVENTS` environment variable can be used to bypass it.

```sh
sudo NOBLE_REPORT_ALL_HCI_EVENTS=1 node <your file>.js
```

## Environment Variables

The following environment variables can configure noble's behavior:

| Variable | Purpose | Default | Example |
|----------|---------|---------|---------|
| NOBLE_HCI_DEVICE_ID | Specify which HCI adapter to use | 0 | `export NOBLE_HCI_DEVICE_ID=1` |
| HCI_CHANNEL_USER | Use the exclusive Linux HCI user channel | false | `export HCI_CHANNEL_USER=1` |
| NOBLE_CODED_PHY | Offer LE Coded PHY for connections (long range, lower throughput; requires controller support) | false | `export NOBLE_CODED_PHY=1` |
| NOBLE_REPORT_ALL_HCI_EVENTS | Report HCI events without waiting for scan response | false | `export NOBLE_REPORT_ALL_HCI_EVENTS=1` |
| BLUETOOTH_HCI_SOCKET_UART_PORT | UART port for HCI communication | none | `export BLUETOOTH_HCI_SOCKET_UART_PORT=/dev/ttyUSB0` |
| BLUETOOTH_HCI_SOCKET_UART_BAUDRATE | UART baudrate | 1000000 | `export BLUETOOTH_HCI_SOCKET_UART_BAUDRATE=1000000` |

> **Note:** The preferred method for configuration is now using the `withBindings()` API rather than environment variables.
