react-native-colmi-ring
v1.1.1
Published
React Native Turbo Module SDK for COLMI / R-series smart health rings (BLE heart rate, SpO2, sleep, HRV, blood pressure, temperature, pressure)
Maintainers
Readme
react-native-colmi-ring
A React Native Turbo Module SDK for COLMI / R-series smart health rings.
Scan, connect, read real-time vitals, and pull full historical health data over BLE — all from JavaScript.
Android only. iOS support is not yet implemented.
Table of Contents
- Installation
- Android Setup
- Quick Start
- API Reference
- Events Reference
- Compatible Rings
- Sleep Protocol Selection
- Changelog
- License
Installation
# npm
npm install react-native-colmi-ring
# yarn
yarn add react-native-colmi-ring
# pnpm
pnpm add react-native-colmi-ringNo manual native linking is required — the module uses React Native's auto-linking.
Android Setup
1 · Add Permissions to AndroidManifest.xml
Open android/app/src/main/AndroidManifest.xml and add:
<!-- Required on all Android versions -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Required on Android 12+ (API 31+) -->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Required on Android 11 and below for BLE scan -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />2 · Request Runtime Permissions
On Android 12+ you must request BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime before calling any SDK method.
import { PermissionsAndroid, Platform } from 'react-native';
async function requestBluetoothPermissions(): Promise<boolean> {
if (Platform.OS !== 'android') return true;
if (Platform.Version < 31) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
const results = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
]);
return Object.values(results).every(
(r) => r === PermissionsAndroid.RESULTS.GRANTED
);
}Quick Start
import { useEffect } from 'react';
import ColmiRing from 'react-native-colmi-ring';
export default function App() {
useEffect(() => {
const subs = [
ColmiRing.on('DeviceFound', (d) => console.log('Found:', d.name, d.address)),
ColmiRing.on('RingConnected', () => console.log('Ring connected!')),
ColmiRing.on('BatteryStatus', (d) => console.log(`Battery: ${d.level}% charging=${d.charging}`)),
ColmiRing.on('DeviceCapabilities', (caps) => {
console.log('Caps:', caps);
if (caps.useNewSleepProtocol) {
ColmiRing.getSleepBigData();
}
}),
];
ColmiRing.startScan();
return () => {
ColmiRing.stopScan();
subs.forEach((s) => s.remove());
};
}, []);
}API Reference
ColmiRing.on(event, handler) → Subscription
Subscribe to a ring event. Always call subscription.remove() when the component unmounts to avoid memory leaks.
const sub = ColmiRing.on('RealTimeHeartRate', ({ value }) => console.log(value));
// cleanup:
sub.remove();Connection
| Method | Description |
|---|---|
| startScan() | Start a BLE scan. Emits DeviceFound for each compatible ring discovered. Also emits bonded rings immediately. |
| stopScan() | Stop the active BLE scan. |
| getBondedRings() | Emit DeviceFound for every already-paired ring without scanning. |
| connect(address: string) | Connect to a ring by MAC address. Triggers the full init sequence automatically. |
| disconnect() | Disconnect and unpair the current ring. Emits RingDisconnected. |
Device Control
| Method | Description |
|---|---|
| setTime() | Sync phone UTC time to the ring. Called automatically on connect. Emits TimeSet. |
| getBattery() | Request battery level. Emits BatteryStatus. |
| blinkTwice() | Flash the ring LED twice for identification. |
| findDevice() | Flash the green LED for ~10 seconds to locate the ring. |
| reboot() | Reboot the ring firmware. Emits RingRebooting. |
Settings
| Method | Description |
|---|---|
| getHeartRateLogSettings() | Read HR auto-log interval. Emits HeartRateLogSettings. |
| setHeartRateLogSettings(enabled: boolean, intervalMinutes: number) | Set HR auto-log interval (1–255 min). |
| getBloodOxygenSettings() | Read SpO2 auto-monitoring setting. Emits SpO2Settings. |
| setBloodOxygenSetting(enabled: boolean) | Enable / disable SpO2 auto-monitoring. |
| getHRVSettings() | Read HRV auto-monitoring setting. Emits HRVSettings. |
| setHRVSetting(enabled: boolean) | Enable / disable HRV auto-monitoring. |
| getBPSettings() | Read blood pressure auto-monitoring setting. Emits BPSettings. |
| setBPSettings(enabled: boolean) | Enable / disable blood pressure auto-monitoring. |
| getPressureSettings() | Read stress/pressure auto-monitoring setting. Emits PressureSettings. |
| setPressureSetting(enabled: boolean) | Enable / disable stress/pressure auto-monitoring. |
| getTemperatureSettings() | Read temperature auto-monitoring setting. Emits TemperatureSettings. |
| setTemperatureSetting(enabled: boolean) | Enable / disable temperature auto-monitoring. |
Real-Time Measurements
Start/stop methods come in pairs. Data arrives continuously via events until stopped or an error occurs.
| Start | Stop | Data Event | Error Event | Stopped Event |
|---|---|---|---|---|
| startRealTimeHeartRate() | stopRealTimeHeartRate() | RealTimeHeartRate | RealTimeHeartRateError | RealTimeHeartRateStopped |
| startRealTimeBloodPressure() | stopRealTimeBloodPressure() | RealTimeBloodPressure | RealTimeBloodPressureError | RealTimeBloodPressureStopped |
| startRealTimeSpO2() | stopRealTimeSpO2() | RealTimeSpO2 | RealTimeSpO2Error | RealTimeSpO2Stopped |
| startRealTimeHRV() | stopRealTimeHRV() | RealTimeHRV | RealTimeHRVError | RealTimeHRVStopped |
| startRealTimeFatigue() | stopRealTimeFatigue() | RealTimeFatigue | RealTimeFatigueError | RealTimeFatigueStopped |
| startRealTimePressure() | stopRealTimePressure() | RealTimePressure | RealTimePressureError | RealTimePressureStopped |
| startRealTimeTemperature() | stopRealTimeTemperature() | RealTimeTemperature | RealTimeTemperatureError | RealTimeTemperatureStopped |
| startRealTimeHealthCheck() | stopRealTimeHealthCheck() | RealTimeHealthCheck | RealTimeHealthCheckError | RealTimeHealthCheckStopped |
Historical Data
| Method | Description |
|---|---|
| getHeartRateLog(targetMidnightMs: number) | Logged HR for one day. Pass midnight Unix ms of the target day. Emits HeartRateLog. |
| getSteps(dayOffset?: number) | 15-min step / calorie / distance buckets. 0 = today, 1 = yesterday. Emits StepsData. |
| getTodaySports() | Today's sport totals summary. Emits TodaySports. |
| getHRVHistory() | HRV history (multi-packet split array). Emits HRVHistory. |
| getPressureHistory() | Stress/pressure history (multi-packet split array). Emits PressureHistory. |
| getSleepBigData() | Full multi-day sleep with stage periods (newer rings). Emits SleepData. |
| getSpO2BigData() | Daily SpO2 min/max hourly summary (newer rings). Emits SpO2BigData. |
| getTemperatureBigData() | Temperature history log (48 × 30-min slots per day). Emits TemperatureHistory. |
Events Reference
Subscribe to any event using ColmiRing.on('EventName', handler).
Connection Events
BluetoothEnabled
Fired when Bluetooth is turned on.
// no payloadBluetoothDisabled
Fired when Bluetooth is turned off or unavailable.
// no payloadDeviceFound
Fired for each compatible ring discovered during scan, and immediately for already-bonded rings.
{
name: string; // BLE display name, e.g. "R02_AB12"
address: string; // MAC address, e.g. "AA:BB:CC:DD:EE:FF"
}RingConnected
Fired after the full init sequence completes (~2.2 s after physical connection).
// no payloadRingDisconnected
Fired when the ring disconnects or disconnect() is called.
// no payloadRingRebooting
Fired immediately after reboot() is called.
// no payloadBigDataReady
Fired when the Big Data BLE service is discovered and ready (newer rings only).
// no payloadDevice Info Events
TimeSet
Fired after the ring's time is synced.
// no payloadBatteryStatus
{
level: number; // 0–100 (percent)
charging: boolean;
}DeviceCapabilities
Fired once after connect. Use these flags to conditionally show or hide features in your UI.
{
supportsBloodOxygen: boolean;
supportsBloodPressure: boolean;
supportsHRV: boolean;
supportsPressure: boolean;
supportsBloodSugar: boolean;
supportsGPS: boolean;
supportsWeather: boolean;
supportsOneKeyCheck: boolean;
supportsTemperature: boolean;
useNewSleepProtocol: boolean; // true → use getSleepBigData(); false → legacy sleep
screenWidth: number;
screenHeight: number;
maxWatchFace: number;
}DeviceNotify
Raw firmware notification pushed by the ring (e.g. incoming call alert, alarm trigger).
{
type: number; // notification type identifier
data: number[]; // raw payload bytes
}Heart Rate Events
HeartRateLog
Historical HR log for a requested day.
{
readings: number[]; // HR value per interval slot (0 = no data for that slot)
timestampMs: number; // Unix ms of midnight for the recorded day
intervalMinutes: number; // sampling interval in minutes (e.g. 5)
totalPackets: number; // number of BLE packets received
}HeartRateLogError
Fired when no HR log data is available for the requested day.
// no payloadHeartRateLogSettings
{
enabled: boolean;
intervalMinutes: number;
}RealTimeHeartRate
{ value: number } // heart rate in bpmRealTimeHeartRateError
Fired when the ring reports an error during real-time HR measurement.
// no payloadRealTimeHeartRateStopped
Fired when real-time HR measurement has stopped.
// no payloadBlood Pressure Events
BloodPressureReading
Historical BP reading auto-pushed by the ring.
{
systolic: number; // mmHg
diastolic: number; // mmHg
timestampMs: number; // Unix ms
}BPSettings
{ enabled: boolean }RealTimeBloodPressure
{
systolic: number; // mmHg
diastolic: number; // mmHg
}RealTimeBloodPressureError
Fired when the ring reports an error during real-time BP measurement.
// no payloadRealTimeBloodPressureStopped
Fired when real-time BP measurement has stopped.
// no payloadSpO2 (Blood Oxygen) Events
SpO2Settings
{ enabled: boolean }SpO2BigData
Daily SpO2 hourly min/max summary (newer rings only).
{
samples: Array<{
daysAgo: number; // 0 = today, 1 = yesterday, …
hour: number; // 0–23
min: number; // minimum SpO2 % in that hour (0 = no data)
max: number; // maximum SpO2 % in that hour (0 = no data)
dateMs: number; // Unix ms of midnight for that day
slotMs: number; // Unix ms of the start of that hour slot
}>
}RealTimeSpO2
{ value: number } // SpO2 percentage (50–100)RealTimeSpO2Error
// no payloadRealTimeSpO2Stopped
// no payloadHRV Events
HRVHistory
Multi-packet HRV history (split array protocol).
{
records: Array<{
offset: number; // time slot index from start of range
values: number[]; // HRV value(s) for that slot
}>;
rangeMinutes: number; // total range covered in minutes
}HRVSettings
{ enabled: boolean }RealTimeHRV
{ value: number }RealTimeHRVError
// no payloadRealTimeHRVStopped
// no payloadPressure / Stress Events
PressureHistory
Multi-packet stress/pressure history (split array protocol).
{
records: Array<{
offset: number; // time slot index
values: number[]; // stress/pressure value(s) for that slot
}>;
rangeMinutes: number; // total range covered in minutes
}PressureSettings
{ enabled: boolean }RealTimePressure
{ value: number } // stress/pressure scoreRealTimePressureError
// no payloadRealTimePressureStopped
// no payloadTemperature Events
TemperatureSettings
{ enabled: boolean }TemperatureHistory
Historical temperature log from Big Data (ID 37). 48 slots × 30-minute intervals per day, starting at 00:00.
{
daysAgo: number; // 0 = today, 1 = yesterday, …
samples: Array<{
slotIndex: number; // 0 = 00:00, 1 = 00:30, …, 47 = 23:30
timestampMs: number; // Unix ms for that 30-min slot
celsius: number; // e.g. 36.5
fahrenheit: number; // e.g. 97.7
raw: number; // raw byte (valid range 150–220); celsius = raw × 0.1 + 20.0
daysAgo: number;
}>
}RealTimeTemperature
{
celsius: number; // e.g. 36.6
fahrenheit: number; // e.g. 97.9
raw: number; // raw byte; celsius = raw / 10.0 + 20.3
}RealTimeTemperatureError
// no payloadRealTimeTemperatureStopped
// no payloadSleep Events
SleepData
Full multi-day sleep data with detailed stage periods (Big Data, newer rings). Prefer this over legacy sleep when DeviceCapabilities.useNewSleepProtocol is true.
{
days: Array<{
daysAgo: number; // 0 = last night, 1 = night before, …
sleepStart: number; // minutes relative to midnight (negative = before midnight)
sleepEnd: number; // minutes relative to midnight
totalMinutes: number; // LIGHT + DEEP + REM + AWAKE total
deepMinutes: number;
lightMinutes: number;
remMinutes: number;
awakeMinutes: number;
periods: Array<{
type: 'LIGHT' | 'DEEP' | 'REM' | 'AWAKE' | 'NODATA' | 'ERROR';
minutes: number;
}>;
}>
}Steps & Sport Events
StepsData
15-minute bucketed step, calorie, and distance data for a requested day.
{
details: Array<{
year: number;
month: number;
day: number;
timeIndex: number; // 15-min slot within the day (0 = 00:00, 95 = 23:45)
calories: number; // kcal for that slot
steps: number; // step count for that slot
distance: number; // metres for that slot
}>
}StepsError
Fired when no step data is available for the requested day.
// no payloadTodaySports
Today's sport activity summary totals. Emitted automatically on connect and on getTodaySports().
{
totalSteps: number; // total steps for today
runningSteps: number; // steps counted as running
calories: number; // total kcal burned
walkingDistance: number; // total distance in metres
activityDuration: number; // active minutes today
}Fatigue & Health Check Events
RealTimeFatigue
{ value: number }RealTimeFatigueError
// no payloadRealTimeFatigueStopped
// no payloadRealTimeHealthCheck
{ value: number }RealTimeHealthCheckError
// no payloadRealTimeHealthCheckStopped
// no payloadSystem / Misc Events
BigDataUnknown
Fired when a Big Data response is received with an unrecognised data ID.
{ dataId: number }Compatible Rings
The SDK automatically identifies rings whose BLE advertisement name starts with any of these prefixes:
| Prefix | Prefix | Prefix | Prefix |
|---|---|---|---|
| R01 | R02 | R03 | R04 |
| R05 | R06 | R07 | R09 |
| R10 | COLMI | VK-5098 | MERLIN |
| Hello Ring | RING1 | boAtring | TR-R02 |
| SE | EVOLVEO | GL-SR2 | Blaupunkt |
| KSIX RING | | | |
Sleep Protocol Selection
Newer rings use a richer multi-day Big Data sleep protocol. Always check DeviceCapabilities before fetching sleep data:
ColmiRing.on('DeviceCapabilities', (caps) => {
if (caps.useNewSleepProtocol) {
// Newer ring — detailed stage periods, multi-day history
ColmiRing.getSleepBigData();
} else {
// Older ring — single-night summary, legacy protocol
const yesterdayMidnightMs = Date.now() - 86_400_000;
// use your legacy sleep call here if implemented
}
});Changelog
1.1.0
- Added
getTemperatureBigData()— fetch 48-slot / 30-min temperature history per day (Big Data ID 37) - Added
TemperatureHistoryevent withslotIndex,timestampMs,celsius,fahrenheit,raw,daysAgo - Fixed temperature slot index off-by-one: timestamps are now aligned correctly to 00:00 (slot 0) with no spurious +30 min offset
- Added
RealTimeTemperature,RealTimeTemperatureError,RealTimeTemperatureStoppedevents - Added
startRealTimeTemperature()/stopRealTimeTemperature()methods - Added
startRealTimeFatigue()/stopRealTimeFatigue()withRealTimeFatigue*events - Added
startRealTimePressure()/stopRealTimePressure()withRealTimePressure*events - Added
getPressureHistory()/PressureHistoryevent withrangeMinutes - Added
getTemperatureSettings()/setTemperatureSetting()/TemperatureSettingsevent - Added
getPressureSettings()/setPressureSetting()/PressureSettingsevent - Added
getBPSettings()/setBPSettings()/BPSettingsevent - Added
getBloodOxygenSettings()/SpO2Settingsevent - Added
getHRVSettings()/HRVSettingsevent HRVHistoryevent now includesrangeMinutesSpO2BigDatasamples now includedaysAgo,dateMs, andslotMsfieldsSleepDatanow includesremMinutesfield- Init sequence now reads all settings (BP, SpO2, Pressure, HRV, Temperature) on connect
TodaySportsis now also auto-emitted on connectBigDataUnknownevent added for unrecognised Big Data IDsBigDataReadyevent added when Big Data BLE service is ready
1.0.0
- Initial release
License
MIT © Balasuriya
