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-polar

v1.0.0

Published

React Native bridge for Polar sensors and watches on iOS and Android.

Readme

react-native-polar

React Native bridge around Polar's BLE SDK for live Polar sensor data.

See PARITY.md for the current SDK-to-bridge parity audit, including the small set of SDK hooks that remain intentionally internal or not locally provable from the iOS binary distribution.

Scope

  • Connection lifecycle
  • Device discovery
  • Device time setup
  • SDK mode toggling and status checks
  • H10 exercise recording lifecycle and fetch/remove access
  • Generic offline recording settings, status, trigger, list/fetch/remove, and start/stop access
  • Passive HR broadcast listening
  • Auto-connect and automatic reconnection controls
  • FTU status/configuration and user device settings/configuration
  • Activity and sleep data reads
  • Direct device-administration helpers for charger state, connection mode, telemetry, sync, restart/reset, and data deletion
  • Device utility reads for battery, RSSI, name, and disk space
  • Connection wait, ATT MTU, device file access, feature-readiness checks, pairing-removal diagnostics, warehouse-sleep control, and device log-configuration read/write helpers
  • Battery and heart-rate telemetry
  • Online stream capability queries
  • Hook-based JS API for:
    • HR
    • ECG
    • ACC
    • PPG
    • PPI
    • GYRO
    • MAGNETOMETER
    • PRESSURE
    • LOCATION
    • TEMPERATURE
    • SKIN_TEMPERATURE

The TypeScript surface and native bridge expose the stream and H10 exercise-recording APIs above. Automated JS, Android, and iOS example checks cover the shipped surface; real-device validation is still required for BLE behavior and stream fidelity.

Install

npm install react-native-polar

Expo:

  • Use this package with Expo development builds or prebuild, not Expo Go. Expo Go cannot load arbitrary native modules.
  • Install the library in your Expo app, add the config plugin, then regenerate native projects.
  • Set Android minSdkVersion to 33; Polar's Android SDK 8.x does not support lower API levels.
{
  "expo": {
    "plugins": [
      [
        "react-native-polar",
        {
          "iosBluetoothPermission": "Allow $(PRODUCT_NAME) to connect to nearby Polar sensors over Bluetooth."
        }
      ],
      [
        "expo-build-properties",
        {
          "android": {
            "minSdkVersion": 33
          }
        }
      ]
    ]
  }
}
npx expo install react-native-polar expo-dev-client expo-build-properties
npx expo prebuild
npx expo run:ios
npx expo run:android

The config plugin adds the required Bluetooth usage descriptions on iOS, the Android 12+ BLE permissions (BLUETOOTH_SCAN with neverForLocation and BLUETOOTH_CONNECT), and sets android.enableJetifier=false in gradle.properties for Polar SDK 8 artifacts. If your app derives location from BLE scan results, override androidPermissions with a plain android.permission.BLUETOOTH_SCAN entry plus the location permissions you need.

iOS:

cd ios
pod install

Android:

  • Polar's current Android SDK line requires minSdkVersion 33.
  • The current Android bridge path also expects a Kotlin 2.3-era Android toolchain.
  • If the host app still enables Jetifier, disable it for Polar SDK 8 artifacts with android.enableJetifier=false.
  • Ensure Bluetooth permissions are declared and requested at runtime.
  • The library depends on Polar's Android BLE SDK through Gradle.

Usage

import React, { useEffect } from 'react';
import { Text, View } from 'react-native';
import { usePolarDevice } from 'react-native-polar';

export function PolarScreen() {
  const deviceId = '8A1F2C4D';
  const polar = usePolarDevice(deviceId);

  useEffect(() => {
    polar.onEcgData((event) => {
      console.log('ECG samples', event.samples.length);
    });

    void polar.startEcg();
    return () => {
      void polar.stopEcg();
    };
  }, [polar]);

  return (
    <View>
      <Text>{polar.connectionState}</Text>
      <Text>{polar.heartRate ?? '--'} bpm</Text>
      <Text>{polar.batteryLevel ?? '--'}%</Text>
    </View>
  );
}

Connection ownership

Mounting usePolarDevice(deviceId) connects to the device and unmounting disconnects it, but connections are reference-counted across hook instances: the first mount for a given deviceId issues the native connect, later mounts adopt the existing connection state, and only the last unmount stops active streams and disconnects. Calling disconnect() explicitly always disconnects immediately, even if other hook instances still use the device. usePolarHrBroadcasts() shares the global broadcast listener the same way — it stops only when the last broadcast hook unmounts.

API

  • usePolarDevice(deviceId)
  • usePolarH10(deviceId) compatibility alias
  • usePolarDiscovery()
  • usePolarDiscovery().startScan(service?)
  • usePolarHrBroadcasts(deviceIds?)
  • startDeviceScan(service?)
  • stopDeviceScan()
  • autoConnectToDevice(options)
  • setAutomaticReconnection(enabled)
  • setPolarFilter(enabled)
  • foregroundEntered()
  • cleanup()
  • shutDown()
  • setApiLogger(listener)
  • startBackgroundRecording(deviceId, sessionId, streams)
  • stopBackgroundRecording(deviceId)
  • getPendingRecordingChunks(sessionId?)
  • readRecordingChunk(path)
  • deleteRecordingChunk(path)
  • getBackgroundRecordingStatus()
  • isFtuDone(deviceId)
  • getUserPhysicalConfiguration(deviceId)
  • getUserDeviceSettings(deviceId)
  • setUserDeviceSettings(deviceId, settings)
  • setUserDeviceLocation(deviceId, location)
  • doFirstTimeUse(deviceId, config)
  • getAvailableOnlineStreamDataTypes(deviceId)
  • getAvailableHRServiceDataTypes(deviceId)
  • requestStreamSettings(deviceId, dataType)
  • requestFullStreamSettings(deviceId, dataType)
  • getSteps(deviceId, range)
  • getActivitySampleData(deviceId, range)
  • getDistance(deviceId, range)
  • getActiveTime(deviceId, range)
  • getCalories(deviceId, range, type)
  • getDailySummaryData(deviceId, range)
  • get247HrSamples(deviceId, range)
  • get247PpiSamples(deviceId, range)
  • getNightlyRecharge(deviceId, range)
  • getSleep(deviceId, range)
  • getSkinTemperature(deviceId, range)
  • getSpo2TestData(deviceId, range)
  • getSleepRecordingState(deviceId)
  • stopSleepRecording(deviceId)
  • getDeviceTime(deviceId)
  • setDeviceTime(deviceId, iso8601)
  • getChargerState(deviceId)
  • getMultiBleConnectionMode(deviceId)
  • setUsbConnectionMode(deviceId, enabled)
  • setAutomaticTrainingDetectionSettings(deviceId, settings)
  • setTelemetryEnabled(deviceId, enabled)
  • setAutomaticOhrMeasurementEnabled(deviceId, enabled)
  • setMultiBleConnectionMode(deviceId, enabled)
  • setDaylightSavingTime(deviceId)
  • doRestart(deviceId)
  • doFactoryReset(deviceId, preservePairing?)
  • turnDeviceOff(deviceId)
  • sendInitializationAndStartSyncNotifications(deviceId)
  • sendTerminateAndStopSyncNotifications(deviceId)
  • listRestApiServices(deviceId)
  • getRestApiDescription(deviceId, path)
  • putNotification(deviceId, notification, path)
  • receiveRestApiEvents(deviceId, listener)
  • deleteStoredDeviceData(deviceId, dataType, date)
  • deleteDeviceDateFolders(deviceId, range)
  • deleteTelemetryData(deviceId)
  • isSdkModeEnabled(deviceId)
  • enableSdkMode(deviceId)
  • disableSdkMode(deviceId)
  • setLedConfig(deviceId, config)
  • checkFirmwareUpdate(deviceId)
  • updateFirmware(deviceId, version?)
  • getWatchFaceConfig(deviceId)
  • setWatchFaceConfig(deviceId, config)
  • getTrainingSessionReferences(deviceId, range)
  • deleteTrainingSession(deviceId, reference)
  • getTrainingSession(deviceId, reference)
  • getTrainingSessionWithProgress(deviceId, reference)
  • startExercise(deviceId, sportProfile)
  • pauseExercise(deviceId)
  • resumeExercise(deviceId)
  • stopExercise(deviceId)
  • getExerciseStatus(deviceId)
  • startExerciseRecording(deviceId, identifier, interval, sampleType)
  • stopExerciseRecording(deviceId)
  • getExerciseRecordingStatus(deviceId)
  • listExercises(deviceId)
  • fetchExercise(deviceId, entry)
  • removeExercise(deviceId, entry)
  • startOfflineExerciseV2(deviceId, sportProfile)
  • stopOfflineExerciseV2(deviceId)
  • getOfflineExerciseStatusV2(deviceId)
  • listOfflineExercisesV2(deviceId, directoryPath?)
  • fetchOfflineExerciseV2(deviceId, entry)
  • removeOfflineExerciseV2(deviceId, entry)
  • isOfflineExerciseV2Supported(deviceId)
  • getAvailableOfflineRecordingDataTypes(deviceId)
  • requestOfflineRecordingSettings(deviceId, dataType)
  • requestFullOfflineRecordingSettings(deviceId, dataType)
  • getOfflineRecordingStatus(deviceId)
  • listOfflineRecordings(deviceId)
  • getOfflineRecord(deviceId, entry, secret?)
  • getOfflineRecordWithProgress(deviceId, entry, secret?)
  • getSubRecordingCount(deviceId, entry)
  • listSplitOfflineRecordings(deviceId)
  • getSplitOfflineRecord(deviceId, entry, secret?)
  • removeOfflineRecord(deviceId, entry)
  • removeOfflineRecords(deviceId, entry)
  • startOfflineRecording(deviceId, dataType, settings, secret?)
  • stopOfflineRecording(deviceId, dataType)
  • getOfflineRecordingTrigger(deviceId)
  • setOfflineRecordingTrigger(deviceId, trigger, secret?)
  • startHrBroadcastListening(deviceIds?)
  • stopHrBroadcastListening()
  • getBatteryLevel(deviceId)
  • getRssiValue(deviceId)
  • getDeviceName(deviceId)
  • getDiskSpace(deviceId)
  • waitForConnection(deviceId)
  • readFile(deviceId, path)
  • getFile(deviceId, path)
  • writeFile(deviceId, path, data)
  • deleteFileOrDirectory(deviceId, path)
  • getFileList(deviceId, path, recursive)
  • getLogConfig(deviceId)
  • setLogConfig(deviceId, config)
  • isFeatureReady(deviceId, feature)
  • setWareHouseSleep(deviceId)
  • checkIfDeviceDisconnectedDueRemovedPairing(deviceId)
  • setMtu(mtu)

Platform Notes

  • getAvailableOnlineStreamDataTypes(deviceId) returns the data types reported by the connected device and current SDK mode. It is not a hard-coded capability list.
  • startLocation() / stopLocation() and onLocationData() are Android-only in practice. Polar's Android SDK 8.x exposes startLocationStreaming, while the current iOS SDK does not, so iOS rejects those calls.
  • foregroundEntered() is Android-only in practice. The Polar iOS SDK does not expose a matching lifecycle hook, so iOS rejects this call.
  • setMtu(mtu) is Android-only in practice. The Polar iOS SDK does not expose ATT MTU configuration, so iOS rejects this call.
  • getSubRecordingCount(deviceId, entry) and removeOfflineRecords(deviceId, entry) are iOS-only in practice. Polar's iOS SDK 8.x still exposes these deprecated split-record helpers, while Polar's Android SDK 8.x no longer does, so Android rejects these calls.
  • startBackgroundRecording(deviceId, sessionId, streams) is a bridge-level convenience, not a Polar SDK feature. It spools HR, ECG, ACC, and PPI samples to on-device JSONL chunk files natively, so recording survives JS reloads. Use getPendingRecordingChunks(), readRecordingChunk(), and deleteRecordingChunk() to drain the spool. Available on both iOS and Android; the spool stops when the device disconnects.

Hook fields:

  • connectionState
  • heartRate
  • rrIntervals
  • batteryLevel
  • contactStatus
  • error
  • sdkModeEnabled
  • deviceTime
  • availableStreamDataTypes
  • readyFeatures

Hook methods:

  • startStream(streamType)
  • stopStream(streamType)
  • refreshAvailableStreamDataTypes()
  • disconnect()
  • reconnect()
  • getDeviceTime()
  • setDeviceTime(iso8601)
  • isSdkModeEnabled()
  • enableSdkMode()
  • disableSdkMode()
  • checkFirmwareUpdate()
  • updateFirmware(version?)
  • getWatchFaceConfig()
  • setWatchFaceConfig(config)
  • waitForConnection()
  • readFile(path)
  • getFile(path)
  • writeFile(path, data)
  • deleteFileOrDirectory(path)
  • getFileList(path, recursive)
  • getLogConfig()
  • setLogConfig(config)
  • isFeatureReady(feature)
  • setWareHouseSleep()
  • checkIfDeviceDisconnectedDueRemovedPairing()
  • getTrainingSessionReferences(range)
  • deleteTrainingSession(reference)
  • getTrainingSession(reference)
  • getTrainingSessionWithProgress(reference)
  • startExercise(sportProfile)
  • pauseExercise()
  • resumeExercise()
  • stopExercise()
  • getExerciseStatus()
  • observeExerciseStatus()
  • stopObservingExerciseStatus()
  • startExerciseRecording(identifier, interval, sampleType)
  • stopExerciseRecording()
  • getExerciseRecordingStatus()
  • listExercises()
  • fetchExercise(entry)
  • removeExercise(entry)
  • getAvailableOfflineRecordingDataTypes()
  • requestOfflineRecordingSettings(dataType)
  • requestFullOfflineRecordingSettings(dataType)
  • getOfflineRecordingStatus()
  • listOfflineRecordings()
  • getOfflineRecord(entry, secret?)
  • removeOfflineRecord(entry)
  • startOfflineRecording(dataType, settings, secret?)
  • stopOfflineRecording(dataType)
  • getOfflineRecordingTrigger()
  • setOfflineRecordingTrigger(trigger, secret?)
  • getBatteryLevel()
  • getChargerState()
  • getRssiValue()
  • getDeviceName()
  • getDiskSpace()
  • getSteps(range)
  • getActivitySampleData(range)
  • getDistance(range)
  • getActiveTime(range)
  • getCalories(range, type)
  • getDailySummaryData(range)
  • get247HrSamples(range)
  • get247PpiSamples(range)
  • getNightlyRecharge(range)
  • getSleep(range)
  • getSleepRecordingState()
  • stopSleepRecording()
  • isFtuDone()
  • getUserPhysicalConfiguration()
  • getUserDeviceSettings()
  • getMultiBleConnectionMode()
  • setUserDeviceSettings(settings)
  • setUserDeviceLocation(location)
  • setUsbConnectionMode(enabled)
  • setAutomaticTrainingDetectionSettings(settings)
  • setTelemetryEnabled(enabled)
  • setAutomaticOhrMeasurementEnabled(enabled)
  • setMultiBleConnectionMode(enabled)
  • setDaylightSavingTime()
  • doFirstTimeUse(config)
  • doRestart()
  • doFactoryReset(preservePairing?)
  • turnDeviceOff()
  • sendInitializationAndStartSyncNotifications()
  • sendTerminateAndStopSyncNotifications()
  • deleteStoredDeviceData(dataType, date)
  • deleteDeviceDateFolders(range)
  • deleteTelemetryData()
  • setLedConfig(config)

Auto-connect options:

  • rssiLimit
  • service?
  • deviceType?

Configuration payloads:

  • PolarFirstTimeUseConfig
  • PolarPhysicalConfiguration
  • PolarUserDeviceSettings
  • PolarUserDeviceLocation
  • PolarUserGender
  • PolarUserTypicalDay

Discovery hook fields:

  • isScanning
  • devices
  • startScan()
  • stopScan()
  • clearDevices()

HR broadcast hook fields:

  • isListening
  • broadcasts
  • startListening()
  • stopListening()
  • clearBroadcasts()

Connection helpers:

  • autoConnectToDevice({ rssiLimit, service?, deviceType? })
  • setAutomaticReconnection(enabled)

Convenience methods:

  • startHr() / stopHr()
  • startEcg() / stopEcg()
  • startAcc() / stopAcc()
  • startPpg() / stopPpg()
  • startPpi() / stopPpi()
  • startGyro() / stopGyro()
  • startMagnetometer() / stopMagnetometer()
  • startPressure() / stopPressure()
  • startTemperature() / stopTemperature()
  • startSkinTemperature() / stopSkinTemperature()

Stream listeners:

  • onEcgData()
  • onAccelerometerData()
  • onPpgData()
  • onPpiData()
  • onGyroData()
  • onMagnetometerData()
  • onPressureData()
  • onTemperatureData()
  • onSkinTemperatureData()
  • onExerciseStatus()

Testing

npm run build
npm run lint
npm test -- --runInBand

Example app verification:

cd example
npm run lint
npm test -- --runInBand
cd android
./gradlew assembleDebug
cd ios
xcodebuild -workspace PolarExample.xcworkspace -scheme PolarExample -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 15,OS=18.4' test CODE_SIGNING_ALLOWED=NO

See TESTING.md for physical-device validation notes.

Example

See example/README.md for the test app scaffold.