npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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)

Readme

react-native-colmi-ring

npm version license platform

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

# npm
npm install react-native-colmi-ring

# yarn
yarn add react-native-colmi-ring

# pnpm
pnpm add react-native-colmi-ring

No 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 payload

BluetoothDisabled

Fired when Bluetooth is turned off or unavailable.

// no payload

DeviceFound

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 payload

RingDisconnected

Fired when the ring disconnects or disconnect() is called.

// no payload

RingRebooting

Fired immediately after reboot() is called.

// no payload

BigDataReady

Fired when the Big Data BLE service is discovered and ready (newer rings only).

// no payload

Device Info Events

TimeSet

Fired after the ring's time is synced.

// no payload

BatteryStatus

{
  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 payload

HeartRateLogSettings

{
  enabled:         boolean;
  intervalMinutes: number;
}

RealTimeHeartRate

{ value: number }  // heart rate in bpm

RealTimeHeartRateError

Fired when the ring reports an error during real-time HR measurement.

// no payload

RealTimeHeartRateStopped

Fired when real-time HR measurement has stopped.

// no payload

Blood 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 payload

RealTimeBloodPressureStopped

Fired when real-time BP measurement has stopped.

// no payload

SpO2 (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 payload

RealTimeSpO2Stopped

// no payload

HRV 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 payload

RealTimeHRVStopped

// no payload

Pressure / 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 score

RealTimePressureError

// no payload

RealTimePressureStopped

// no payload

Temperature 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 payload

RealTimeTemperatureStopped

// no payload

Sleep 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 payload

TodaySports

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 payload

RealTimeFatigueStopped

// no payload

RealTimeHealthCheck

{ value: number }

RealTimeHealthCheckError

// no payload

RealTimeHealthCheckStopped

// no payload

System / 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 TemperatureHistory event with slotIndex, 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, RealTimeTemperatureStopped events
  • Added startRealTimeTemperature() / stopRealTimeTemperature() methods
  • Added startRealTimeFatigue() / stopRealTimeFatigue() with RealTimeFatigue* events
  • Added startRealTimePressure() / stopRealTimePressure() with RealTimePressure* events
  • Added getPressureHistory() / PressureHistory event with rangeMinutes
  • Added getTemperatureSettings() / setTemperatureSetting() / TemperatureSettings event
  • Added getPressureSettings() / setPressureSetting() / PressureSettings event
  • Added getBPSettings() / setBPSettings() / BPSettings event
  • Added getBloodOxygenSettings() / SpO2Settings event
  • Added getHRVSettings() / HRVSettings event
  • HRVHistory event now includes rangeMinutes
  • SpO2BigData samples now include daysAgo, dateMs, and slotMs fields
  • SleepData now includes remMinutes field
  • Init sequence now reads all settings (BP, SpO2, Pressure, HRV, Temperature) on connect
  • TodaySports is now also auto-emitted on connect
  • BigDataUnknown event added for unrecognised Big Data IDs
  • BigDataReady event added when Big Data BLE service is ready

1.0.0

  • Initial release

License

MIT © Balasuriya