@oraculi/telematics-sdk
v0.1.3
Published
Telematics SDK for React Native
Readme
@oraculi/telematics-sdk
Telematics SDK for React Native (Nitro Modules).
Installation
npm install @oraculi/telematics-sdk react-native-nitro-modulescd ios && pod installAutolinking is enabled by default with the React Native CLI. If autolinking is disabled, follow
the React Native manual linking guide and point iOS to TelematicsSdk.podspec and Android to
node_modules/@oraculi/telematics-sdk/android.
Permissions
iOS (Info.plist):
NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescriptionNSMotionUsageDescriptionUIBackgroundModeswithlocation(required for background tracking)- Trip notifications request notification permission at runtime when enabled.
Android (AndroidManifest.xml):
android.permission.ACCESS_FINE_LOCATIONandroid.permission.ACCESS_COARSE_LOCATIONandroid.permission.ACCESS_BACKGROUND_LOCATION(Android 10+, requested by the SDK)android.permission.POST_NOTIFICATIONS(Android 13+ for trip notifications)android.permission.FOREGROUND_SERVICEandroid.permission.FOREGROUND_SERVICE_LOCATION
Note: The SDK always requests background location permission to enable background tracking.
Usage
import {
configure,
requestPermission,
startTracking,
stopTracking,
addListener,
removeListener,
} from '@oraculi/telematics-sdk';
configure({
apiKey: 'demo-key',
trackingMode: 'driving',
syncIntervalMinutes: 15,
});
await requestPermission();
startTracking();
const subId = addListener('scoreUpdated', ({ score }) => {
console.log('Score', score?.safetyScore);
});
// Later
removeListener(subId);
stopTracking();Presets
You can pass a preset string to apply a preconfigured setup. Any explicit fields you pass override
the preset defaults. Use preset: 'none' to skip preset defaults entirely. If you omit preset,
the SDK defaults to none (fully customizable).
configure({
preset: 'ride_hailing',
apiKey: 'demo-key',
});Available presets:
ride_hailing(high accuracy, always-on tracking)delivery(balanced accuracy, always-on tracking)car_rental(driving-only tracking, lower sampling)insurance(high accuracy, driving-only tracking)
Initialization
The SDK initializes itself by calling a native-only init endpoint using the apiKey. Initialization
is asynchronous, and tracking won't start until the init endpoint returns a 2xx response. If init
fails, startTracking() is a no-op and getTrackingState().lastError is populated.
The SDK posts to:
https://backoffice.speezydev.com/api/api-keys/verify(POST)
Headers:
Content-Type: application/jsonx-api-key: <apiKey>
Sync (trip uploads)
Trips are submitted to:
https://backoffice.speezydev.com/api/trips(POST)
Headers:
Content-Type: application/jsonx-api-key: <apiKey>
Payload shape (top-level):
externalId: trip idstartedAt: ISO stringendedAt: ISO string (optional)distanceKm: numberpayload: full telematics payload (trip/events/samples)
Events
Supported events:
tripStartedtripEndedscoreUpdatedsyncStateinitState(messageispending,ready, orfailed)error(emitted whenlastErrorchanges)
Error payloads include errorCode (stable identifier) and errorMessage (human-readable). Common
codes: not_initialized, permission_denied, init_missing_api_key, init_invalid_endpoint,
init_payload_failed, init_failed, location_error, context_unavailable (Android only).
Trip notifications
By default, the SDK posts local notifications when a trip starts, is in progress, and ends. Disable
them by setting tripNotificationsEnabled: false if you want to provide your own notifications.
Example app
The example app lives in examples/react-native and installs the published SDK from npm.
- Update the API key in
examples/react-native/App.tsx. - The SDK is wired to the backoffice endpoints above; change the native endpoint constants if you need a different backend.
- Run the app using the steps in
examples/react-native/README.md.
Shared core
The shared C++ core lives in packages/telematics-core/cpp and is used by native targets via
CMake (Android) and CocoaPods (iOS). This keeps the core logic reusable for the future Swift SDK.
API Reference
TelematicsConfig
apiKey?: string(required for init)preset?: string(ride_hailing,delivery,car_rental,insurance, ornone; default:none)trackingMode?: 'driving' | 'always' | 'manual'(default:driving)accuracy?: 'low' | 'balanced' | 'high'(default:balanced)syncIntervalMinutes?: number(default:15, minimum:15, maximum:1440)retentionDays?: number(default:30)scoringWeights?: { braking?: number; accel?: number; speeding?: number; phoneUse?: number; cornering?: number }- defaults: braking
5, accel5, speeding3, phoneUse8, cornering3
- defaults: braking
efficiencyWeights?: { idling?: number; routeEfficiency?: number }- defaults: idling
40, routeEfficiency60
- defaults: idling
sampleIntervalMs?: number(default:1000, minimum:250)sampleDistanceMeters?: number(default:5)sampleMaxPoints?: number(default:1800, clamped to0..10000)distanceFilter?: number(meters; overrides the base native distance filter)locationUpdateIntervalMs?: number(minimum time between location updates; Android native, iOS throttled)locationElasticityEnabled?: boolean(default:true)- when
true, tightens distance/time filters as speed increases
- when
locationFilterEnabled?: boolean(default:false)- enables C++ location smoothing + acceptance gating before trip/event detection
locationFilterUseKalman?: boolean(default:true)locationFilterMinDistanceMeters?: number(default:3)locationFilterMaxAccuracyMeters?: number(default:50)locationFilterMaxSpeedMps?: number(default:55)locationFilterSpeedWindowSize?: number(default:5)locationFilterHeadingWindowSize?: number(default:5)autoTripDetectionEnabled?: boolean(default:true)- set to
falseto disable automatic trip start/end detection
- set to
walkingModeEnabled?: boolean(default:false)- lowers the movement threshold for walking or mixed travel tests
tripNotificationsEnabled?: boolean(default:true)- set to
falseto disable built-in trip notifications
- set to
manualTripControlEnabled?: boolean(default:false)- debug-only: when
true,startTracking()begins a trip andstopTracking()ends it
- debug-only: when
Methods
configure(config: TelematicsConfig): void- Non-blocking; triggers native initialization and applies config.
requestPermission(): Promise<PermissionStatus>checkPermission(): PermissionStatusstartTracking(): void- No-op if init has not succeeded or permission is denied.
stopTracking(): voidgetTrackingState(): TrackingStategetTripHistory(limit?: number): TripSummary[]getCurrentTrip(): TripSummary | undefinedgetCurrentScore(): DrivingScore | undefinedsyncNow(): Promise<boolean>- Returns
falseif init has not succeeded.
- Returns
deleteLocalData(): voidaddListener(event: TelematicsEvent, callback): stringremoveListener(subscriptionId: string): void
Data models
TrackingState:{ isTracking, lastError?, lastPosition?, permission }LocationPoint:{ latitude, longitude, speedMps?, accuracyM?, timestampMs }TripSummary:{ tripId, startTime, endTime?, distanceMeters, durationSeconds, avgSpeedMps, maxSpeedMps, safetyScore, efficiencyScore?, events? }DrivingEvent:{ type, timestamp, lat, lng, speedMps?, severity? }DrivingScore:{ safetyScore, efficiencyScore?, factors }TelematicsEventPayload:{ trip?, score?, message?, errorCode?, errorMessage? }
Background behavior
- iOS: Significant Location Changes + stationary geofence are used for low-power wakeups. If the user force-quits the app, iOS will not relaunch it for location events.
- iOS requires "Always" location permission for background tracking; "When In Use" stops updates.
- Android: Foreground service keeps tracking active. If the user force-stops the app, tracking and uploads stop until the app is opened again.
Troubleshooting
Initialization fails
- Ensure
apiKeyis set inconfigure(). - Verify the native init endpoint returns 2xx.
- Check network connectivity and TLS configuration.
- If you are still using the dummy endpoint, replace it in native code.
Permission denied
- iOS: confirm Info.plist keys are present and permissions were granted.
- iOS:
requestPermission()will attempt to upgrade When In Use -> Always if needed. - Android: ensure location permissions are in the manifest and approved at runtime.
- Background location permission is required for tracking while the app is in the background.
No trips recorded
- Make sure
startTracking()is called afterrequestPermission()resolves. - Confirm init has succeeded (no
lastErrorandstartTracking()is not a no-op). - Trips require movement; verify on a device with GPS and motion.
- For walking or mixed travel tests, set
walkingModeEnabled: true. - For debug-only manual trips, set
manualTripControlEnabled: trueand use start/stop.
Sync not happening
- Init must succeed before sync can run.
syncIntervalMinutesmust be > 0 (minimum 15).- Call
syncNow()to force a run and inspect the result.
Unexpected battery drain
- Use
accuracy: 'balanced'unless high precision is required. - Consider longer sampling intervals and distances for low-frequency use cases.
FAQ
Q: Can I disable presets?
A: Yes. Omit preset or set preset: 'none'.
Q: Can I call configure() more than once?
A: Yes. The SDK applies the latest config and re-runs initialization.
Contributing
License
MIT
Made with create-react-native-library
