1.0.0 • Published 2 years ago

@awarns/wear-os v1.0.0

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
2 years ago

@awarns/wear-os

npm (scoped) npm

This module allows to collect data from the sensors of an Android WearOS smartwatch, and enables the communication between both devices (i.e., smartphone and smartwatch).

This plugin acts as a wrapper on top of the nativescript-wearos-sensors plugin, which enables the communication and the data collection from a paired WearOS smartwatch. In order to use these features, a WearOS smartwatch must be paired with the smartphone and have installed a counterpart application, developed using the WearOSSensors WearOS library.

Note: check the requirements of both libraries for more detailed information:

Install the plugin using the following command line instruction:

ns plugin add @awarns/wear-os

Usage

Setup

This plugin requires you to register its loader during the framework initialization, like this:

// ... platform imports
import { awarns } from '@awarns/core';
import { demoTasks } from '../tasks';
import { demoTaskGraph } from '../graph';
import { registerWearOSPlugin } from '@awarns/wear-os';

awarns
  .init(
    demoTasks,
    demoTaskGraph,
    [
      registerWearOSPlugin(config), // Parameter config is optional
    ]
  )
  // ... handle initialization promise

Plugin loader parameter (WearOSPluginConfig):

ParameterTypeDescription
sensors?WatchSensor[]Enable the specified sensors. By default, all sensors are enabled.
enablePlainMessaging?booleanEnable the plain messaging communication mechanism. Default: false.
enableWearCommands?booleanEnable the command mechanism that allows to start the data collection on the watch side. Default: false.

In addition, you also have to specify if you want to use these plugin features and which watch you want to use. This offers to possibility to use or not these plugin features without modifying the task graph definition. For example, you can disable the features if there isn't a paired watch available. Here is an example:

import { getConnectedWatches, setWatchFeaturesState, useWatch } from '@awarns/wear-os';

export async function setupWatchToUse(): Promise<void> {
  const watches = await getConnectedWatches();

  if (watches.length === 0) {
    console.log('No WearOS watches connected! Disabling wear-os plugin features...');
    setWatchFeaturesState(false);
    return;
  }

  const watch = watches[0];
  console.log(`Setup wear-os plugin to use ${watch.name} watch!`);
  setWatchFeaturesState(true);
  useWatch(watch);
}

Tasks

Task nameDescription
startDetecting{prefix}Watch{sensor}ChangesAllows to start the data collection for a sensor with a specific configuration (see below). The prefix can be used to distinguish among different configurations.
stopDetectingWatch{sensor}ChangesThe complement to the previous task. Allows to stop collecting data from sensor.
sendPlainMessageToWatchAllows to send a string-based message to the paired smartwatch. An example of use could be to send information for updating the UI.
sendPlainMessageToWatchAndAwaitResponseAllows to send a string-based message to the paired smartwatch and to wait for a response from it.

Start data collection for a sensor with specific configuration

To register these tasks for their use, you just need to import them and call their generator functions inside your application's task list:

import { Task } from '@awarns/core/tasks';
import {
  startDetectingWatchSensorChangesTask,
  WatchSensor,
  WatchSensorDelay,
} from '@awarns/wear-os';

export const demoTasks: Array<Task> = [
  startDetectingWatchSensorChangesTask(WatchSensor.ACCELEROMETER, { sensorDelay: WatchSensorDelay.NORMAL, batchSize: 50 }),
  // startDetectingWatchAccelerometerChanges

  startDetectingWatchSensorChangesTask(WatchSensor.ACCELEROMETER, { sensorDelay: WatchSensorDelay.FASTEST, batchSize: 50 }, 'Fast'),
  // startDetectingFastWatchAccelerometerChanges

  startDetectingWatchSensorChangesTask(WatchSensor.HEART_RATE, { sensorDelay: WatchSensorDelay.NORMAL, batchSize: 5 }),
  // startDetectingWatchHeartRateChanges

  startDetectingWatchSensorChangesTask(WatchSensor.GEOLOCATION, { sensorDelay: 5000, batchSize: 5 }),
  // startDetectingWatchGeolocationChanges
]

Warning: the data collection for a WatchSensor can only be started once, if startDetectingFastWatchAccelerometerChanges is executed after startDetectingWatchAccelerometerChanges and while the collection is in progress, startDetectingFastWatchAccelerometerChanges will be ignored.

Therefore, if you want to dynamically change the collection's configuration while the collection is in progress, you will have to stop the collection and start it again with the new desired configuration. However, due to the underlying communication APIs (i.e., Bluetooth), the order of the tasks is not guaranteed. This means that if the stop and start tasks are executed very close in time, the start task could be executed before the stop task in the smartwatch. If you really need to change the collection's configuration, you should wait a certain amount of time (e.g., 1 second) after the execution of the stop task before executing the start task.

Task generator parameters:

Parameter nameTypeDescription
sensorWatchSensorSensor to collect data from. See below.
providerConfigurationProviderConfigurationCollection's configuration of the task. See below.
prefix (Optional)stringAdds the prefix to the name of the task. Useful to create multiple tasks for the same sensor but with multiple configurations.
  • WatchSensor
ValueDescription
ACCELEROMETERRepresents the watch's accelerometer sensor.
GYROSCOPERepresents the watch's gyroscope sensor.
MAGNETOMETERRepresents the watch's magnetometer sensor.
HEART_RATERepresents the watch's heart rate monitor.
GEOLOCATIONRepresents the watch's GPS system.
  • ProviderConfiguration
PropertyTypeDescription
sensorDelayWatchSensorDelay | numberIndicates the time between two consecutive samples. It can be a WatchSensorDelay (i.e., UI, NORMAL, GAME or FASTEST) or a value in milliseconds. It is highly recommended to use values in milliseconds (and higher than 1000 ms) with WatchSensor.GEOLOCATION due to WatchSensorDelay it's oriented for high sampling rate sensors.
batchSizenumberIndicates the amount of samples to be delivered in each record.

Note: the sensorDelay is taken as a suggestion by the Android OS. Samples could be delivered at a smaller or higher rate.

Task output events:

These tasks don't produce significant events after they complete their execution aside from the regular {task-name}Finished events.

However, once the start task has finished running, relevant events will be emitted by the internal listeners while the data collection is active. These are listed below.

Example usage in the application task graph:

on('startEvent', run('startDetectingWatchAccelerometerChanges'));
on('startEvent', run('startDetectingWatchGyroscopeChanges'));
on('startEvent', run('startDetectingWatchMagnetometerChanges'));
on('startEvent', run('startDetectingWatchHeartRateChanges'));
on('startEvent', run('startDetectingWatchGeolocationChanges'));

on('watchAccelerometerSamplesAcquired', run('writeRecords'));
on('watchGyroscopeSamplesAcquired', run('writeRecords'));
on('watchMagnetometerSamplesAcquired', run('writeRecords'));
on('watchHeartRateSamplesAcquired', run('writeRecords'));
on('watchGeolocationAcquired', run('writeRecords'));
on('watchGeolocationAcquired', run('checkAreaOfInterestProximity', { nearbyRange: 100, offset: 15 }));

Note: To use the writeRecords or the checkAreaOfInterestProximity task, the persistence or geofencing package must be installed and configured, respectively. See persistence and geofencing package docs.

Stop data collection for a sensor

To register these tasks for their use, you just need to import them and call their generator functions inside your application's task list:

import { Task } from '@awarns/core/tasks';
import {
  stopDetectingWatchSensorChangesTask,
  WatchSensor,
} from '@awarns/wear-os';

export const demoTasks: Array<Task> = [
  stopDetectingWatchSensorChangesTask(WatchSensor.ACCELEROMETER), // stopDetectingWatchAccelerometerChanges
  stopDetectingWatchSensorChangesTask(WatchSensor.GYROSCOPE),     // stopDetectingWatchGyroscopeChanges
  stopDetectingWatchSensorChangesTask(WatchSensor.MAGNETOMETER),  // stopDetectingWatchMagnetometerChanges
  stopDetectingWatchSensorChangesTask(WatchSensor.HEART_RATE),    // stopDetectingWatchHeartRateChanges
  stopDetectingWatchSensorChangesTask(WatchSensor.GEOLOCATION),   // stopDetectingWatchGeolocationChanges
];

Note: a stop task of a specific WatchSensor can be used to stop the collection started by any start task for that WatchSensor no matter the specific configuration.

Task generator parameters:

Parameter nameTypeDescription
sensorWatchSensorSensor to stop the data collection from.

Task output events:

These tasks don't produce significant events after they complete their execution aside from the regular {task-name}Finished events.

Example usage in the application task graph:

on('startEvent', run('startDetectingWatchAccelerometerChanges').every(1, 'minute'));
on('startEvent', run('startDetectingWatchGyroscopeChanges').every(1, 'minute'));
on('startEvent', run('startDetectingWatchMagnetometerChanges').every(1, 'minute'));
on('startEvent', run('startDetectingWatchHeartRateChanges').every(1, 'minute'));
on('startEvent', run('startDetectingWatchGeolocationChanges').every(1, 'minute'));

on('watchAccelerometerSamplesAcquired', run('stopDetectingWatchAccelerometerChanges'));
on('watchGyroscopeSamplesAcquired', run('stopDetectingWatchGyroscopeChanges'));
on('watchMagnetometerSamplesAcquired', run('stopDetectingWatchMagnetometerChanges'));
on('watchHeartRateSamplesAcquired', run('stopDetectingWatchHeartRateChanges'));
on('watchGeolocationAcquired', run('stopDetectingWatchGeolocationChanges'));

Note: it makes no sense to use these tasks without using before their complementary tasks to start the data collection.

Send a message to the paired watch

Note: to be able to use this feature, the messaging feature must be enabled.

To register these tasks for their use, you just need to import them and call their generator functions inside your application's task list:

import { Task } from '@awarns/core/tasks';
import {
  sendPlainMessageToWatchTask
} from '@awarns/wear-os';

export const demoTasks: Array<Task> = [
  sendPlainMessageToWatchTask() // sendPlainMessageToWatch
];

Task generator parameters:

This task generators take no parameters

Task output events:

Example usage in the application task graph:

on('startEvent', run('sendPlainMessageToWatch', {
  plainMessage: {                                 
    message: 'Hi from the smartphone!!'
  }
}).every(1, 'minute'));

on('plainMessageSent', run('writeRecords'));

Note: To use the writeRecords task, the persistence package must be installed and configured. See persistence package docs.

Send a message to the paired watch and wait for a response

Note: to be able to use this feature, the messaging feature must be enabled.

To register these tasks for their use, you just need to import them and call their generator functions inside your application's task list:

import { Task } from '@awarns/core/tasks';
import {
  sendPlainMessageToWatchAndAwaitResponseTask
} from '@awarns/wear-os';

export const demoTasks: Array<Task> = [
  sendPlainMessageToWatchAndAwaitResponseTask() // sendPlainMessageToWatchAndAwaitResponse
];

Task generator parameters:

This task generators take no parameters

Task output events:

Example usage in the application task graph:

on('startEvent', run('sendPlainMessageToWatchAndAwaitResponse', {
  plainMessage: {
    message: 'Tell me something ;)'
  },
  timeout: 3000
}).every(1, 'minute'));

on('plainMessageSentAndResponseReceived', run('writeRecords'));

Note: To use the writeRecords task, the persistence package must be installed and configured. See persistence package docs.

Send a message from an event's data

Note: to be able to use this feature, the messaging feature must be enabled.

You can also invoke these tasks by injecting the message in the event that triggers their execution. This allows to send messages in a more flexible way (i.e., no need to specify the message in the task graph).

Example usage:

import { awarns } from '@awarns/core';
import { PlainMessage } from '@awarns/wear-os';

export function sendMessage(message: PlainMessage) {
 awarns.emitEvent('sendMessage', {
   data: message
 });
}

Then, in the task graph:

on('sendMessage', run('sendPlainMessageToWatch'));

Receive watch-triggered message

Note: to be able to use this feature, the messaging feature must be enabled.

The watch can also send message to the smartphone by its own (i.e., no need to receive a message from the smartphone first to then reply). When those messages are received by the smartphone, the plainMessageReceivedEvent is emitted.

Example usage in the application task graph:

on('plainMessageReceived', run('writeRecords'));

Note: To use the writeRecords task, the persistence package must be installed and configured. See persistence package docs.

Events

NamePayloadDescription
watchAccelerometerSamplesAcquiredTriAxialContains a list of samples with the x, y, and z values of an accelerometer sensor.
watchGyroscopeSamplesAcquiredTriAxialContains a list of samples with the x, y, and z values of a gyroscope sensor.
watchMagnetometerSamplesAcquiredTriAxialContains a list of samples with the x, y, and z values of a magnetometer sensor.
watchHeartRateSamplesAcquiredHeartRateContains a list with the values of a heart rate sensor.
watchGeolocationAcquiredGeolocation | Array\Contains one or more GNSS locations. If the batchSize is set to 1, the payload will be a Geolocation record. Otherwise, the payload will be a Geolocation array.
plainMessageSentMessageSentContains the content of the message sent to the watch.
plainMessageSentAndResponseReceivedMessageReceivedContains the content of the message sent to the watch and the response from it.
plainMessageReceivedMessageReceivedContains the content of a message received from the watch.

Records

TriAxial

PropertyTypeDescription
idstringRecord's unique id.
typestringOne of the following values: watch-accelerometer, watch-gyroscope, or watch-magnetometer.
changeChangeAlways NONE.
timestampDateThe local time when the data was collected. It is equal to the time of the first sample in the record.
samplesTriAxialSample[]List with the collected samples.
TriAxialSample
PropertyTypeDescription
xnumberValue x of the sensor.
ynumberValue y of the sensor.
znumberValue z of the sensor.
timestampnumberThe local time (UNIX timestamp) when the sample was collected.

HeartRate

PropertyTypeDescription
idstringRecord's unique id.
typestringAlways watch-heart-rate.
changeChangeAlways NONE.
timestampDateThe local time when the data was collected. It is equal to the time of the first sample in the record.
samplesHeartRateSample[]List with the collected samples.
HeartRateSample
PropertyTypeDescription
valuenumberHeart rate value reported by the sensor.
timestampnumberThe local time (UNIX timestamp) when the sample was collected.

Geolocation

PropertyTypeDescription
idstringRecord's unique id.
typestringAlways watch-geolocation.
changeChangeAlways NONE.
timestampDateThe local time when the data was collected. It is equal to the time of the first sample in the record.
latitudenumberLatitude reported by the GPS.
longitudenumberLongitude reported by the GPS.
altitudenumberAltitude reported by the GPS.
verticalAccuracynumberThe estimated error in the latitude.
horizontalAccuracynumberThe estimated error in the longitude.
speednumberThe estimated speed of the device when the location was acquired.
directionnumberThe estimated direction of the device when the location was acquired.

MessageSent

PropertyTypeDescription
idstringRecord's unique id.
typestringAlways plain-message-sent.
changeChangeAlways NONE.
timestampDateThe local time when the message was sent.
contentPlaiMessageContent of the message sent.
PlainMessage
PropertyTypeDescription
messagestringThe content of the message.
inResponseTo?PlainMessageCan contain a PlainMessage to indicate that the current message is a response to the inResponseTo message.

MessageReceived

PropertyTypeDescription
idstringRecord's unique id.
typestringAlways plain-message-received.
changeChangeAlways NONE.
timestampDateThe local time when the message was sent.
contentReceivedMessageContent of the received message.
ReceivedMessage
PropertyTypeDescription
senderNodeIdstringId of the watch that sent the message.
plainMessagePlainMessageMessage received.

License

Apache License Version 2.0