@ihealth/ihealthlibrary-react-native
Version:
iHealth React Native SDK - supports React Native New Architecture (TurboModules) and Old Architecture
794 lines (618 loc) • 24.4 kB
Markdown
# iHealth React Native SDK
[](https://www.npmjs.com/package/@ihealth/ihealthlibrary-react-native)
Official React Native SDK for iHealth Bluetooth health devices.
**v2.0.0 fully supports React Native New Architecture (TurboModules)** while remaining backward compatible with the Old Architecture (Bridge mode).
## Table of Contents
- [Requirements](#requirements)
- [Installation](#installation)
- [iOS Setup](#ios-setup)
- [Android Setup](#android-setup)
- [Quick Start](#quick-start)
- [Event Listening — Important](#event-listening--important)
- [Device Management](#device-management)
- [Device API Reference](#device-api-reference)
- [Supported Devices](#supported-devices)
- [Migrating from v1.x](#migrating-from-v1x)
- [FAQ](#faq)
- [Release Notes](#release-notes)
## Requirements
| Platform | Minimum Version |
|----------|----------------|
| React Native | **>= 0.76.0** |
| iOS | 12.0+ |
| Android | API 24+ (Android 7.0+) |
## Installation
```bash
npm install @ihealth/ihealthlibrary-react-native
# or
yarn add @ihealth/ihealthlibrary-react-native
```
## iOS Setup
### 1. Install Pods
```bash
cd ios && pod install
```
### 2. Add Permissions (Info.plist)
```xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Required to connect to iHealth devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Required to connect to iHealth devices</string>
```
### 3. Enable New Architecture (optional, recommended for RN 0.76+)
Edit `ios/Podfile.properties.json`:
```json
{
"newArchEnabled": "true"
}
```
Then run `pod install` again.
## Android Setup
### 1. Add Permissions (AndroidManifest.xml)
```xml
<!-- Bluetooth (Android 11 and below) -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Bluetooth (Android 12+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
```
### 2. Request Runtime Permissions
```javascript
import { PermissionsAndroid, Platform } from 'react-native';
async function requestBluetoothPermissions() {
if (Platform.OS !== 'android') return true;
const permissions = Platform.Version >= 31
? [
PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
]
: [
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
];
const result = await PermissionsAndroid.requestMultiple(permissions);
return Object.values(result).every(
v => v === PermissionsAndroid.RESULTS.GRANTED
);
}
```
### 3. SDK License Authentication (Android only)
Place the `.pem` license file provided by iHealth into `android/app/src/main/assets/`, then call the following once on app startup:
```javascript
import { iHealthDeviceManagerModule } from '@ihealth/ihealthlibrary-react-native';
iHealthDeviceManagerModule.sdkAuthWithLicense('your_license_file.pem');
```
> **iOS does not require license authentication.**
### 4. Enable New Architecture (optional, recommended for RN 0.76+)
Edit `android/gradle.properties`:
```properties
newArchEnabled=true
```
## Quick Start
The example below shows a complete flow — scan → connect → measure — using the BP5S blood pressure monitor:
```javascript
import React, { useEffect, useState } from 'react';
import { View, Button, Text, NativeEventEmitter, NativeModules } from 'react-native';
import {
iHealthDeviceManagerModule,
BP5SModule,
} from '@ihealth/ihealthlibrary-react-native';
export default function BP5SScreen() {
const [devices, setDevices] = useState([]);
const [connectedMac, setMac] = useState(null);
const [result, setResult] = useState('');
// Step 1 — Listen for scan and connection events
useEffect(() => {
const managerEmitter = new NativeEventEmitter(
NativeModules.iHealthDeviceManagerModule
);
const onScan = managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Scan_Device,
(e) => setDevices(prev => [...prev, e])
);
const onConnected = managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Device_Connected,
(e) => setMac(e.mac)
);
const onDisconnect = managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Device_Disconnect,
() => setMac(null)
);
return () => {
onScan.remove();
onConnected.remove();
onDisconnect.remove();
};
}, []);
// Step 2 — Listen for device data events
useEffect(() => {
const bp5sEmitter = new NativeEventEmitter(NativeModules.BP5SModule);
const onData = bp5sEmitter.addListener(
BP5SModule.Event_Notify,
(e) => setResult(JSON.stringify(e))
);
return () => onData.remove();
}, []);
return (
<View>
<Button
title="Scan"
onPress={() => {
setDevices([]);
iHealthDeviceManagerModule.startDiscovery(
iHealthDeviceManagerModule.BP5S
);
}}
/>
{devices.map(d => (
<Button
key={d.mac}
title={`Connect ${d.mac}`}
onPress={() =>
iHealthDeviceManagerModule.connectDevice(
d.mac,
iHealthDeviceManagerModule.BP5S
)
}
/>
))}
{connectedMac && (
<Button
title="Start Measure"
onPress={() => BP5SModule.startMeasure(connectedMac)}
/>
)}
<Text>{result}</Text>
</View>
);
}
```
## Event Listening — Important
> ⚠️ **Breaking change in v2.0.0**: You must use `NativeEventEmitter` instead of `DeviceEventEmitter`.
### Correct Usage
```javascript
import { NativeEventEmitter, NativeModules } from 'react-native';
import { BP5SModule } from '@ihealth/ihealthlibrary-react-native';
useEffect(() => {
// Pass the corresponding NativeModules instance for your device
const emitter = new NativeEventEmitter(NativeModules.BP5SModule);
const listener = emitter.addListener(BP5SModule.Event_Notify, (event) => {
console.log(event);
});
return () => listener.remove(); // Always clean up on unmount
}, []);
```
### Incorrect Usage (events will be lost)
```javascript
// ❌ Do NOT use DeviceEventEmitter
import { DeviceEventEmitter } from 'react-native';
DeviceEventEmitter.addListener(BP5SModule.Event_Notify, handler);
// ❌ Do NOT create the emitter at module top-level (outside useEffect)
const emitter = new NativeEventEmitter(NativeModules.BP5SModule); // top-level — wrong
```
### NativeModules Name Reference
| SDK Import Name | NativeModules Key |
|----------------|-------------------|
| `iHealthDeviceManagerModule` | `NativeModules.iHealthDeviceManagerModule` |
| `BP5Module` | `NativeModules.BP5Module` |
| `BP5SModule` | `NativeModules.BP5SModule` |
| `BP550BTModule` | `NativeModules.BP550BTModule` |
| `BP3LModule` | `NativeModules.BP3LModule` |
| `BP7Module` | `NativeModules.BP7Module` |
| `BP7SModule` | `NativeModules.BP7SModule` |
| `PO3Module` | `NativeModules.PO3Module` |
| `PO1Module` | `NativeModules.PO1Module` |
| `HS2SModule` | `NativeModules.HS2SModule` |
| `HS2SProModule` | `NativeModules.HS2SProModule` |
| `HS4SModule` | `NativeModules.HS4SModule` |
| `HS6Module` | `NativeModules.HS6Module` |
| `BG5SModule` | `NativeModules.BG5SModule` |
| `BG5Module` | `NativeModules.BG5Module` |
| `BG1Module` | `NativeModules.BG1Module` |
| `BG1AModule` | `NativeModules.BG1AModule` |
| `BG1SModule` | `NativeModules.BG1SModule` |
| `AM3SModule` | `NativeModules.AM3SModule` |
| `AM4Module` | `NativeModules.AM4Module` |
| `AM5Module` | `NativeModules.AM5Module` |
| `AM6Module` | `NativeModules.AM6Module` |
| `BTMModule` | `NativeModules.BTMModule` |
| `TS28BModule` | `NativeModules.TS28BModule` |
| `NT13BModule` | `NativeModules.NT13BModule` |
| `PT3SBTModule` | `NativeModules.PT3SBTModule` |
| `ECGModule` (iOS only) | `NativeModules.ECGModule` |
| `ECGUSBModule` (iOS only) | `NativeModules.ECGUSBModule` |
## Device Management
### Scan for Devices
```javascript
import { iHealthDeviceManagerModule } from '@ihealth/ihealthlibrary-react-native';
// Start scanning — pass the device type constant
iHealthDeviceManagerModule.startDiscovery(iHealthDeviceManagerModule.BP5S);
// Stop scanning
iHealthDeviceManagerModule.stopDiscovery();
```
**Device type constants:**
| Constant | Value | Device |
|----------|-------|--------|
| `iHealthDeviceManagerModule.BP5` | `'BP5'` | BP5 Blood Pressure Monitor |
| `iHealthDeviceManagerModule.BP5S` | `'BP5S'` | BP5S Blood Pressure Monitor |
| `iHealthDeviceManagerModule.BP7` | `'BP7'` | BP7 Blood Pressure Monitor |
| `iHealthDeviceManagerModule.BP7S` | `'BP7S'` | BP7S Blood Pressure Monitor |
| `iHealthDeviceManagerModule.BP3L` | `'BP3L'` | BP3L Blood Pressure Monitor |
| `iHealthDeviceManagerModule.KN550` | `'KN550'` | KN-550BT Blood Pressure Monitor |
| `iHealthDeviceManagerModule.PO3` | `'PO3'` | PO3 Pulse Oximeter |
| `iHealthDeviceManagerModule.PO1` | `'PO1'` | PO1 Pulse Oximeter |
| `iHealthDeviceManagerModule.HS2S` | `'HS2S'` | HS2S Body Scale |
| `iHealthDeviceManagerModule.AM6` | `'AM6'` | AM6 Activity Tracker |
| `iHealthDeviceManagerModule.BG5S` | `'BG5S'` | BG5S Blood Glucose Meter |
| `iHealthDeviceManagerModule.BG1A` | `'BG1A'` | BG1A Blood Glucose Meter |
| `iHealthDeviceManagerModule.BG1S` | `'BG1S'` | BG1S Blood Glucose Meter |
| `iHealthDeviceManagerModule.BTM` | `'FDIR_V3'` | BTM Thermometer |
| `iHealthDeviceManagerModule.NT13B` | `'NT13B'` | NT13B Thermometer |
| `iHealthDeviceManagerModule.TS28B` | `'TS28B'` | TS28B Thermometer |
| `iHealthDeviceManagerModule.PT3SBT` | `'PT3SBT'` | PT3SBT Thermometer |
| `iHealthDeviceManagerModule.AM3S` | `'AM3S'` | AM3S Activity Monitor |
| `iHealthDeviceManagerModule.AM4` | `'AM4'` | AM4 Activity Monitor |
| `iHealthDeviceManagerModule.AM5` | `'AM5'` | AM5 Activity Monitor |
| `iHealthDeviceManagerModule.ECG3` | `'ECG3'` | ECG3 (iOS only) |
| `iHealthDeviceManagerModule.ECG3USB` | `'ECG3USB'` | ECG3USB (iOS only) |
### Connect / Disconnect
```javascript
// Connect — mac comes from the scan event (event.mac)
iHealthDeviceManagerModule.connectDevice(mac, iHealthDeviceManagerModule.BP5S);
// Disconnect
iHealthDeviceManagerModule.disconnectDevice(mac, iHealthDeviceManagerModule.BP5S);
```
### Device Manager Events
```javascript
const managerEmitter = new NativeEventEmitter(
NativeModules.iHealthDeviceManagerModule
);
// Device discovered during scan
// Payload: { mac: string, type: string, rssi?: number }
managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Scan_Device, handler
);
// Scan finished
managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Scan_Finish, handler
);
// Device connected successfully
// Payload: { mac: string, type: string }
managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Device_Connected, handler
);
// Device connection failed
// Payload: { mac: string, type: string }
managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Device_Connect_Failed, handler
);
// Device disconnected
// Payload: { mac: string, type: string }
managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Device_Disconnect, handler
);
// SDK license authentication result (Android only)
// Payload: { authen: boolean }
managerEmitter.addListener(
iHealthDeviceManagerModule.Event_Authenticate_Result, handler
);
```
## Device API Reference
### Blood Pressure Monitor — BP5 / BP5S / BP3L / BP7
```javascript
import { BP5SModule, BPProfileModule } from '@ihealth/ihealthlibrary-react-native';
import { NativeEventEmitter, NativeModules } from 'react-native';
const emitter = new NativeEventEmitter(NativeModules.BP5SModule);
emitter.addListener(BP5SModule.Event_Notify, (event) => {
switch (event.action) {
case BPProfileModule.ACTION_ONLINE_REAL_TIME_MEASUREMENT:
// Real-time data: event.sys, event.dia, event.heartRate, event.pulse
break;
case BPProfileModule.ACTION_ONLINE_RESULT:
// Final measurement result
break;
case BPProfileModule.ACTION_BATTERY_BP:
// Battery level: event.battery (0–100)
break;
case BPProfileModule.ACTION_ERROR_BP:
// Error: event.error
break;
}
});
// Measurement
BP5SModule.startMeasure(mac);
BP5SModule.stopMeasure(mac);
// Offline data
BP5SModule.enbleOffline(mac, 1); // Enable offline mode
BP5SModule.enbleOffline(mac, 0); // Disable offline mode
BP5SModule.getOffLineNum(mac); // Get number of offline records
BP5SModule.getOffLineData(mac); // Download offline data
BP5SModule.deleteData(mac); // Delete downloaded offline data
// Device info
BP5SModule.getBattery(mac);
BP5SModule.getFunctionInfo(mac);
BP5SModule.getHardwareVersion(mac);
BP5SModule.disconnect(mac);
```
### Blood Pressure Monitor — KN-550BT (BP550BTModule)
```javascript
import { BP550BTModule } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.BP550BTModule);
emitter.addListener(BP550BTModule.Event_Notify, (event) => {
console.log(event);
});
BP550BTModule.getBattery(mac);
BP550BTModule.getFirmVersion(mac);
BP550BTModule.getFunctionInfo(mac);
BP550BTModule.getOffLineNum(mac);
BP550BTModule.getOffLineData(mac);
BP550BTModule.getTime(mac);
BP550BTModule.getDisplayConfig(mac);
BP550BTModule.setDisplayConfig(mac, showSystolic, showDiastolic);
BP550BTModule.transferFinished(mac);
BP550BTModule.disconnect(mac);
```
### Pulse Oximeter — PO3 / PO1
```javascript
import { PO3Module } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.PO3Module);
emitter.addListener(PO3Module.Event_Notify, (event) => {
// event: { action, spo2, pr, pi, waveformData, ... }
console.log(event);
});
PO3Module.startMeasure(mac); // Start real-time measurement
PO3Module.getBattery(mac);
PO3Module.getHistoryData(mac); // Download history records
PO3Module.disconnect(mac);
```
### Body Scale — HS2S / HS2S Pro
```javascript
import { HS2SModule } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.HS2SModule);
emitter.addListener(HS2SModule.Event_Notify, (event) => {
console.log(event);
});
// Unit: 1 = kg, 2 = jin (Chinese unit), 3 = lb
HS2SModule.setUnit(mac, 1);
// User management — userID must be a 16-character string
const userID = '1234567890123456';
HS2SModule.updateUserInfo(
mac, userID, createTimestamp,
weight, // kg
age, // years
height, // cm
sex, // 0 = female, 1 = male
impedanceMark,
fitnessMark
);
HS2SModule.getUserInfo(mac);
HS2SModule.deleteUser(mac, userID);
// Measurement (userType: 1 = registered user, 0 = guest)
HS2SModule.measure(
mac, 1, userID, createTimestamp,
weight, age, height, sex, impedanceMark, fitnessMark
);
// History data
HS2SModule.getMemoryDataCount(mac, userID);
HS2SModule.getMemoryData(mac, userID);
HS2SModule.deleteMemoryData(mac, userID);
HS2SModule.getAnonymousMemoryDataCount(mac);
HS2SModule.getAnonymousMemoryData(mac);
HS2SModule.deleteAnonymousMemoryData(mac);
// Heart rate measurement mode (HS2S Pro)
HS2SProModule.enterHS2SProHeartRateMeasurementMode(mac);
HS2SProModule.exitHS2SProHeartRateMeasurementMode(mac);
HS2SModule.disconnect(mac);
```
### Blood Glucose Meter — BG5S
```javascript
import { BG5SModule } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.BG5SModule);
emitter.addListener(BG5SModule.Event_Notify, (event) => {
console.log(event);
});
BG5SModule.getStatusInfo(mac);
// Sync time: date string + timezone offset (hours)
BG5SModule.setTime(mac, '2025-01-01 12:00:00', 8);
// Unit: 1 = mmol/L, 2 = mg/dL
BG5SModule.setUnit(mac, 1);
// Start measurement: testType 1 = blood, 2 = urine
BG5SModule.startMeasure(mac, 1);
// Offline data
BG5SModule.setOfflineModel(mac, true);
BG5SModule.getOfflineData(mac);
BG5SModule.deleteOfflineData(mac);
BG5SModule.deleteUsedStrip(mac);
// Note: both spellings are accepted
BG5SModule.disconnect(mac); // recommended
BG5SModule.disConnect(mac); // also valid
```
### Blood Glucose Meter — BG1A
```javascript
import { BG1AModule } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.BG1AModule);
emitter.addListener(BG1AModule.Event_Notify, (event) => {
console.log(event);
});
BG1AModule.getDeviceInfo(mac);
BG1AModule.setMeasureMode(mac, 1); // 1 = blood, 2 = urine
BG1AModule.setDeviceTime(mac); // Sync phone time to device
BG1AModule.getHistoryData(mac);
BG1AModule.deleteHistoryData(mac);
BG1AModule.disconnect(mac);
```
### Blood Glucose Meter — BG1S
```javascript
import { BG1SModule } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.BG1SModule);
emitter.addListener(BG1SModule.Event_Notify, (event) => {
console.log(event);
});
BG1SModule.getFunction(mac); // Get device function info
BG1SModule.measure(mac, testType); // testType: 1 = blood
BG1SModule.disconnect(mac);
```
### Activity Tracker — AM6
```javascript
import { AM6Module } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.AM6Module);
emitter.addListener(AM6Module.Event_Notify, (event) => {
console.log(event);
});
// Initialization
AM6Module.getDeviceInfoAndSyncTime(mac, true);
AM6Module.setUserInfo(mac, userID, gender, age, height, weight);
AM6Module.setPhonePlatform(mac);
// Sync health data
AM6Module.readySyncData(mac);
AM6Module.getDailyData(mac);
AM6Module.getStepData(mac);
AM6Module.getSleepData(mac);
AM6Module.getHeartRateData(mac);
AM6Module.getBloodOxygenData(mac);
AM6Module.getActivityData(mac);
AM6Module.deleteData(mac, dataType);
// Reminders
AM6Module.setTargetRemind(mac, enable, stepTarget, calorieTarget);
AM6Module.setTargetRemind(mac, true, 8000, 300); // example
AM6Module.getTargetRemind(mac);
AM6Module.setSedentaryRemind(mac, enable, startTime, endTime);
AM6Module.setRaiseToLightRemind(mac, enable, startTime, endTime);
AM6Module.setDoNotDisturbMode(mac, enable, startTime, endTime);
// Alarm clock — format: "repeat:days:time;..." e.g. "1:1-1-1-1-1-1-1:480"
AM6Module.setAlarmClockList(mac, alarmString);
AM6Module.getAlarmClockList(mac);
// Display & wear settings
AM6Module.setWearHand(mac, 1); // 0 = left, 1 = right
AM6Module.getWearHand(mac);
// Notifications
AM6Module.notifyMessage(mac, timestamp, enable, type, title, content);
// Bind / unbind user
AM6Module.startBind(mac);
AM6Module.bindUserSuccess(mac, userID);
AM6Module.bindUserFail(mac);
AM6Module.unBindUser(mac, userID);
AM6Module.findDevice(mac, 1); // Vibrate to locate the device
AM6Module.rebootDevice(mac);
AM6Module.disconnect(mac);
```
### Thermometer — BTM / NT13B / TS28B / PT3SBT
```javascript
import { BTMModule } from '@ihealth/ihealthlibrary-react-native';
const emitter = new NativeEventEmitter(NativeModules.BTMModule);
emitter.addListener(BTMModule.Event_Notify, (event) => {
// event: { action, temperature, unit, ... }
console.log(event);
});
BTMModule.getBattery(mac);
BTMModule.startMeasure(mac);
BTMModule.stopMeasure(mac);
BTMModule.disconnect(mac);
```
## Supported Devices
| Category | Model | Module | Platform |
|----------|-------|--------|----------|
| Blood Pressure | BP5 | `BP5Module` | iOS & Android |
| Blood Pressure | BP5S | `BP5SModule` | iOS & Android |
| Blood Pressure | BP3L | `BP3LModule` | iOS & Android |
| Blood Pressure | BP7 | `BP7Module` | iOS & Android |
| Blood Pressure | BP7S | `BP7SModule` | iOS & Android |
| Blood Pressure | KN-550BT | `BP550BTModule` | iOS & Android |
| Pulse Oximeter | PO3 | `PO3Module` | iOS & Android |
| Pulse Oximeter | PO1 | `PO1Module` | iOS & Android |
| Body Scale | HS2S | `HS2SModule` | iOS & Android |
| Body Scale | HS2S Pro | `HS2SProModule` | iOS & Android |
| Body Scale | HS4S | `HS4SModule` | iOS & Android |
| Body Scale | HS6 | `HS6Module` | iOS & Android |
| Blood Glucose | BG5S | `BG5SModule` | iOS & Android |
| Blood Glucose | BG5 | `BG5Module` | iOS & Android |
| Blood Glucose | BG1 | `BG1Module` | iOS & Android |
| Blood Glucose | BG1A | `BG1AModule` | iOS & Android |
| Blood Glucose | BG1S | `BG1SModule` | iOS & Android |
| Activity Tracker | AM3S | `AM3SModule` | iOS & Android |
| Activity Tracker | AM4 | `AM4Module` | iOS & Android |
| Activity Tracker | AM5 | `AM5Module` | iOS & Android |
| Activity Tracker | AM6 | `AM6Module` | iOS & Android |
| Thermometer | BTM | `BTMModule` | iOS & Android |
| Thermometer | TS28B | `TS28BModule` | iOS & Android |
| Thermometer | NT13B | `NT13BModule` | iOS & Android |
| Thermometer | PT3SBT | `PT3SBTModule` | iOS & Android |
| ECG | ECG3 | `ECGModule` | **iOS only** |
| ECG | ECG3USB | `ECGUSBModule` | **iOS only** |
## Migrating from v1.x
See [MIGRATION_GUIDE.md](./MIGRATION_GUIDE.md) for a full guide.
**The only code change required** is replacing `DeviceEventEmitter` with `NativeEventEmitter`:
```javascript
// Before (v1.x) ❌
import { DeviceEventEmitter } from 'react-native';
DeviceEventEmitter.addListener(BP5SModule.Event_Notify, handler);
// After (v2.0+) ✅
import { NativeEventEmitter, NativeModules } from 'react-native';
const emitter = new NativeEventEmitter(NativeModules.BP5SModule);
emitter.addListener(BP5SModule.Event_Notify, handler);
```
## FAQ
**Q: I can scan and connect to a device, but never receive measurement data.**
A: Make sure you are creating `NativeEventEmitter` inside `useEffect`, not at the module top-level or component function body. The emitter must be created after React Native has fully initialized the native modules.
**Q: iOS `pod install` fails with a linker error about a missing static library.**
A: Delete the `ios/Pods` directory and run `cd ios && pod install` again.
**Q: Android method calls have no effect.**
A: Confirm that the `.pem` license file is present in `android/app/src/main/assets/` and that `sdkAuthWithLicense()` was called at app startup.
**Q: `NativeModules.XXXModule` is `null`.**
A: ECG3 and ECG3USB are iOS-only. They will be `null` on Android. Use `Platform.OS === 'ios'` before accessing these modules.
**Q: What is the correct method name for HS2S heart rate mode?**
A: Both spellings are supported and equivalent:
```javascript
HS2SModule.enterHS2SHeartRateMeasurementMode(mac); // v1.x name, still works
HS2SModule.enterHS2SProHeartRateMeasurementMode(mac); // native method name
```
**Q: How do I disconnect the BG5S?**
A: Both of the following work identically:
```javascript
BG5SModule.disconnect(mac); // recommended
BG5SModule.disConnect(mac); // also valid (legacy spelling)
```
**Q: I'm upgrading from an older RN version — do I need to change anything else?**
A: If you are upgrading to React Native >= 0.76 and enabling the New Architecture, the event listener change above is the only SDK-level change. Your device API calls (methods on `BP5SModule`, `AM6Module`, etc.) remain unchanged.
## Release Notes
### v2.0.0
- **Full support for React Native New Architecture (TurboModules / Fabric)**
- Minimum React Native version bumped to **>= 0.76.0**
- iOS: all modules use `initWithDisabledObservation` to guarantee event dispatch in TurboModule mode
- iOS: 29 `+TurboModule.mm` category files added for C++ JSI bridge
- Android: `@ReactModule` annotations added to all modules for interop layer compatibility
- 29 TypeScript spec files added (`src/Native*.ts`) for Codegen support
- Event name constants are now hardcoded in JavaScript — no longer depend on `constantsToExport`
- **Breaking change**: event listeners must use `NativeEventEmitter` (see [Migrating from v1.x](#migrating-from-v1x))
### v1.9.1
- Support Android 16KB page size
- Bug fixes
### v1.9.0
- Adapt to Android 15
- Code optimization and bug fixes
### v1.8.0
- Support new version of BP5S (hardware version > 2.0.0) with offline data time correction
- Code optimization