# react-native-sdk-ble

A comprehensive React Native library for Bluetooth Low Energy (BLE) communication, supporting both iOS and Android platforms. This library provides a clean, promise-based API for scanning, connecting, and interacting with BLE devices.

## Features

- 🔍 **Device Scanning**: Scan for nearby BLE devices with filtering options
- 🔗 **Connection Management**: Connect to multiple devices simultaneously  
- 🛠️ **Service Discovery**: Discover services and characteristics
- 📖 **Read/Write Operations**: Read from and write to characteristics
- 🔔 **Notifications**: Enable/disable characteristic notifications
- 📱 **Cross Platform**: Works on both iOS and Android
- 🔐 **Permission Handling**: Automatic permission management
- 📝 **TypeScript Support**: Full TypeScript definitions included
- 🎯 **Event-Driven**: React to BLE events with a clean event system

## Installation

```sh
npm install react-native-sdk-ble
# or
yarn add react-native-sdk-ble
```

### iOS Setup

Add the following to your `ios/YourApp/Info.plist`:

```xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to communicate with BLE devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to communicate with BLE devices</string>
```

### Android Setup

The library automatically includes the required permissions in its manifest. For Android 6+ and 12+, runtime permissions will be handled automatically.

Required permissions (automatically included):
- `BLUETOOTH`
- `BLUETOOTH_ADMIN`  
- `ACCESS_FINE_LOCATION` (Android 6-11)
- `BLUETOOTH_SCAN` (Android 12+)
- `BLUETOOTH_CONNECT` (Android 12+)

## Quick Start

```typescript
import bleManager from 'react-native-sdk-ble-v3';

// Request permissions (Android)
await bleManager.requestPermissions();

// Start scanning for devices
await bleManager.startScan({
  timeout: 10000, // Stop after 10 seconds
  allowDuplicates: false,
});

// Listen for discovered devices
bleManager.on('scanResult', ({ device }) => {
  console.log('Found device:', device.name, device.id);
});

// Connect to a device
await bleManager.connect(deviceId);

// Discover services
const services = await bleManager.discoverServices(deviceId);

// Read a characteristic
const value = await bleManager.readCharacteristic(
  deviceId,
  serviceUuid,
  characteristicUuid
);

// Write to a characteristic
await bleManager.writeCharacteristic(
  deviceId,
  serviceUuid, 
  characteristicUuid,
  'Hello BLE!'
);

// Enable notifications
await bleManager.setNotify(deviceId, serviceUuid, characteristicUuid, true);

// Listen for notifications
bleManager.on('characteristicChanged', ({ deviceId, value }) => {
  // console.log('Notification from', deviceId, ':', value);
});
```

## API Reference

### Scanning

#### `startScan(options?: BleScanOptions): Promise<void>`

Start scanning for BLE devices.

```typescript
interface BleScanOptions {
  serviceUUIDs?: string[];        // Filter by service UUIDs
  allowDuplicates?: boolean;      // Allow duplicate discoveries
  scanMode?: 'lowPower' | 'balanced' | 'lowLatency'; // Android scan mode
  timeout?: number;               // Auto-stop after timeout (ms)
}

await bleManager.startScan({
  serviceUUIDs: ['180D'], // Heart Rate Service
  timeout: 10000,
  scanMode: 'balanced'
});
```

#### `stopScan(): Promise<void>`

Stop scanning for devices.

```typescript
await bleManager.stopScan();
```

### Connection Management

#### `connect(deviceId: string): Promise<void>`

Connect to a BLE device.

```typescript
await bleManager.connect('AA:BB:CC:DD:EE:FF');
```

#### `disconnect(deviceId: string): Promise<void>`

Disconnect from a device.

```typescript
await bleManager.disconnect(deviceId);
```

#### `isConnected(deviceId: string): Promise<boolean>`

Check if a device is connected.

```typescript
const connected = await bleManager.isConnected(deviceId);
```

#### `getConnectedDevices(): Promise<BleDevice[]>`

Get all currently connected devices.

```typescript
const devices = await bleManager.getConnectedDevices();
```

### Service Discovery

#### `discoverServices(deviceId: string): Promise<BleService[]>`

Discover services and characteristics for a connected device.

```typescript
const services = await bleManager.discoverServices(deviceId);
services.forEach(service => {
  console.log('Service:', service.uuid);
  service.characteristics.forEach(char => {
    console.log('  Characteristic:', char.uuid, char.properties);
  });
});
```

### Characteristic Operations

#### `readCharacteristic(deviceId: string, serviceUuid: string, characteristicUuid: string): Promise<string>`

Read data from a characteristic. Returns hex string.

```typescript
const data = await bleManager.readCharacteristic(
  deviceId,
  '180D', // Heart Rate Service  
  '2A37'  // Heart Rate Measurement
);
console.log('Heart rate data:', data);
```

#### `writeCharacteristic(deviceId: string, serviceUuid: string, characteristicUuid: string, data: string, options?: BleWriteOptions): Promise<void>`

Write data to a characteristic.

```typescript
interface BleWriteOptions {
  withResponse?: boolean; // Default: true
}

// Write UTF-8 string
await bleManager.writeCharacteristic(
  deviceId, 
  serviceUuid, 
  characteristicUuid, 
  'Hello BLE!'
);

// Write hex data
await bleManager.writeCharacteristic(
  deviceId,
  serviceUuid, 
  characteristicUuid,
  '0x48656C6C6F', // "Hello" in hex
  { withResponse: false }
);
```

#### `setNotify(deviceId: string, serviceUuid: string, characteristicUuid: string, enable: boolean): Promise<void>`

Enable or disable notifications for a characteristic.

```typescript
// Enable notifications
await bleManager.setNotify(deviceId, serviceUuid, characteristicUuid, true);

// Disable notifications  
await bleManager.setNotify(deviceId, serviceUuid, characteristicUuid, false);
```

### State & Permissions

#### `requestPermissions(): Promise<boolean>`

Request necessary BLE permissions (Android).

```typescript
const granted = await bleManager.requestPermissions();
if (!granted) {
  console.log('BLE permissions not granted');
}
```

#### `isBluetoothEnabled(): Promise<boolean>`

Check if Bluetooth is enabled.

```typescript
const enabled = await bleManager.isBluetoothEnabled();
```

#### `enableBluetooth(): Promise<void>`

Request to enable Bluetooth (shows system dialog).

```typescript
await bleManager.enableBluetooth();
```

## Events

The library uses an event-driven architecture. Listen for events using the `on()` method:

### Event Types

```typescript
type BleEvent = 
  | 'scanResult'
  | 'connected' 
  | 'disconnected'
  | 'servicesDiscovered'
  | 'characteristicChanged'
  | 'error'
  | 'bluetoothStateChanged';
```

### Event Listeners

#### `on<T extends BleEvent>(event: T, listener: (data: BleEventData[T]) => void): void`

Add an event listener.

#### `off<T extends BleEvent>(event: T, listener?: (data: BleEventData[T]) => void): void`

Remove an event listener.

#### `removeAllListeners(): void`

Remove all event listeners.

### Event Examples

```typescript
// Device discovered during scan
bleManager.on('scanResult', ({ device }) => {
  console.log(`Found: ${device.name} (${device.id})`);
  console.log(`RSSI: ${device.rssi}`);
  console.log(`Services: ${device.serviceUUIDs}`);
});

// Device connected
bleManager.on('connected', ({ deviceId }) => {
  console.log(`Connected to ${deviceId}`);
});

// Device disconnected
bleManager.on('disconnected', ({ deviceId, reason }) => {
  console.log(`Disconnected from ${deviceId}: ${reason}`);
});

// Services discovered
bleManager.on('servicesDiscovered', ({ deviceId, services }) => {
  console.log(`Found ${services.length} services for ${deviceId}`);
});

// Characteristic notification/indication
bleManager.on('characteristicChanged', ({ deviceId, serviceUuid, characteristicUuid, value }) => {
  console.log(`Notification: ${characteristicUuid} = ${value}`);
});

// Error occurred
bleManager.on('error', ({ code, message, deviceId }) => {
  console.error(`BLE Error ${code}: ${message}`, deviceId);
});

// Bluetooth state changed  
bleManager.on('bluetoothStateChanged', ({ state }) => {
  console.log(`Bluetooth state: ${state}`);
});
```

## Data Types

```typescript
interface BleDevice {
  id: string;                    // Device MAC address or UUID
  name?: string;                 // Device name
  rssi?: number;                 // Signal strength
  manufacturerData?: string;     // Manufacturer data (hex)
  serviceUUIDs?: string[];       // Advertised services
}

interface BleService {
  uuid: string;                  // Service UUID
  characteristics: BleCharacteristic[];
}

interface BleCharacteristic {
  uuid: string;                  // Characteristic UUID
  properties: string[];          // ['read', 'write', 'notify', 'indicate']
  descriptors?: BleDescriptor[];
}
```

## Error Handling

All methods return promises that may reject with errors. Common error codes:

- `BLUETOOTH_NOT_ENABLED` - Bluetooth is turned off
- `PERMISSIONS_NOT_GRANTED` - Required permissions missing
- `DEVICE_NOT_FOUND` - Device not discovered or invalid ID
- `DEVICE_NOT_CONNECTED` - Operation requires connection
- `SERVICE_NOT_FOUND` - Service UUID not found on device
- `CHARACTERISTIC_NOT_FOUND` - Characteristic UUID not found
- `OPERATION_FAILED` - Generic operation failure

```typescript
try {
  await bleManager.connect(deviceId);
} catch (error) {
  if (error.message.includes('DEVICE_NOT_FOUND')) {
    console.log('Device not in range or invalid ID');
  }
}
```

## Example App

See the [example](./example/) directory for a complete demo app showing:

- Device scanning with real-time results
- Multi-device connection management  
- Service and characteristic exploration
- Read/write operations with hex and text data
- Notification handling
- Error handling and logging
- Permission management

To run the example:

```bash
cd example
npm install

# iOS
npm run ios

# Android  
npm run android
```

## Testing

Run the test suite:

```bash
npm test
```

The library includes comprehensive unit tests covering:
- All API methods
- Event handling
- Error scenarios
- Mock native module interactions

## Platform Differences

### iOS
- Uses CoreBluetooth framework
- No runtime permissions required (only Info.plist)
- Cannot programmatically enable Bluetooth
- UUID-based device identification

### Android
- Uses BluetoothLE APIs
- Requires runtime permissions (automatically handled)
- Can request Bluetooth enable
- MAC address device identification
- Requires location permissions for scanning (Android 6-11)

## Troubleshooting

### Android Issues

**Location permissions**: On Android 6-11, location permissions are required for BLE scanning even if you don't use location features.

**Android 12+ permissions**: The library automatically handles the new `BLUETOOTH_SCAN` and `BLUETOOTH_CONNECT` permissions.

**Scanning not working**: Ensure Bluetooth and location (if required) are enabled.

### iOS Issues  

**Info.plist**: Ensure Bluetooth usage descriptions are added to Info.plist.

**Background scanning**: iOS restricts background BLE operations. The app should be in foreground for reliable operation.

### General Issues

**Connection failures**: Ensure the device is in range and not connected to another app.

**Service discovery**: Wait for the `servicesDiscovered` event before accessing characteristics.

**Notifications**: Not all characteristics support notifications. Check the `properties` array.

## Roadmap

- [ ] Background scanning support
- [ ] Peripheral (advertiser) mode
- [ ] Custom scan filters
- [ ] Connection priority settings  
- [ ] MTU size negotiation
- [ ] Descriptor read/write operations

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.

## License

MIT

## Credits

Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
