Sensors

Six motion and environment sensors, bound to expo-sensors: Accelerometer, Gyroscope, Magnetometer, DeviceMotion, Barometer and LightSensor.

Import
import { Sensor } from '@ng-native/expo';

Every one of them is the same object underneath - addListener, setUpdateInterval, isAvailableAsync - so this is one Sensor class parameterised by the reading, not one per sensor. That is also why these are injection tokens rather than six @Service() classes: a @Service() class cannot be generic in the thing it reports, so each sensor is a distinct InjectionToken<Sensor<T>> that shares the one implementation.

Install

npx expo install expo-sensors
import {
  Accelerometer,
  Gyroscope,
  Magnetometer,
  DeviceMotion,
  Barometer,
  LightSensor,
} from '@ng-native/expo/sensors';

The smallest useful example

import { Component, DestroyRef, inject } from '@angular/core';
import { Accelerometer } from '@ng-native/expo/sensors';

@Component({
  selector: 'app-shake-detector',
  template: `<text>z: {{ motion.reading().z.toFixed(2) }}</text>`,
})
export class ShakeDetector {
  protected readonly motion = inject(Accelerometer);

  constructor() {
    inject(DestroyRef).onDestroy(this.motion.start(50));
  }
}

Nothing is subscribed until start is called, and the interval is explicit rather than defaulted away: the sensible interval is a property of what the app is doing with the reading - a compass wants a tenth of a second, a shake detector less - and every event is a change-detection pass, so a sensor left at its default is a phone that never idles.

What each one reports

  • Accelerometer - { x, y, z } in g. Includes gravity, so a phone lying flat reads about 1 on z.
  • Gyroscope - { x, y, z }, rotation in radians per second.
  • Magnetometer - { x, y, z }, the magnetic field in microteslas. What a compass is built on.
  • DeviceMotion - everything at once: acceleration, accelerationIncludingGravity, rotation, rotationRate, orientation, interval.
  • Barometer - { pressure, relativeAltitude }, pressure in hectopascals. Present on fewer devices than the rest; check available before relying on it.
  • LightSensor - { illuminance } in lux. Android only.

Each is read the same way regardless of the shape:

  • reading - a signal, the most recent value, or the stated zero until the sensor has said anything.
  • available - boolean | null. Null until the platform has answered, which is always one turn away even though the underlying question is really a constant - isAvailableAsync is asynchronous regardless. A signal rather than the promise the module itself offers, because the question is normally asked in a template (@if (barometer.available())), and a promise there is truthy on the first frame and every frame after: exactly the failure this project keeps guarding against.
  • start(intervalMs = 100) - starts the listener at that interval and returns the function that stops it. Calling it again restarts at the new interval.
  • stop() - what the returned function calls. Call stop() or the returned cleanup function when you no longer need readings.

Without the module installed

available resolves to false. reading stays at the stated zero (all axes 0, or the equivalent rest state for DeviceMotion). start() returns a function that does nothing.

Reference

Sensorclass@ng-native/expo
import { Sensor } from '@ng-native/expo';

Methods

start(intervalMs = 100): () => void

Start reading, at intervalMs between readings. Returns the function that stops.

Angular components as native iOS and Android views, built and shipped with Expo.

An alpha. MIT licensed. Sponsor its development.

An independent project, not affiliated with or endorsed by Google, the Angular team or Expo. Angular is a trademark of Google LLC.