@neurosity/sdk
Version:
Neurosity SDK
1,294 lines (1,291 loc) • 46.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.__firebase = exports.Notion = exports.Neurosity = void 0;
const rxjs_1 = require("rxjs");
const rxjs_2 = require("rxjs");
const operators_1 = require("rxjs/operators");
const operators_2 = require("rxjs/operators");
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
const index_1 = require("./api/index");
const index_2 = require("./api/index");
const streaming_1 = require("./types/streaming");
const subscription_1 = require("./utils/subscription");
const status_1 = require("./types/status");
const errors = __importStar(require("./utils/errors"));
const platform = __importStar(require("./utils/platform"));
const hapticEffects = __importStar(require("./utils/hapticEffects"));
const oauth_1 = require("./utils/oauth");
const oauth_2 = require("./utils/oauth");
const createOAuthURL_1 = require("./api/https/createOAuthURL");
const getOAuthToken_1 = require("./api/https/getOAuthToken");
const is_node_1 = require("./utils/is-node");
const metrics_1 = require("./utils/metrics");
const bluetooth_1 = require("./api/bluetooth");
const types_1 = require("./api/bluetooth/types");
const defaultOptions = {
timesync: false,
autoSelectDevice: true,
streamingMode: streaming_1.STREAMING_MODE.WIFI_ONLY,
emulator: false,
emulatorHost: "localhost",
emulatorAuthPort: 9099,
emulatorDatabasePort: 9000,
emulatorFunctionsPort: 5001,
emulatorFirestorePort: 8080,
emulatorOptions: {}
};
/**
* import StreamingModes from "@site/src/components/StreamingModes";
*
* Example
* ```typescript
* import { Neurosity } from "@neurosity/sdk";
*
* const neurosity = new Neurosity();
* ```
*/
class Neurosity {
/**
* Creates new instance of the Neurosity SDK
*
* ```typescript
* const neurosity = new Neurosity();
* ```
* @param options
*/
constructor(options = {}) {
/**
* @hidden
*/
this.streamingMode$ = new rxjs_2.ReplaySubject(1);
const { streamingMode, bluetoothTransport } = options;
this.options = Object.freeze(Object.assign(Object.assign({}, defaultOptions), options));
this.cloudClient = new index_1.CloudClient(this.options);
if (!!bluetoothTransport) {
this.bluetoothClient = new bluetooth_1.BluetoothClient({
selectedDevice$: this.onDeviceChange(),
osHasBluetoothSupport$: this._osHasBluetoothSupport(),
createBluetoothToken: this.createBluetoothToken.bind(this),
transport: bluetoothTransport
});
}
this._initStreamingMode(streamingMode, !!bluetoothTransport);
}
/**
*
* @hidden
*/
_initStreamingMode(streamingMode, hasBluetoothTransport) {
const streamingModeFeaturesBluetooth = [
streaming_1.STREAMING_MODE.BLUETOOTH_WITH_WIFI_FALLBACK,
streaming_1.STREAMING_MODE.WIFI_WITH_BLUETOOTH_FALLBACK
].includes(streamingMode);
const isInvalidStreamingMode = !Object.values(streaming_1.STREAMING_MODE).includes(streamingMode);
const isMissingBluetoothTransport = streamingModeFeaturesBluetooth && !hasBluetoothTransport;
this.isMissingBluetoothTransport = isMissingBluetoothTransport;
const shouldDefaultToCloud = !streamingMode || isInvalidStreamingMode || isMissingBluetoothTransport;
// Default to backwards compatible cloud streaming mode if:
// 1. No streaming mode is provided
// 2. An invalid streaming mode is provided
// 3. A streaming mode containing bluetooth is provided, but without a bluetooth transport
if (shouldDefaultToCloud) {
this.streamingMode$.next(streaming_1.STREAMING_MODE.WIFI_ONLY);
}
else {
this.streamingMode$.next(streamingMode);
}
}
/**
*
* @hidden
*/
_osHasBluetoothSupport() {
return (0, rxjs_1.combineLatest)({
selectedDevice: this.onDeviceChange(),
osVersion: this.osVersion().pipe((0, operators_1.startWith)(null))
}).pipe((0, operators_1.map)(({ selectedDevice, osVersion }) => (0, bluetooth_1.osHasBluetoothSupport)(selectedDevice, osVersion)));
}
/**
* Subscribe to the device's streaming state changes and the current strategy
*
* Streams the current mode of streaming (wifi or bluetooth).
*
* ```typescript
* neurosity.streamingState().subscribe((streamingState) => {
* console.log(streamingState);
* // { streamingMode: "wifi-only", activeMode: "wifi", connected: true }
* });
* ```
*/
streamingState() {
const isWifiOnline = (state) => [status_1.STATUS.ONLINE, status_1.STATUS.UPDATING].includes(state);
return this.streamingMode$.pipe((0, operators_1.switchMap)((streamingMode) => {
return (0, rxjs_1.combineLatest)({
selectedDevice: this.onDeviceChange(),
osHasBluetoothSupport: this._osHasBluetoothSupport()
}).pipe((0, operators_1.switchMap)(({ selectedDevice, osHasBluetoothSupport }) => {
if (!selectedDevice) {
return (0, rxjs_1.of)({
connected: false,
streamingMode,
activeMode: streaming_1.STREAMING_TYPE.WIFI
});
}
const isUnableToUseBluetooth = this.isMissingBluetoothTransport || !osHasBluetoothSupport;
if (isUnableToUseBluetooth) {
return this.cloudClient.status().pipe((0, operators_1.map)(({ state }) => ({
connected: isWifiOnline(state),
streamingMode,
activeMode: streaming_1.STREAMING_TYPE.WIFI
})));
}
return (0, rxjs_1.combineLatest)({
wifiStatus: this.cloudClient.status(),
bluetoothConnection: !!(this === null || this === void 0 ? void 0 : this.bluetoothClient)
? this.bluetoothClient.connection()
: (0, rxjs_1.of)(types_1.BLUETOOTH_CONNECTION.DISCONNECTED)
}).pipe((0, operators_1.map)(({ wifiStatus, bluetoothConnection }) => {
const isBluetoothConnected = bluetoothConnection === types_1.BLUETOOTH_CONNECTION.CONNECTED;
switch (streamingMode) {
default:
case streaming_1.STREAMING_MODE.WIFI_ONLY:
return {
connected: isWifiOnline(wifiStatus.state),
streamingMode,
activeMode: streaming_1.STREAMING_TYPE.WIFI
};
case streaming_1.STREAMING_MODE.WIFI_WITH_BLUETOOTH_FALLBACK:
return {
connected: isWifiOnline(wifiStatus.state) || !isBluetoothConnected
? isWifiOnline(wifiStatus.state)
: isBluetoothConnected,
streamingMode,
activeMode: isWifiOnline(wifiStatus.state) || !isBluetoothConnected
? streaming_1.STREAMING_TYPE.WIFI
: streaming_1.STREAMING_TYPE.BLUETOOTH
};
case streaming_1.STREAMING_MODE.BLUETOOTH_WITH_WIFI_FALLBACK:
return {
connected: isBluetoothConnected
? true
: isWifiOnline(wifiStatus.state),
streamingMode,
activeMode: isBluetoothConnected
? streaming_1.STREAMING_TYPE.BLUETOOTH
: streaming_1.STREAMING_TYPE.WIFI
};
}
}), (0, operators_2.distinctUntilChanged)((a, b) => (0, fast_deep_equal_1.default)(a, b)));
}));
}));
}
/**
*
* @hidden
*/
_withStreamingModeObservable(streams) {
const { wifi, bluetooth } = streams;
return this.streamingState().pipe((0, operators_1.switchMap)(({ activeMode }) => {
switch (activeMode) {
case streaming_1.STREAMING_TYPE.WIFI:
return wifi();
case streaming_1.STREAMING_TYPE.BLUETOOTH:
return bluetooth();
default:
return wifi();
}
}));
}
/**
*
* @hidden
*/
_withStreamingModePromise(promises) {
return __awaiter(this, void 0, void 0, function* () {
const { wifi, bluetooth } = promises;
const { activeMode } = yield (0, rxjs_2.firstValueFrom)(this.streamingState());
switch (activeMode) {
case streaming_1.STREAMING_TYPE.WIFI:
return yield wifi();
case streaming_1.STREAMING_TYPE.BLUETOOTH:
return yield bluetooth();
default:
return yield wifi();
}
});
}
/**
*
* @hidden
*/
get bluetooth() {
return this === null || this === void 0 ? void 0 : this.bluetoothClient;
}
/**
*
* @hidden
*/
_getCloudMetricDependencies() {
return {
options: this.options,
cloudClient: this.cloudClient,
onDeviceChange: this.onDeviceChange.bind(this),
status: this.status.bind(this)
};
}
/**
* Starts user session
*
* ```typescript
* await neurosity.login({
* email: "...",
* password: "..."
* });
* ```
*
* @param credentials
*/
login(credentials) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.cloudClient.login(credentials);
});
}
/**
* Ends user session
*
* ```typescript
* await neurosity.logout();
* // session has ended
* ```
*
*/
logout() {
return __awaiter(this, void 0, void 0, function* () {
return yield this.cloudClient.logout();
});
}
/**
* @internal
* Not user facing.
*/
__getApp() {
return this.cloudClient.__getApp();
}
/**
* Subscribe to auth state changes
*
* Streams the state of the auth session. If user has logged in, the user object will be set. When logged out, the user object will be null.
*
* ```typescript
* neurosity.onAuthStateChanged().subscribe((user) => {
* console.log(user);
* });
* ```
*/
onAuthStateChanged() {
return this.cloudClient.onAuthStateChanged();
}
/**
* @internal
* Not user facing yet
*/
addDevice(deviceId) {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "addDevice");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return this.cloudClient.addDevice(deviceId);
}
/**
* @internal
* Not user facing yet
*/
removeDevice(deviceId) {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "removeDevice");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return this.cloudClient.removeDevice(deviceId);
}
/**
* @internal
* Not user facing yet
*/
transferDevice(options) {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "transferDevice");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return this.cloudClient.transferDevice(options);
}
/**
* @internal
* Not user facing yet
*/
onUserDevicesChange() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "onUserDevicesChange");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this.cloudClient.onUserDevicesChange();
}
/**
* @internal
* Not user facing yet
*/
onUserClaimsChange() {
return this.cloudClient.onUserClaimsChange();
}
/**
* Get user devices
*
* Returns a list of devices claimed by the user authenticated.
*
* ```typescript
* const devices = await neurosity.getDevices();
* console.log(devices);
* ```
*/
getDevices() {
return __awaiter(this, void 0, void 0, function* () {
return yield this.cloudClient.getDevices();
});
}
/**
* Select Device
*
* Rarely necessary, but useful when the user owns multiple devices.
*
* A common use case for manually selecting a device is when you wish to build a device dropdown a user can select from, instead of collecting the Device Id from the user ahead of time.
*
* The 3 steps to manually selecting a device are:
*
* - Set `autoSelectDevice` to false when instantiating the `Neurosity` class.
* - Authenticate with your Neurosity account to access your devices by calling the `neurosity.login(...)` function.
* - Call the `neurosity.selectDevice(...)` function with a device selector function.
*
* ```typescript
* const devices = await neurosity.selectDevice((devices) =>
* devices.find((device) => device.deviceNickname === "Crown-A1B")
* );
*
* console.log(devices);
* ```
*
* > If you own multiple devices, and don't pass `autoSelectDevice`, then the first device on the list will be automatically selected.
*
* For more info, check out the "Device Selection" guide.
*/
selectDevice(deviceSelector) {
return __awaiter(this, void 0, void 0, function* () {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "selectDevice");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return yield this.cloudClient.selectDevice(deviceSelector);
});
}
/**
* Get selected device
*
* ```typescript
* const selectedDevice = await neurosity.getSelectedDevice();
* console.log(selectedDevice);
* ```
*/
getSelectedDevice() {
return __awaiter(this, void 0, void 0, function* () {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "getSelectedDevice");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return yield this.cloudClient.getSelectedDevice();
});
}
/**
* ```typescript
* const info = await neurosity.getInfo();
* ```
*/
getInfo() {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
return Promise.reject(errors.mustSelectDevice);
}
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "getInfo");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return yield this._withStreamingModePromise({
wifi: () => this.cloudClient.getInfo(),
bluetooth: () => this.bluetoothClient.getInfo()
});
});
}
/**
* Observes selected device
*
* ```typescript
* neurosity.onDeviceChange().subscribe(device => {
* console.log(device);
* });
* ```
*/
onDeviceChange() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "onDeviceChange");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this.cloudClient.onDeviceChange();
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Ends database connection
*
* ```typescript
* await neurosity.disconnect();
* ```
*/
disconnect() {
return __awaiter(this, void 0, void 0, function* () {
return yield this._withStreamingModePromise({
wifi: () => this.cloudClient.disconnect(),
bluetooth: () => this.bluetoothClient.disconnect()
});
});
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* @internal
* Not user facing
*/
dispatchAction(action) {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
return Promise.reject(errors.mustSelectDevice);
}
const [hasOAuthError, OAuthError] = (0, oauth_2.validateOAuthScopeForAction)(this.cloudClient.userClaims, action);
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return yield this._withStreamingModePromise({
wifi: () => this.cloudClient.dispatchAction(action),
bluetooth: () => this.bluetoothClient.dispatchAction(action)
});
});
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Injects an EEG marker to data stream
*
* ```typescript
* neurosity.addMarker("eyes-closed");
*
* // later...
*
* neurosity.addMarker("eyes-open");
* ```
*
* @param label Name the label to inject
*/
addMarker(label) {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
throw errors.mustSelectDevice;
}
if (!label) {
throw new Error(`${errors.prefix}A label is required for addMarker`);
}
return yield this._withStreamingModePromise({
wifi: () => this.cloudClient.dispatchAction({
command: "marker",
action: "add",
message: {
label,
timestamp: this.cloudClient.timestamp
}
}),
bluetooth: () => this.bluetoothClient.addMarker(label)
});
});
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Queue haptic motor commands
*
* To queue haptic P7 only,
* ```typescript
* await neurosity.haptics({
* P7: ["tripleClick100"]
* });
* ```
*
* To queue both motors at the same time
* ```typescript
* await neurosity.haptics({
* P7: [neurosity.getHapticEffects().strongClick100],
* P8: [neurosity.getHapticEffects().strongClick100]
* });
* ```
*
* You can queue different commands to the motors too
* ```typescript
* const effects = neurosity.getHapticEffects();
* await neurosity.haptics({
* P7: [effects.transitionRampUpLongSmooth1_0_to_100,
* effects.transitionRampDownLongSmooth1_100_to_0],
* P8: [effects.strongClick100]
* });
* ```
*
* @param effects Effects to queue. The key of the object passed should be the location of the motor
* to queue. Each key can be an array of up to 7 commands. There is no haptic support for model
* version 1, Notion DK1. The Haptic motor's location is positioned in reference to the 10-10 EEG
* system used to label the channels of the Crown's EEG sensors. Notion 2 and Crown have haptics
* at P7 and P8. A list of haptic commands can be found on ./utils/hapticCodes.ts - there
* are about 127 of them!
*/
haptics(effects) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const metric = "haptics";
if (!(yield this.cloudClient.didSelectDevice())) {
return Promise.reject(errors.mustSelectDevice);
}
const modelVersion = (_a = (yield this.getSelectedDevice())) === null || _a === void 0 ? void 0 : _a.modelVersion;
const supportsHaptics = platform.supportsHaptics(modelVersion);
if (!supportsHaptics) {
return Promise.reject(errors.metricNotSupportedByModel(metric, modelVersion));
}
const newPlatformHapticRequest = platform.getPlatformHapticMotors(modelVersion);
for (const key in effects) {
if (!Object.keys(newPlatformHapticRequest).includes(key)) {
return Promise.reject(errors.locationNotFound(key, modelVersion));
}
const singleMotorEffects = effects[key];
const maxItems = 7;
if (singleMotorEffects.length > maxItems) {
return Promise.reject(errors.exceededMaxItems(maxItems));
}
newPlatformHapticRequest[key] = singleMotorEffects;
}
const payload = {
command: metric,
action: "queue",
responseRequired: true,
responseTimeout: 1000,
message: { effects: newPlatformHapticRequest }
};
return yield this._withStreamingModePromise({
wifi: () => this.cloudClient.dispatchAction(payload),
bluetooth: () => this.bluetoothClient.dispatchAction(payload)
});
});
}
/**
* ```typescript
* const effects = neurosity.getHapticEffects();
* ```
*/
getHapticEffects() {
return hapticEffects;
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Observes accelerometer data
* Supported by the Crown and Notion 2 devices.
*
* ```typescript
* neurosity.accelerometer().subscribe(accelerometer => {
* console.log(accelerometer);
* });
*
* // { acceleration: ..., inclination: ..., orientation: ..., pitch: ..., roll: ..., x: ..., y: ..., z: ... }
* ```
*
* @returns Observable of accelerometer metric events
*/
accelerometer() {
const metric = "accelerometer";
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, metric);
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this.onDeviceChange().pipe((0, operators_1.switchMap)((selectedDevice) => {
const modelVersion = (selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.modelVersion) || platform.MODEL_VERSION_1;
const supportsAccel = platform.supportsAccel(modelVersion);
if (!supportsAccel) {
return (0, rxjs_1.throwError)(() => errors.metricNotSupportedByModel(metric, modelVersion));
}
return this._withStreamingModeObservable({
wifi: () => (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric,
labels: (0, subscription_1.getLabels)(metric),
atomic: true
}),
bluetooth: () => this.bluetoothClient.accelerometer()
});
}));
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* The `raw` brainwaves parameter emits epochs of 16 samples for Crown and 25 for Notion 1 and 2.
*
* Example
* ```typescript
* neurosity.brainwaves("raw").subscribe(brainwaves => {
* console.log(brainwaves);
* });
* ```
*
* Raw Unfiltered - The `rawUnfiltered` brainwaves parameter emits epochs of 16 samples for Crown and 25 for Notion 1 and 2.
* Example
* ```typescript
* neurosity.brainwaves("rawUnfiltered").subscribe(brainwaves => {
* console.log(brainwaves);
* });
* ```
*
* Power By Band - The `powerByBand` brainwaves parameter emits epochs 4 times a second. Every frequency label (e.g. beta) contains an average power value per channel.
*
* Example
* ```typescript
* neurosity.brainwaves("powerByBand").subscribe(brainwaves => {
* console.log(brainwaves);
* });
* ```
*
* Power Spectral Density (PSD) - The `psd` brainwaves parameter emits epochs 4 times a second. Every frequency label (e.g. alpha) contains the computed FFT (Fast Fourier transform) value per channel (see the `psd` property), as well as the frequency ranges (see the `freqs` property).
*
* Example
* ```typescript
* neurosity.brainwaves("psd").subscribe(brainwaves => {
* console.log(brainwaves);
* });
* ```
*
* @param label Name of metric properties to filter by
* @returns Observable of brainwaves metric events
*/
brainwaves(label) {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "brainwaves");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this._withStreamingModeObservable({
wifi: () => (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric: "brainwaves",
labels: label ? [label] : [],
atomic: false
}),
// @TODO: doesn't support multiple labels, we should make the higher
// order function only support one label
bluetooth: () => this.bluetoothClient.brainwaves(label)
});
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Example
* ```typescript
* neurosity.calm().subscribe(calm => {
* console.log(calm.probability);
* });
*
* // 0.45
* // 0.47
* // 0.53
* // 0.51
* // ...
* ```
*
* @returns Observable of calm events - awareness/calm alias
*/
calm() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "calm");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this._withStreamingModeObservable({
wifi: () => (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric: "awareness",
labels: ["calm"],
atomic: false
}),
bluetooth: () => this.bluetoothClient.calm()
});
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Observes signal quality data where each property is the name
* of the channel and the value includes the standard deviation and
* a status set by the device
*
* ```typescript
* neurosity.signalQuality().subscribe(signalQuality => {
* console.log(signalQuality);
* });
*
* // { FC6: { standardDeviation: 3.5, status: "good" }, C3: {...}, ... }
* ```
*
* @returns Observable of signalQuality metric events
*/
signalQuality() {
const metric = "signalQuality";
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, metric);
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this._withStreamingModeObservable({
wifi: () => (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric,
labels: (0, subscription_1.getLabels)(metric),
atomic: true
}),
bluetooth: () => this.bluetoothClient.signalQuality()
});
}
/**
* <StreamingModes wifi={true} />
*
* Observes last state of `settings` and all subsequent `settings` changes
*
* ```typescript
* neurosity.settings().subscribe(settings => {
* console.log(settings.lsl);
* });
*
* // true
* // ...
* ```
*
* @returns Observable of `settings` metric events
*/
settings() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "settings");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this.cloudClient.observeNamespace("settings");
}
/**
* <StreamingModes wifi={true} />
*
* Observes the current OS version and all subsequent version changes in real-time.
*
* ```typescript
* neurosity.osVersion().subscribe((osVersion) => {
* console.log(osVersion);
* });
*
* // "16.0.0"
* ```
*
* @returns Observable of `osVersion` events. e.g 16.0.0
*/
osVersion() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "osVersion");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this.cloudClient.osVersion();
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Example
* ```typescript
* neurosity.focus().subscribe(focus => {
* console.log(focus.probability);
* });
*
* // 0.56
* // 0.46
* // 0.31
* // 0.39
* // ...
* ```
*
* @returns Observable of focus events - awareness/focus alias
*/
focus() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "focus");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this._withStreamingModeObservable({
wifi: () => (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric: "awareness",
labels: ["focus"],
atomic: false
}),
bluetooth: () => this.bluetoothClient.focus()
});
}
/**
* <StreamingModes wifi={true} />
*
* @param label Name of metric properties to filter by
* @returns Observable of kinesis metric events
*/
kinesis(label) {
const metric = "kinesis";
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, metric);
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric,
labels: label ? [label] : [],
atomic: false
});
}
/**
* <StreamingModes wifi={true} />
*
* @param label Name of metric properties to filter by
* @returns Observable of predictions metric events
*/
predictions(label) {
const metric = "predictions";
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, metric);
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return (0, metrics_1.getCloudMetric)(this._getCloudMetricDependencies(), {
metric,
labels: label ? [label] : [],
atomic: false
});
}
/**
* <StreamingModes wifi={true} bluetooth={true} />
*
* Observes last state of `status` and all subsequent `status` changes
*
* ```typescript
* neurosity.status().subscribe(status => {
* console.log(status.state);
* });
*
* // "online"
* // ...
* ```
*
* @returns Observable of `status` metric events
*/
status() {
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "status");
if (hasOAuthError) {
return (0, rxjs_1.throwError)(() => OAuthError);
}
return this._withStreamingModeObservable({
wifi: () => this.cloudClient.status(),
bluetooth: () => this.bluetoothClient.status()
});
}
/**
* @internal
* Not user facing yet
*
* <StreamingModes wifi={true} />
*
* Changes device settings programmatically. These settings can be
* also changed from the developer console under device settings.
*
* Available settings [[ChangeSettings]]
*
* Example
* ```typescript
* neurosity.changeSettings({
* lsl: true
* });
* ```
*/
changeSettings(settings) {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
return Promise.reject(errors.mustSelectDevice);
}
const [hasOAuthError, OAuthError] = (0, oauth_1.validateOAuthScopeForFunctionName)(this.cloudClient.userClaims, "changeSettings");
if (hasOAuthError) {
return Promise.reject(OAuthError);
}
return yield this.cloudClient.changeSettings(settings);
});
}
/**
* <StreamingModes wifi={true} />
*
* ```typescript
* neurosity.training.record({
* metric: "kinesis",
* label: "push"
* });
*
* neurosity.training.stop({
* metric: "kinesis",
* label: "push"
* });
* ```
*
* @returns Training methods
*/
get training() {
return {
/**
* <StreamingModes wifi={true} />
*
* Records a training for a metric/label pair
* @category Training
*/
record: (training) => __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
throw errors.mustSelectDevice;
}
const userId = this.cloudClient.user && "uid" in this.cloudClient.user
? this.cloudClient.user.uid
: null;
const message = Object.assign(Object.assign({ fit: false, baseline: false, timestamp: this.cloudClient.timestamp }, training), { userId });
yield this.cloudClient.actions.dispatch({
command: "training",
action: "record",
message
});
}),
/**
* <StreamingModes wifi={true} />
*
* Stops the training for a metric/label pair
* @category Training
*/
stop: (training) => __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
throw errors.mustSelectDevice;
}
yield this.cloudClient.actions.dispatch({
command: "training",
action: "stop",
message: Object.assign({}, training)
});
}),
/**
* <StreamingModes wifi={true} />
*
* Stops all trainings
* @category Training
*/
stopAll: () => __awaiter(this, void 0, void 0, function* () {
if (!(yield this.cloudClient.didSelectDevice())) {
throw errors.mustSelectDevice;
}
yield this.cloudClient.actions.dispatch({
command: "training",
action: "stopAll",
message: {}
});
})
};
}
/**
* @internal
* Proof of Concept for disconnecting db
*/
goOffline() {
this.cloudClient.goOffline();
}
/**
* @internal
* Proof of Concept for resuming db connection
*/
goOnline() {
this.cloudClient.goOnline();
}
/**
* @internal
* Not user facing yet
*
* Creates user account and automatically signs in with same credentials
*
* @param emailAndPasswordObject
* @returns user credential
*/
createAccount(credentials) {
return this.cloudClient.createAccount(credentials);
}
/**
* @internal
* Not user facing yet
*
* Removes all devices from an account and then deletes the account
*/
deleteAccount() {
return this.cloudClient.deleteAccount();
}
/**
* @internal
* Not user facing
*
* Creates token (JWT) designed to authenticate and authorize Bluetooth clients/centrals.
*
* @returns token
*/
createBluetoothToken() {
return this.cloudClient.createBluetoothToken();
}
/**
* @internal
* Not user facing yet
*
* Creates custom token (JWT) to use to login with `{ customToken }`.
*
* @returns custom token
*/
createCustomToken() {
return this.cloudClient.createCustomToken();
}
/**
* @internal
* Not user facing yet
*
* Gets the offset between the device's clock and the client's clock
* Requires option.timesync to be true
*
* @returns timesyncOffset
*/
getTimesyncOffset() {
if (!this.options.timesync) {
console.warn(`getTimesyncOffset() requires options.timesync to be true.`);
}
return this.options.timesync ? this.cloudClient.getTimesyncOffset() : 0;
}
/**
* Create OAuth URL
* 💡 OAuth requires developers to register their apps with Neurosity
* [Read full OAuth guide](/docs/oauth)
*
* Creates client-specific OAuth URL. This is the first step of the OAuth workflow. Use this function to create a URL you can use to redirect users to the Neurosity sign-in page.
* 💡 This function is designed to only run on the server side for security reasons, as it requires your client secret.
*
* ```typescript
* const { Neurosity } = require("@neurosity/sdk");
*
* const neurosity = new Neurosity({
* autoSelectDevice: false
* });
*
* exports.handler = async function (event) {
* return neurosity
* .createOAuthURL({
* clientId: process.env.NEUROSITY_OAUTH_CLIENT_ID,
* clientSecret: process.env.NEUROSITY_OAUTH_CLIENT_SECRET,
* redirectUri: process.env.NEUROSITY_OAUTH_CLIENT_REDIRECT_URI,
* responseType: "token",
* state: Math.random().toString().split(".")[1],
* scope: [
* "read:devices-info",
* "read:devices-status",
* "read:signal-quality",
* "read:brainwaves"
* ]
* })
* .then((url) => ({
* statusCode: 200,
* body: JSON.stringify({ url })
* }))
* .catch((error) => ({
* statusCode: 400,
* body: JSON.stringify(error.response.data)
* }));
* };
* ```
* @returns custom token
*/
createOAuthURL(config) {
if (!is_node_1.isNode) {
return Promise.reject(new Error(`${errors.prefix}the createOAuthURL method must be used on the server side (node.js) for security reasons.`));
}
return (0, createOAuthURL_1.createOAuthURL)(config, this.options);
}
/**
* Get OAuth Token
* 💡 OAuth requires developers to register their apps with Neurosity
* [Read full OAuth guide](/docs/oauth)
*
* Gets client-specific OAuth token for a given userId.
*
* 💡 This function is designed to only run on the server side for security reasons, as it requires your client secret.
* Here's an example of a cloud function that receives a `userId` via query params and loads the client id and client secret securely via environment variables.
*
*
* ```typescript
* const { Neurosity } = require("@neurosity/sdk");
*
* const neurosity = new Neurosity({
* autoSelectDevice: false
* });
*
* exports.handler = async function (event) {
* const userId = event.queryStringParameters?.userId;
*
* return neurosity
* .getOAuthToken({
* clientId: process.env.NEUROSITY_OAUTH_CLIENT_ID,
* clientSecret: process.env.NEUROSITY_OAUTH_CLIENT_SECRET,
* userId
* })
* .then((token) => ({
* statusCode: 200,
* body: JSON.stringify(token)
* }))
* .catch((error) => ({
* statusCode: 200,
* body: JSON.stringify(error.response.data)
* }));
* };
* ```
* @returns custom token
*/
getOAuthToken(query) {
if (!is_node_1.isNode) {
return Promise.reject(new Error(`${errors.prefix}the getOAuthToken method must be used on the server side (node.js) for security reasons.`));
}
return (0, getOAuthToken_1.getOAuthToken)(query, this.options);
}
/**
* Remove OAuth Access
* 💡 OAuth requires developers to register their apps with Neurosity
* [Read full OAuth guide](/docs/oauth)
*
* Removes client-specific OAuth token for a given userId. Requires SDK to be signed in with OAuth custom token.
*
* ```typescript
* await neurosity.removeOAuthAccess().catch((error) => {
* // handle error here...
* });
* ```
* @returns custom token
*/
removeOAuthAccess() {
return this.cloudClient.removeOAuthAccess();
}
/**
* <StreamingModes wifi={true} />
*
* Observes and returns a list of all Kinesis `experiments` and all subsequent experiment changes.
* Here's an example of how to get a list of all Kinesis labels that have been trained:
*
* ```typescript
*
* const getUniqueLabels = (experiments) => {
* const labels = experiments.flatMap((experiment) => experiment.labels);
* // only return unique labels
* return [...new Set(labels)];
* }
*
* neurosity.onUserExperiments().subscribe((experiments) => {
* console.log(experiments);
* console.log("labels", getUniqueLabels(experiments));
* });
*
* // [{ id: '...', deviceId: '...', labels: [ 'drop' ], name: 'Lightgray cheetah', timestamp: 1577908381552, totalTrials: 16, userId: '...' }]
* // ["drop", "lift", "push"]
* ```
*
* @returns Observable of `experiments` events
*/
onUserExperiments() {
return this.cloudClient.onUserExperiments();
}
/**
* <StreamingModes wifi={true} />
*
* Deletes a specific experiment provided an experiment ID
*
* ```typescript
* await neurosity.deleteUserExperiment(experiment.id);
* ```
*
* @param experimentId The ID of the Experiment
* @returns void
*/
deleteUserExperiment(experimentId) {
return this.cloudClient.deleteUserExperiment(experimentId);
}
}
exports.Neurosity = Neurosity;
/**
*
* @hidden
*/
Neurosity.credentialWithLink = index_2.credentialWithLink;
/**
*
* @hidden
*/
Neurosity.createUser = index_1.createUser;
/**
*
* @hidden
*/
Neurosity.SERVER_TIMESTAMP = index_2.SERVER_TIMESTAMP;
/**
* @hidden
* Deprecated class kept for backwards compatibility purposes.
*/
class Notion extends Neurosity {
constructor(options = {}) {
super(options);
console.log(`The Notion class is deprecated and will be removed in the next version of the SDK. Please use the Neurosity class instead. e.g. new Notion() => new Neurosity()`);
}
}
exports.Notion = Notion;
/**
* @hidden
* Internal use only. Will be removed in next versions.
*/
var firebase_1 = require("./api/firebase");
Object.defineProperty(exports, "__firebase", { enumerable: true, get: function () { return firebase_1.__firebase; } });