@neurosity/sdk 6.3.0 → 6.4.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/browser/neurosity.iife.js +1550 -1536
  2. package/dist/browser/neurosity.js +15 -15
  3. package/dist/browser/neurosity.js.map +1 -1
  4. package/dist/cjs/Neurosity.d.ts +5 -0
  5. package/dist/cjs/Neurosity.js +20 -6
  6. package/dist/cjs/api/bluetooth/BluetoothClient.d.ts +2 -0
  7. package/dist/cjs/api/bluetooth/BluetoothClient.js +35 -30
  8. package/dist/cjs/api/bluetooth/BluetoothTransport.d.ts +2 -2
  9. package/dist/cjs/api/bluetooth/react-native/ReactNativeTransport.d.ts +2 -3
  10. package/dist/cjs/api/bluetooth/react-native/ReactNativeTransport.js +37 -45
  11. package/dist/cjs/api/bluetooth/utils/osHasBluetoothSupport.d.ts +2 -2
  12. package/dist/cjs/api/bluetooth/utils/osHasBluetoothSupport.js +4 -2
  13. package/dist/cjs/api/bluetooth/web/WebBluetoothTransport.d.ts +2 -3
  14. package/dist/cjs/api/bluetooth/web/WebBluetoothTransport.js +35 -41
  15. package/dist/cjs/api/index.d.ts +3 -1
  16. package/dist/cjs/api/index.js +4 -0
  17. package/dist/electron/index.js +10 -10
  18. package/dist/electron/index.js.map +1 -1
  19. package/dist/esm/Neurosity.d.ts +5 -0
  20. package/dist/esm/Neurosity.js +20 -6
  21. package/dist/esm/api/bluetooth/BluetoothClient.d.ts +2 -0
  22. package/dist/esm/api/bluetooth/BluetoothClient.js +36 -31
  23. package/dist/esm/api/bluetooth/BluetoothTransport.d.ts +2 -2
  24. package/dist/esm/api/bluetooth/react-native/ReactNativeTransport.d.ts +2 -3
  25. package/dist/esm/api/bluetooth/react-native/ReactNativeTransport.js +33 -41
  26. package/dist/esm/api/bluetooth/utils/osHasBluetoothSupport.d.ts +2 -2
  27. package/dist/esm/api/bluetooth/utils/osHasBluetoothSupport.js +4 -2
  28. package/dist/esm/api/bluetooth/web/WebBluetoothTransport.d.ts +2 -3
  29. package/dist/esm/api/bluetooth/web/WebBluetoothTransport.js +33 -39
  30. package/dist/esm/api/index.d.ts +3 -1
  31. package/dist/esm/api/index.js +4 -0
  32. package/dist/esm/neurosity.mjs +1550 -1536
  33. package/dist/examples/neurosity.iife.js +1550 -1536
  34. package/dist/examples/neurosity.js +15 -15
  35. package/dist/examples/neurosity.mjs +1550 -1536
  36. package/package.json +1 -1
@@ -80,6 +80,11 @@ export declare class Neurosity {
80
80
  * @hidden
81
81
  */
82
82
  _initStreamingMode(streamingMode: STREAMING_MODE, hasBluetoothTransport: boolean): void;
83
+ /**
84
+ *
85
+ * @hidden
86
+ */
87
+ _osHasBluetoothSupport(): Observable<any>;
83
88
  /**
84
89
  * Subscribe to the device's streaming state changes and the current strategy
85
90
  *
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { combineLatest, of, throwError } from "rxjs";
11
11
  import { ReplaySubject, firstValueFrom, EMPTY } from "rxjs";
12
- import { distinctUntilChanged, map, switchMap } from "rxjs/operators";
12
+ import { map, startWith, switchMap } from "rxjs/operators";
13
+ import { distinctUntilChanged } from "rxjs/operators";
13
14
  import isEqual from "fast-deep-equal";
14
15
  import { CloudClient, createUser } from "./api/index";
15
16
  import { credentialWithLink, SERVER_TIMESTAMP } from "./api/index";
@@ -70,6 +71,7 @@ export class Neurosity {
70
71
  if (!!bluetoothTransport) {
71
72
  this.bluetoothClient = new BluetoothClient({
72
73
  selectedDevice$: this.onDeviceChange(),
74
+ osHasBluetoothSupport$: this._osHasBluetoothSupport(),
73
75
  createBluetoothToken: this.createBluetoothToken.bind(this),
74
76
  transport: bluetoothTransport
75
77
  });
@@ -100,6 +102,16 @@ export class Neurosity {
100
102
  this.streamingMode$.next(streamingMode);
101
103
  }
102
104
  }
105
+ /**
106
+ *
107
+ * @hidden
108
+ */
109
+ _osHasBluetoothSupport() {
110
+ return combineLatest({
111
+ selectedDevice: this.onDeviceChange(),
112
+ osVersion: this.osVersion().pipe(startWith(null))
113
+ }).pipe(map(({ selectedDevice, osVersion }) => osHasBluetoothSupport(selectedDevice, osVersion)));
114
+ }
103
115
  /**
104
116
  * Subscribe to the device's streaming state changes and the current strategy
105
117
  *
@@ -115,12 +127,14 @@ export class Neurosity {
115
127
  streamingState() {
116
128
  const isWifiOnline = (state) => [STATUS.ONLINE, STATUS.UPDATING].includes(state);
117
129
  return this.streamingMode$.pipe(switchMap((streamingMode) => {
118
- return this.onDeviceChange().pipe(switchMap((selectDevice) => {
119
- if (!selectDevice) {
130
+ return combineLatest({
131
+ selectedDevice: this.onDeviceChange(),
132
+ osHasBluetoothSupport: this._osHasBluetoothSupport()
133
+ }).pipe(switchMap(({ selectedDevice, osHasBluetoothSupport }) => {
134
+ if (!selectedDevice) {
120
135
  return EMPTY;
121
136
  }
122
- const isUnableToUseBluetooth = this.isMissingBluetoothTransport ||
123
- !osHasBluetoothSupport(selectDevice);
137
+ const isUnableToUseBluetooth = this.isMissingBluetoothTransport || !osHasBluetoothSupport;
124
138
  if (isUnableToUseBluetooth) {
125
139
  return this.cloudClient.status().pipe(map(({ state }) => ({
126
140
  connected: isWifiOnline(state),
@@ -786,7 +800,7 @@ export class Neurosity {
786
800
  if (hasOAuthError) {
787
801
  return throwError(() => OAuthError);
788
802
  }
789
- return this.cloudClient.observeNamespace("info/osVersion");
803
+ return this.cloudClient.osVersion();
790
804
  }
791
805
  /**
792
806
  * <StreamingModes wifi={true} bluetooth={true} />
@@ -16,12 +16,14 @@ declare type CreateBluetoothToken = () => Promise<string>;
16
16
  declare type Options = {
17
17
  transport: BluetoothTransport;
18
18
  selectedDevice$: Observable<DeviceInfo>;
19
+ osHasBluetoothSupport$: Observable<boolean>;
19
20
  createBluetoothToken: CreateBluetoothToken;
20
21
  };
21
22
  export declare class BluetoothClient {
22
23
  transport: BluetoothTransport;
23
24
  deviceInfo: DeviceInfo;
24
25
  selectedDevice$: ReplaySubject<DeviceInfo>;
26
+ osHasBluetoothSupport$: ReplaySubject<boolean>;
25
27
  isAuthenticated$: ReplaySubject<boolean>;
26
28
  _focus$: Observable<any>;
27
29
  _calm$: Observable<any>;
@@ -7,20 +7,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { defer, firstValueFrom, timer } from "rxjs";
11
- import { ReplaySubject, EMPTY } from "rxjs";
12
- import { switchMap, share, tap } from "rxjs/operators";
13
- import { distinctUntilChanged } from "rxjs/operators";
10
+ import { defer, timer } from "rxjs";
11
+ import { ReplaySubject, firstValueFrom, EMPTY } from "rxjs";
12
+ import { switchMap, share, tap, distinctUntilChanged } from "rxjs/operators";
14
13
  import { WebBluetoothTransport } from "./web/WebBluetoothTransport";
15
14
  import { ReactNativeTransport } from "./react-native/ReactNativeTransport";
16
15
  import { csvBufferToEpoch } from "./utils/csvBufferToEpoch";
17
16
  import { BLUETOOTH_CONNECTION } from "./types";
18
- import { osHasBluetoothSupport } from "./utils/osHasBluetoothSupport";
19
17
  export class BluetoothClient {
20
18
  constructor(options) {
21
19
  this.selectedDevice$ = new ReplaySubject(1);
20
+ this.osHasBluetoothSupport$ = new ReplaySubject(1);
22
21
  this.isAuthenticated$ = new ReplaySubject(1);
23
- const { transport, selectedDevice$, createBluetoothToken } = options !== null && options !== void 0 ? options : {};
22
+ const { transport, selectedDevice$, osHasBluetoothSupport$, createBluetoothToken } = options !== null && options !== void 0 ? options : {};
24
23
  if (!transport) {
25
24
  throw new Error(`No bluetooth transport provided.`);
26
25
  }
@@ -29,8 +28,15 @@ export class BluetoothClient {
29
28
  if (selectedDevice$) {
30
29
  selectedDevice$.subscribe(this.selectedDevice$);
31
30
  }
32
- // Auto Connect
33
- this.transport._autoConnect(this.selectedDevice$).subscribe({
31
+ // Pass events to the internal osHasBluetoothSupport$ if osHasBluetoothSupport$ is passed via options
32
+ if (osHasBluetoothSupport$) {
33
+ osHasBluetoothSupport$.subscribe(this.osHasBluetoothSupport$);
34
+ }
35
+ this.osHasBluetoothSupport$
36
+ .pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport
37
+ ? this.transport._autoConnect(this.selectedDevice$)
38
+ : EMPTY))
39
+ .subscribe({
34
40
  error: (error) => {
35
41
  var _a;
36
42
  this.transport.addLog(`Auto connect: error -> ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
@@ -45,7 +51,11 @@ export class BluetoothClient {
45
51
  this.transport.addLog("Auto authentication not enabled");
46
52
  }
47
53
  // Auto manage action notifications
48
- this.transport._autoToggleActionNotifications(this.selectedDevice$);
54
+ this.osHasBluetoothSupport$
55
+ .pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport
56
+ ? this.transport._autoToggleActionNotifications()
57
+ : EMPTY))
58
+ .subscribe();
49
59
  // Multicast metrics (share)
50
60
  this._focus$ = this._subscribeWhileAuthenticated("focus");
51
61
  this._calm$ = this._subscribeWhileAuthenticated("calm");
@@ -69,27 +79,24 @@ export class BluetoothClient {
69
79
  const reauthenticateInterval$ = timer(0, REAUTHENTICATE_INTERVAL).pipe(tap(() => {
70
80
  this.transport.addLog(`Auto authentication in progress...`);
71
81
  }));
72
- return this.selectedDevice$.pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice)
73
- ? EMPTY
74
- : this.connection().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
75
- ? reauthenticateInterval$
76
- : EMPTY), switchMap(() => __awaiter(this, void 0, void 0, function* () { return yield this.isAuthenticated(); })), tap(([isAuthenticated]) => __awaiter(this, void 0, void 0, function* () {
77
- if (!isAuthenticated) {
78
- const token = yield createBluetoothToken();
79
- yield this.authenticate(token);
80
- }
81
- else {
82
- this.transport.addLog(`Already authenticated`);
83
- }
84
- })))));
82
+ return this.osHasBluetoothSupport$.pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport ? this.connection() : EMPTY), switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
83
+ ? reauthenticateInterval$
84
+ : EMPTY), switchMap(() => __awaiter(this, void 0, void 0, function* () { return yield this.isAuthenticated(); })), tap(([isAuthenticated]) => __awaiter(this, void 0, void 0, function* () {
85
+ if (!isAuthenticated) {
86
+ const token = yield createBluetoothToken();
87
+ yield this.authenticate(token);
88
+ }
89
+ else {
90
+ this.transport.addLog(`Already authenticated`);
91
+ }
92
+ })));
85
93
  }
86
94
  enableAutoConnect(autoConnect) {
87
95
  this.transport.enableAutoConnect(autoConnect);
88
96
  }
89
97
  _hasBluetoothSupport() {
90
98
  return __awaiter(this, void 0, void 0, function* () {
91
- const selectedDevice = yield firstValueFrom(this.selectedDevice$);
92
- return osHasBluetoothSupport(selectedDevice);
99
+ return yield firstValueFrom(this.osHasBluetoothSupport$);
93
100
  });
94
101
  }
95
102
  authenticate(token) {
@@ -170,13 +177,11 @@ export class BluetoothClient {
170
177
  });
171
178
  }
172
179
  _subscribeWhileAuthenticated(characteristicName) {
173
- return this.selectedDevice$.pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice)
174
- ? EMPTY
175
- : this.isAuthenticated$.pipe(distinctUntilChanged(), switchMap((isAuthenticated) => isAuthenticated
176
- ? this.transport.subscribeToCharacteristic({
177
- characteristicName
178
- })
179
- : EMPTY))), share());
180
+ return this.osHasBluetoothSupport$.pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport ? this.isAuthenticated$ : EMPTY), distinctUntilChanged(), switchMap((isAuthenticated) => isAuthenticated
181
+ ? this.transport.subscribeToCharacteristic({
182
+ characteristicName
183
+ })
184
+ : EMPTY), share());
180
185
  }
181
186
  focus() {
182
187
  return this._focus$;
@@ -1,7 +1,7 @@
1
1
  import { Observable, Subject } from "rxjs";
2
2
  import { BLUETOOTH_CONNECTION, TRANSPORT_TYPE } from "./types";
3
3
  import { Action } from "../../types/actions";
4
- import { DeviceInfo } from "../../types/deviceInfo";
4
+ import { DeviceInfo, OSVersion } from "../../types/deviceInfo";
5
5
  import { Peripheral } from "./react-native/types/BleManagerTypes";
6
6
  export declare type DeviceNicknameOrPeripheral = string | Peripheral;
7
7
  /**
@@ -10,7 +10,7 @@ export declare type DeviceNicknameOrPeripheral = string | Peripheral;
10
10
  export interface BluetoothTransport {
11
11
  type: TRANSPORT_TYPE;
12
12
  connect(deviceNicknameORPeripheral?: DeviceNicknameOrPeripheral): Promise<void>;
13
- _autoConnect(selectedDevice$: Observable<DeviceInfo>): Observable<void>;
13
+ _autoConnect(selectedDevice$: Observable<DeviceInfo>, osVersion: Observable<OSVersion>): Observable<void>;
14
14
  disconnect(): Promise<void>;
15
15
  connection(): Observable<BLUETOOTH_CONNECTION>;
16
16
  requestDevice?(): any;
@@ -1,5 +1,4 @@
1
- import { BehaviorSubject, ReplaySubject } from "rxjs";
2
- import { Observable } from "rxjs";
1
+ import { Observable, BehaviorSubject, ReplaySubject } from "rxjs";
3
2
  import { BluetoothTransport } from "../BluetoothTransport";
4
3
  import { ActionOptions, SubscribeOptions } from "../types";
5
4
  import { TRANSPORT_TYPE, BLUETOOTH_CONNECTION } from "../types";
@@ -65,7 +64,7 @@ export declare class ReactNativeTransport implements BluetoothTransport {
65
64
  writeCharacteristic(characteristicName: string, data: string): Promise<void>;
66
65
  _addPendingAction(actionId: number): void;
67
66
  _removePendingAction(actionId: number): void;
68
- _autoToggleActionNotifications(selectedDevice$: Observable<DeviceInfo>): Promise<void>;
67
+ _autoToggleActionNotifications(): Observable<any>;
69
68
  dispatchAction({ characteristicName, action }: ActionOptions): Promise<any>;
70
69
  }
71
70
  export {};
@@ -10,8 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { BLUETOOTH_PRIMARY_SERVICE_UUID_STRING } from "@neurosity/ipk";
11
11
  import { BLUETOOTH_CHUNK_DELIMITER } from "@neurosity/ipk";
12
12
  import { BLUETOOTH_DEVICE_NAME_PREFIXES } from "@neurosity/ipk";
13
- import { BehaviorSubject, defer, merge, of, ReplaySubject, timer } from "rxjs";
14
- import { fromEventPattern, Observable, NEVER, EMPTY } from "rxjs";
13
+ import { Observable, BehaviorSubject, ReplaySubject, NEVER } from "rxjs";
14
+ import { defer, merge, of, timer, fromEventPattern } from "rxjs";
15
15
  import { switchMap, map, filter, takeUntil, tap } from "rxjs/operators";
16
16
  import { shareReplay, distinctUntilChanged, finalize } from "rxjs/operators";
17
17
  import { take, share, scan, distinct } from "rxjs/operators";
@@ -23,7 +23,6 @@ import { DEFAULT_ACTION_RESPONSE_TIMEOUT } from "../constants";
23
23
  import { CHARACTERISTIC_UUIDS_TO_NAMES } from "../constants";
24
24
  import { ANDROID_MAX_MTU } from "../constants";
25
25
  import { REACT_NATIVE_MAX_BYTE_SIZE } from "../constants";
26
- import { osHasBluetoothSupport } from "../utils/osHasBluetoothSupport";
27
26
  const defaultOptions = {
28
27
  autoConnect: true
29
28
  };
@@ -105,12 +104,10 @@ export class ReactNativeTransport {
105
104
  const selectedDeviceAfterDisconnect$ = this.onDisconnected$.pipe(switchMap(() => selectedDevice$));
106
105
  return this._isAutoConnectEnabled$.pipe(switchMap((isAutoConnectEnabled) => isAutoConnectEnabled
107
106
  ? merge(selectedDevice$, selectedDeviceAfterDisconnect$)
108
- : NEVER), switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice)
109
- ? NEVER
110
- : this.scan().pipe(switchMap((peripherals) => {
111
- const peripheralMatch = peripherals.find((peripheral) => peripheral.name === (selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.deviceNickname));
112
- return peripheralMatch ? of(peripheralMatch) : NEVER;
113
- }), distinct((peripheral) => peripheral.id), take(1))), switchMap((peripheral) => __awaiter(this, void 0, void 0, function* () {
107
+ : NEVER), switchMap((selectedDevice) => this.scan().pipe(switchMap((peripherals) => {
108
+ const peripheralMatch = peripherals.find((peripheral) => peripheral.name === (selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.deviceNickname));
109
+ return peripheralMatch ? of(peripheralMatch) : NEVER;
110
+ }), distinct((peripheral) => peripheral.id), take(1))), switchMap((peripheral) => __awaiter(this, void 0, void 0, function* () {
114
111
  return yield this.connect(peripheral);
115
112
  })));
116
113
  }
@@ -333,40 +330,35 @@ export class ReactNativeTransport {
333
330
  const actions = this.pendingActions$.getValue();
334
331
  this.pendingActions$.next(actions.filter((id) => id !== actionId));
335
332
  }
336
- _autoToggleActionNotifications(selectedDevice$) {
337
- return __awaiter(this, void 0, void 0, function* () {
338
- let started = false;
339
- const sideEffects$ = this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
340
- ? this.pendingActions$
341
- : NEVER), tap((pendingActions) => __awaiter(this, void 0, void 0, function* () {
342
- var _a, _b;
343
- const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName("actions");
344
- const hasPendingActions = !!pendingActions.length;
345
- if (hasPendingActions && !started) {
346
- started = true;
347
- try {
348
- yield this.BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID);
349
- this.addLog(`Started notifications for [actions] characteristic`);
350
- }
351
- catch (error) {
352
- this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
353
- }
333
+ _autoToggleActionNotifications() {
334
+ let started = false;
335
+ return this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
336
+ ? this.pendingActions$
337
+ : NEVER), tap((pendingActions) => __awaiter(this, void 0, void 0, function* () {
338
+ var _a, _b;
339
+ const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName("actions");
340
+ const hasPendingActions = !!pendingActions.length;
341
+ if (hasPendingActions && !started) {
342
+ started = true;
343
+ try {
344
+ yield this.BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID);
345
+ this.addLog(`Started notifications for [actions] characteristic`);
354
346
  }
355
- if (!hasPendingActions && started) {
356
- started = false;
357
- try {
358
- yield this.BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID);
359
- this.addLog(`Stopped notifications for actions characteristic`);
360
- }
361
- catch (error) {
362
- this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
363
- }
347
+ catch (error) {
348
+ this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
364
349
  }
365
- })));
366
- selectedDevice$
367
- .pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice) ? EMPTY : sideEffects$))
368
- .subscribe();
369
- });
350
+ }
351
+ if (!hasPendingActions && started) {
352
+ started = false;
353
+ try {
354
+ yield this.BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID);
355
+ this.addLog(`Stopped notifications for actions characteristic`);
356
+ }
357
+ catch (error) {
358
+ this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
359
+ }
360
+ }
361
+ })));
370
362
  }
371
363
  dispatchAction({ characteristicName, action }) {
372
364
  return __awaiter(this, void 0, void 0, function* () {
@@ -1,2 +1,2 @@
1
- import { DeviceInfo } from "../../../types/deviceInfo";
2
- export declare function osHasBluetoothSupport(selectedDevice: DeviceInfo): any;
1
+ import { DeviceInfo, OSVersion } from "../../../types/deviceInfo";
2
+ export declare function osHasBluetoothSupport(selectedDevice: DeviceInfo, osVersion?: OSVersion): any;
@@ -1,5 +1,5 @@
1
1
  import semverGte from "semver/functions/gte";
2
- export function osHasBluetoothSupport(selectedDevice) {
2
+ export function osHasBluetoothSupport(selectedDevice, osVersion) {
3
3
  if (!selectedDevice) {
4
4
  return false;
5
5
  }
@@ -12,5 +12,7 @@ export function osHasBluetoothSupport(selectedDevice) {
12
12
  if (isEmulator) {
13
13
  return false;
14
14
  }
15
- return semverGte(selectedDevice.osVersion, "16.0.0");
15
+ // `osVersion` is updated in real time,
16
+ // unlike accessing via `selectedDevice.osVersion`
17
+ return semverGte(osVersion !== null && osVersion !== void 0 ? osVersion : selectedDevice.osVersion, "16.0.0");
16
18
  }
@@ -1,6 +1,5 @@
1
1
  /// <reference types="web-bluetooth" />
2
- import { BehaviorSubject, ReplaySubject } from "rxjs";
3
- import { Observable } from "rxjs";
2
+ import { Observable, BehaviorSubject, ReplaySubject } from "rxjs";
4
3
  import { BluetoothTransport } from "../BluetoothTransport";
5
4
  import { ActionOptions, SubscribeOptions } from "../types";
6
5
  import { TRANSPORT_TYPE, BLUETOOTH_CONNECTION } from "../types";
@@ -48,7 +47,7 @@ export declare class WebBluetoothTransport implements BluetoothTransport {
48
47
  writeCharacteristic(characteristicName: string, data: string): Promise<void>;
49
48
  _addPendingAction(actionId: number): void;
50
49
  _removePendingAction(actionId: number): void;
51
- _autoToggleActionNotifications(selectedDevice$: Observable<DeviceInfo>): Promise<void>;
50
+ _autoToggleActionNotifications(): Observable<any>;
52
51
  dispatchAction({ characteristicName, action }: ActionOptions): Promise<any>;
53
52
  }
54
53
  export {};
@@ -11,8 +11,8 @@ import { BLUETOOTH_PRIMARY_SERVICE_UUID_HEX } from "@neurosity/ipk";
11
11
  import { BLUETOOTH_CHUNK_DELIMITER } from "@neurosity/ipk";
12
12
  import { BLUETOOTH_DEVICE_NAME_PREFIXES } from "@neurosity/ipk";
13
13
  import { BLUETOOTH_COMPANY_IDENTIFIER_HEX } from "@neurosity/ipk";
14
- import { BehaviorSubject, defer, merge, of, ReplaySubject, timer } from "rxjs";
15
- import { fromEventPattern, Observable, EMPTY, NEVER } from "rxjs";
14
+ import { Observable, BehaviorSubject, ReplaySubject } from "rxjs";
15
+ import { defer, merge, timer, fromEventPattern, NEVER } from "rxjs";
16
16
  import { switchMap, map, filter, tap } from "rxjs/operators";
17
17
  import { shareReplay, distinctUntilChanged } from "rxjs/operators";
18
18
  import { take, share } from "rxjs/operators";
@@ -23,7 +23,6 @@ import { encode, decode } from "../utils/encoding";
23
23
  import { TRANSPORT_TYPE, BLUETOOTH_CONNECTION } from "../types";
24
24
  import { DEFAULT_ACTION_RESPONSE_TIMEOUT } from "../constants";
25
25
  import { CHARACTERISTIC_UUIDS_TO_NAMES } from "../constants";
26
- import { osHasBluetoothSupport } from "../utils/osHasBluetoothSupport";
27
26
  const defaultOptions = {
28
27
  autoConnect: true
29
28
  };
@@ -64,7 +63,7 @@ export class WebBluetoothTransport {
64
63
  _autoConnect(selectedDevice$) {
65
64
  return this._isAutoConnectEnabled$.pipe(switchMap((isAutoConnectEnabled) => isAutoConnectEnabled
66
65
  ? merge(selectedDevice$, this.onDisconnected$.pipe(switchMap(() => selectedDevice$)))
67
- : NEVER), switchMap((selectedDevice) => osHasBluetoothSupport(selectedDevice) ? of(selectedDevice) : EMPTY), switchMap((selectedDevice) => __awaiter(this, void 0, void 0, function* () {
66
+ : NEVER), switchMap((selectedDevice) => __awaiter(this, void 0, void 0, function* () {
68
67
  var _a;
69
68
  const { deviceNickname } = selectedDevice;
70
69
  if (this.isConnected()) {
@@ -304,43 +303,38 @@ export class WebBluetoothTransport {
304
303
  const actions = this.pendingActions$.getValue();
305
304
  this.pendingActions$.next(actions.filter((id) => id !== actionId));
306
305
  }
307
- _autoToggleActionNotifications(selectedDevice$) {
308
- return __awaiter(this, void 0, void 0, function* () {
309
- let actionsCharacteristic;
310
- let started = false;
311
- const sideEffects$ = this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
312
- ? defer(() => this.getCharacteristicByName("actions")).pipe(switchMap((characteristic) => {
313
- actionsCharacteristic = characteristic;
314
- return this.pendingActions$;
315
- }))
316
- : NEVER), tap((pendingActions) => __awaiter(this, void 0, void 0, function* () {
317
- var _a, _b;
318
- const hasPendingActions = !!pendingActions.length;
319
- if (hasPendingActions && !started) {
320
- started = true;
321
- try {
322
- yield actionsCharacteristic.startNotifications();
323
- this.addLog(`Started notifications for [actions] characteristic`);
324
- }
325
- catch (error) {
326
- this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
327
- }
306
+ _autoToggleActionNotifications() {
307
+ let actionsCharacteristic;
308
+ let started = false;
309
+ return this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
310
+ ? defer(() => this.getCharacteristicByName("actions")).pipe(switchMap((characteristic) => {
311
+ actionsCharacteristic = characteristic;
312
+ return this.pendingActions$;
313
+ }))
314
+ : NEVER), tap((pendingActions) => __awaiter(this, void 0, void 0, function* () {
315
+ var _a, _b;
316
+ const hasPendingActions = !!pendingActions.length;
317
+ if (hasPendingActions && !started) {
318
+ started = true;
319
+ try {
320
+ yield actionsCharacteristic.startNotifications();
321
+ this.addLog(`Started notifications for [actions] characteristic`);
328
322
  }
329
- if (!hasPendingActions && started) {
330
- started = false;
331
- try {
332
- yield actionsCharacteristic.stopNotifications();
333
- this.addLog(`Stopped notifications for actions characteristic`);
334
- }
335
- catch (error) {
336
- this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
337
- }
323
+ catch (error) {
324
+ this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
338
325
  }
339
- })));
340
- selectedDevice$
341
- .pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice) ? EMPTY : sideEffects$))
342
- .subscribe();
343
- });
326
+ }
327
+ if (!hasPendingActions && started) {
328
+ started = false;
329
+ try {
330
+ yield actionsCharacteristic.stopNotifications();
331
+ this.addLog(`Stopped notifications for actions characteristic`);
332
+ }
333
+ catch (error) {
334
+ this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
335
+ }
336
+ }
337
+ })));
344
338
  }
345
339
  dispatchAction({ characteristicName, action }) {
346
340
  return __awaiter(this, void 0, void 0, function* () {
@@ -11,7 +11,7 @@ import { Credentials, CustomToken } from "../types/credentials";
11
11
  import { EmailAndPassword } from "../types/credentials";
12
12
  import { ChangeSettings } from "../types/settings";
13
13
  import { DeviceStatus } from "../types/status";
14
- import { DeviceInfo, DeviceSelector } from "../types/deviceInfo";
14
+ import { DeviceInfo, DeviceSelector, OSVersion } from "../types/deviceInfo";
15
15
  import { UserClaims } from "../types/user";
16
16
  import { OAuthRemoveResponse } from "../types/oauth";
17
17
  import { Experiment } from "../types/experiment";
@@ -30,8 +30,10 @@ export declare class CloudClient implements Client {
30
30
  protected timesync: Timesync;
31
31
  protected subscriptionManager: SubscriptionManager;
32
32
  protected status$: Observable<DeviceStatus>;
33
+ protected osVersion$: Observable<OSVersion>;
33
34
  constructor(options: SDKOptions);
34
35
  onDeviceChange(): Observable<DeviceInfo>;
36
+ osVersion(): Observable<OSVersion>;
35
37
  private setAutoSelectedDevice;
36
38
  get actions(): Actions;
37
39
  dispatchAction(action: Action): Promise<any>;
@@ -31,6 +31,7 @@ export class CloudClient {
31
31
  this.firebaseUser = new FirebaseUser(this.firebaseApp);
32
32
  this._selectedDevice.next(undefined);
33
33
  this.status$ = heartbeatAwareStatus(this.observeNamespace("status").pipe(share())).pipe(filterInternalKeys(), shareReplay(1));
34
+ this.osVersion$ = this.observeNamespace("info/osVersion").pipe(shareReplay(1));
34
35
  this.firebaseUser.onAuthStateChanged().subscribe((user) => {
35
36
  this.user = user;
36
37
  });
@@ -64,6 +65,9 @@ export class CloudClient {
64
65
  .asObservable()
65
66
  .pipe(filter((value) => value !== undefined));
66
67
  }
68
+ osVersion() {
69
+ return this.osVersion$;
70
+ }
67
71
  // Automatically select device when user logs in
68
72
  setAutoSelectedDevice() {
69
73
  return __awaiter(this, void 0, void 0, function* () {