@mentra/bluetooth-sdk 0.1.3 → 0.1.5

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 (61) hide show
  1. package/README.md +63 -18
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +5 -5
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +38 -12
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +1 -1
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +88 -3
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +11 -5
  7. package/app.plugin.js +3 -1
  8. package/build/BluetoothSdk.types.d.ts +20 -7
  9. package/build/BluetoothSdk.types.d.ts.map +1 -1
  10. package/build/BluetoothSdk.types.js.map +1 -1
  11. package/build/_internal.d.ts +1 -1
  12. package/build/_internal.js +1 -1
  13. package/build/_internal.js.map +1 -1
  14. package/build/_private/BluetoothSdkModule.d.ts +5 -4
  15. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  16. package/build/_private/BluetoothSdkModule.js +1 -1
  17. package/build/_private/BluetoothSdkModule.js.map +1 -1
  18. package/build/index.d.ts +4 -3
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +81 -2
  21. package/build/index.js.map +1 -1
  22. package/build/react/index.d.ts +5 -0
  23. package/build/react/index.d.ts.map +1 -0
  24. package/build/react/index.js +4 -0
  25. package/build/react/index.js.map +1 -0
  26. package/build/react/useBluetoothEvent.d.ts +6 -0
  27. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  28. package/build/react/useBluetoothEvent.js +21 -0
  29. package/build/react/useBluetoothEvent.js.map +1 -0
  30. package/build/react/useBluetoothScan.d.ts +22 -0
  31. package/build/react/useBluetoothScan.d.ts.map +1 -0
  32. package/build/react/useBluetoothScan.js +135 -0
  33. package/build/react/useBluetoothScan.js.map +1 -0
  34. package/build/react/useBluetoothStatus.d.ts +16 -0
  35. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  36. package/build/react/useBluetoothStatus.js +137 -0
  37. package/build/react/useBluetoothStatus.js.map +1 -0
  38. package/build/react/useGlassesConnection.d.ts +29 -0
  39. package/build/react/useGlassesConnection.d.ts.map +1 -0
  40. package/build/react/useGlassesConnection.js +153 -0
  41. package/build/react/useGlassesConnection.js.map +1 -0
  42. package/build/react/useMentraBluetooth.d.ts +98 -0
  43. package/build/react/useMentraBluetooth.d.ts.map +1 -0
  44. package/build/react/useMentraBluetooth.js +156 -0
  45. package/build/react/useMentraBluetooth.js.map +1 -0
  46. package/ios/BluetoothSdkModule.swift +5 -5
  47. package/ios/Source/Bridge.swift +42 -8
  48. package/ios/Source/DeviceManager.swift +0 -1
  49. package/ios/Source/DeviceStore.swift +1 -1
  50. package/ios/Source/MentraBluetoothSDK.swift +117 -10
  51. package/package.json +7 -1
  52. package/src/BluetoothSdk.types.ts +20 -7
  53. package/src/_internal.ts +1 -1
  54. package/src/_private/BluetoothSdkModule.ts +7 -5
  55. package/src/index.ts +93 -4
  56. package/src/react/index.ts +24 -0
  57. package/src/react/useBluetoothEvent.ts +38 -0
  58. package/src/react/useBluetoothScan.ts +173 -0
  59. package/src/react/useBluetoothStatus.ts +180 -0
  60. package/src/react/useGlassesConnection.ts +209 -0
  61. package/src/react/useMentraBluetooth.ts +307 -0
@@ -0,0 +1,135 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import BluetoothSdk, { DeviceModels } from "../index";
3
+ function dedupeKey(device, dedupe) {
4
+ if (typeof dedupe === "function") {
5
+ return dedupe(device);
6
+ }
7
+ if (dedupe === "name") {
8
+ return `${device.model}:${device.name}`;
9
+ }
10
+ return device.id;
11
+ }
12
+ function dedupeDevices(devices, dedupe) {
13
+ const seen = new Set();
14
+ const nextDevices = [];
15
+ devices.forEach((device) => {
16
+ const key = dedupeKey(device, dedupe);
17
+ if (seen.has(key)) {
18
+ return;
19
+ }
20
+ seen.add(key);
21
+ nextDevices.push(device);
22
+ });
23
+ return nextDevices;
24
+ }
25
+ function hasDevice(devices, selectedDevice, dedupe) {
26
+ if (!selectedDevice) {
27
+ return true;
28
+ }
29
+ const selectedKey = dedupeKey(selectedDevice, dedupe);
30
+ return devices.some((device) => dedupeKey(device, dedupe) === selectedKey);
31
+ }
32
+ export function useBluetoothScan(options = {}) {
33
+ const [devices, setDevices] = useState([]);
34
+ const [error, setError] = useState(null);
35
+ const [model, setModelState] = useState(options.model ?? DeviceModels.MentraLive);
36
+ const [scanning, setScanning] = useState(false);
37
+ const [selectedDevice, selectDevice] = useState(null);
38
+ const activeScanRef = useRef(0);
39
+ const dedupeRef = useRef(options.dedupe ?? "id");
40
+ const onErrorRef = useRef(options.onError);
41
+ const scanningRef = useRef(false);
42
+ const timeoutMsRef = useRef(options.timeoutMs);
43
+ useEffect(() => {
44
+ dedupeRef.current = options.dedupe ?? "id";
45
+ onErrorRef.current = options.onError;
46
+ timeoutMsRef.current = options.timeoutMs;
47
+ }, [options.dedupe, options.onError, options.timeoutMs]);
48
+ useEffect(() => {
49
+ scanningRef.current = scanning;
50
+ }, [scanning]);
51
+ useEffect(() => {
52
+ if (!hasDevice(devices, selectedDevice, dedupeRef.current)) {
53
+ selectDevice(null);
54
+ }
55
+ }, [devices, selectedDevice]);
56
+ useEffect(() => {
57
+ return () => {
58
+ if (!scanningRef.current) {
59
+ return;
60
+ }
61
+ activeScanRef.current += 1;
62
+ void BluetoothSdk.stopScan().catch(() => undefined);
63
+ };
64
+ }, []);
65
+ function clearResults() {
66
+ setDevices([]);
67
+ selectDevice(null);
68
+ }
69
+ function setModel(nextModel) {
70
+ setModelState(nextModel);
71
+ clearResults();
72
+ }
73
+ async function startScan(nextModel) {
74
+ const scanId = activeScanRef.current + 1;
75
+ activeScanRef.current = scanId;
76
+ const scanModel = nextModel ?? model;
77
+ if (nextModel && nextModel !== model) {
78
+ setModelState(nextModel);
79
+ }
80
+ setError(null);
81
+ setScanning(true);
82
+ scanningRef.current = true;
83
+ setDevices([]);
84
+ selectDevice(null);
85
+ try {
86
+ const nextDevices = await BluetoothSdk.scan(scanModel, {
87
+ ...(timeoutMsRef.current == null ? {} : { timeoutMs: timeoutMsRef.current }),
88
+ onResults: (results) => {
89
+ if (activeScanRef.current !== scanId) {
90
+ return;
91
+ }
92
+ setDevices(dedupeDevices(results, dedupeRef.current));
93
+ },
94
+ });
95
+ const finalDevices = dedupeDevices(nextDevices, dedupeRef.current);
96
+ if (activeScanRef.current === scanId) {
97
+ setDevices(finalDevices);
98
+ setError(null);
99
+ }
100
+ return finalDevices;
101
+ }
102
+ catch (nextError) {
103
+ if (activeScanRef.current === scanId) {
104
+ setError(nextError);
105
+ onErrorRef.current?.(nextError);
106
+ }
107
+ throw nextError;
108
+ }
109
+ finally {
110
+ if (activeScanRef.current === scanId) {
111
+ setScanning(false);
112
+ scanningRef.current = false;
113
+ }
114
+ }
115
+ }
116
+ async function stopScan() {
117
+ activeScanRef.current += 1;
118
+ setScanning(false);
119
+ scanningRef.current = false;
120
+ await BluetoothSdk.stopScan();
121
+ }
122
+ return {
123
+ clearResults,
124
+ devices,
125
+ error,
126
+ model,
127
+ scanning,
128
+ selectedDevice,
129
+ selectDevice,
130
+ setModel,
131
+ startScan,
132
+ stopScan,
133
+ };
134
+ }
135
+ //# sourceMappingURL=useBluetoothScan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBluetoothScan.js","sourceRoot":"","sources":["../../src/react/useBluetoothScan.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef, useState} from \"react\"\n\nimport BluetoothSdk, {DeviceModels} from \"../index\"\nimport type {Device, DeviceModel} from \"../BluetoothSdk.types\"\n\nexport type BluetoothScanDedupe = \"id\" | \"name\" | ((device: Device) => string)\n\nexport type UseBluetoothScanOptions = {\n dedupe?: BluetoothScanDedupe\n model?: DeviceModel\n onError?: (error: unknown) => void\n timeoutMs?: number\n}\n\nexport type BluetoothScanHookResult = {\n clearResults: () => void\n devices: Device[]\n error: unknown | null\n model: DeviceModel\n scanning: boolean\n selectedDevice: Device | null\n selectDevice: (device: Device | null) => void\n setModel: (model: DeviceModel) => void\n startScan: (model?: DeviceModel) => Promise<Device[]>\n stopScan: () => Promise<void>\n}\n\nfunction dedupeKey(device: Device, dedupe: BluetoothScanDedupe): string {\n if (typeof dedupe === \"function\") {\n return dedupe(device)\n }\n if (dedupe === \"name\") {\n return `${device.model}:${device.name}`\n }\n return device.id\n}\n\nfunction dedupeDevices(devices: Device[], dedupe: BluetoothScanDedupe): Device[] {\n const seen = new Set<string>()\n const nextDevices: Device[] = []\n\n devices.forEach((device) => {\n const key = dedupeKey(device, dedupe)\n if (seen.has(key)) {\n return\n }\n seen.add(key)\n nextDevices.push(device)\n })\n\n return nextDevices\n}\n\nfunction hasDevice(devices: Device[], selectedDevice: Device | null, dedupe: BluetoothScanDedupe): boolean {\n if (!selectedDevice) {\n return true\n }\n const selectedKey = dedupeKey(selectedDevice, dedupe)\n return devices.some((device) => dedupeKey(device, dedupe) === selectedKey)\n}\n\nexport function useBluetoothScan(options: UseBluetoothScanOptions = {}): BluetoothScanHookResult {\n const [devices, setDevices] = useState<Device[]>([])\n const [error, setError] = useState<unknown | null>(null)\n const [model, setModelState] = useState<DeviceModel>(options.model ?? DeviceModels.MentraLive)\n const [scanning, setScanning] = useState(false)\n const [selectedDevice, selectDevice] = useState<Device | null>(null)\n const activeScanRef = useRef(0)\n const dedupeRef = useRef<BluetoothScanDedupe>(options.dedupe ?? \"id\")\n const onErrorRef = useRef(options.onError)\n const scanningRef = useRef(false)\n const timeoutMsRef = useRef(options.timeoutMs)\n\n useEffect(() => {\n dedupeRef.current = options.dedupe ?? \"id\"\n onErrorRef.current = options.onError\n timeoutMsRef.current = options.timeoutMs\n }, [options.dedupe, options.onError, options.timeoutMs])\n\n useEffect(() => {\n scanningRef.current = scanning\n }, [scanning])\n\n useEffect(() => {\n if (!hasDevice(devices, selectedDevice, dedupeRef.current)) {\n selectDevice(null)\n }\n }, [devices, selectedDevice])\n\n useEffect(() => {\n return () => {\n if (!scanningRef.current) {\n return\n }\n activeScanRef.current += 1\n void BluetoothSdk.stopScan().catch(() => undefined)\n }\n }, [])\n\n function clearResults() {\n setDevices([])\n selectDevice(null)\n }\n\n function setModel(nextModel: DeviceModel) {\n setModelState(nextModel)\n clearResults()\n }\n\n async function startScan(nextModel?: DeviceModel): Promise<Device[]> {\n const scanId = activeScanRef.current + 1\n activeScanRef.current = scanId\n const scanModel = nextModel ?? model\n if (nextModel && nextModel !== model) {\n setModelState(nextModel)\n }\n\n setError(null)\n setScanning(true)\n scanningRef.current = true\n setDevices([])\n selectDevice(null)\n\n try {\n const nextDevices = await BluetoothSdk.scan(scanModel, {\n ...(timeoutMsRef.current == null ? {} : {timeoutMs: timeoutMsRef.current}),\n onResults: (results) => {\n if (activeScanRef.current !== scanId) {\n return\n }\n setDevices(dedupeDevices(results, dedupeRef.current))\n },\n })\n const finalDevices = dedupeDevices(nextDevices, dedupeRef.current)\n if (activeScanRef.current === scanId) {\n setDevices(finalDevices)\n setError(null)\n }\n return finalDevices\n } catch (nextError) {\n if (activeScanRef.current === scanId) {\n setError(nextError)\n onErrorRef.current?.(nextError)\n }\n throw nextError\n } finally {\n if (activeScanRef.current === scanId) {\n setScanning(false)\n scanningRef.current = false\n }\n }\n }\n\n async function stopScan() {\n activeScanRef.current += 1\n setScanning(false)\n scanningRef.current = false\n await BluetoothSdk.stopScan()\n }\n\n return {\n clearResults,\n devices,\n error,\n model,\n scanning,\n selectedDevice,\n selectDevice,\n setModel,\n startScan,\n stopScan,\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { PublicBluetoothStatus, PublicGlassesStatus } from "../BluetoothSdk.types";
2
+ export type UseBluetoothStatusOptions = {
3
+ enabled?: boolean;
4
+ onError?: (error: unknown) => void;
5
+ };
6
+ export type BluetoothStatusHookResult = {
7
+ bluetoothStatus: Partial<PublicBluetoothStatus>;
8
+ connected: boolean;
9
+ error: unknown | null;
10
+ glassesStatus: Partial<PublicGlassesStatus>;
11
+ loading: boolean;
12
+ ready: boolean;
13
+ refresh: () => Promise<void>;
14
+ };
15
+ export declare function useBluetoothStatus(options?: UseBluetoothStatusOptions): BluetoothStatusHookResult;
16
+ //# sourceMappingURL=useBluetoothStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBluetoothStatus.d.ts","sourceRoot":"","sources":["../../src/react/useBluetoothStatus.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAIV,qBAAqB,EACrB,mBAAmB,EAGpB,MAAM,uBAAuB,CAAA;AAE9B,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,eAAe,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAC/C,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IACrB,aAAa,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC3C,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B,CAAA;AAiCD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,yBAAyB,CAmHrG"}
@@ -0,0 +1,137 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import BluetoothSdk from "../_private/BluetoothSdkModule";
3
+ import { createDisconnectedGlassesStatus, isConnectedGlassesConnectionStatus, isReadyGlassesConnectionStatus, } from "../BluetoothSdk.types";
4
+ function wifiStatusFromEvent(event) {
5
+ switch (event.state) {
6
+ case "connected":
7
+ return { state: "connected", ssid: event.ssid, localIp: event.localIp };
8
+ case "disconnected":
9
+ return { state: "disconnected" };
10
+ }
11
+ }
12
+ function hotspotStatusFromEvent(event) {
13
+ if (event.state === "enabled") {
14
+ return {
15
+ state: "enabled",
16
+ ssid: event.ssid,
17
+ password: event.password,
18
+ localIp: event.localIp,
19
+ };
20
+ }
21
+ return { state: event.state };
22
+ }
23
+ function mergeGlassesStatus(current, changed) {
24
+ if (changed.connection?.state === "disconnected") {
25
+ return { ...createDisconnectedGlassesStatus(), ...changed };
26
+ }
27
+ return { ...current, ...changed };
28
+ }
29
+ export function useBluetoothStatus(options = {}) {
30
+ const enabled = options.enabled ?? true;
31
+ const onErrorRef = useRef(options.onError);
32
+ const [bluetoothStatus, setBluetoothStatus] = useState({});
33
+ const [error, setError] = useState(null);
34
+ const [glassesStatus, setGlassesStatus] = useState(() => createDisconnectedGlassesStatus());
35
+ const [loading, setLoading] = useState(enabled);
36
+ useEffect(() => {
37
+ onErrorRef.current = options.onError;
38
+ }, [options.onError]);
39
+ async function refresh() {
40
+ setLoading(true);
41
+ try {
42
+ const [nextGlassesStatus, nextBluetoothStatus] = await Promise.all([
43
+ BluetoothSdk.getGlassesStatus(),
44
+ BluetoothSdk.getBluetoothStatus(),
45
+ ]);
46
+ setGlassesStatus(nextGlassesStatus);
47
+ setBluetoothStatus(nextBluetoothStatus);
48
+ setError(null);
49
+ }
50
+ catch (nextError) {
51
+ setError(nextError);
52
+ onErrorRef.current?.(nextError);
53
+ }
54
+ finally {
55
+ setLoading(false);
56
+ }
57
+ }
58
+ useEffect(() => {
59
+ if (!enabled) {
60
+ setLoading(false);
61
+ return undefined;
62
+ }
63
+ let mounted = true;
64
+ const loadInitialStatus = async () => {
65
+ setLoading(true);
66
+ try {
67
+ const [nextGlassesStatus, nextBluetoothStatus] = await Promise.all([
68
+ BluetoothSdk.getGlassesStatus(),
69
+ BluetoothSdk.getBluetoothStatus(),
70
+ ]);
71
+ if (!mounted) {
72
+ return;
73
+ }
74
+ setGlassesStatus(nextGlassesStatus);
75
+ setBluetoothStatus(nextBluetoothStatus);
76
+ setError(null);
77
+ }
78
+ catch (nextError) {
79
+ if (!mounted) {
80
+ return;
81
+ }
82
+ setError(nextError);
83
+ onErrorRef.current?.(nextError);
84
+ }
85
+ finally {
86
+ if (mounted) {
87
+ setLoading(false);
88
+ }
89
+ }
90
+ };
91
+ void loadInitialStatus();
92
+ const removeGlasses = BluetoothSdk.onGlassesStatus((changed) => {
93
+ setGlassesStatus((current) => mergeGlassesStatus(current, changed));
94
+ });
95
+ const removeBluetooth = BluetoothSdk.onBluetoothStatus((changed) => {
96
+ setBluetoothStatus((current) => ({ ...current, ...changed }));
97
+ });
98
+ const batterySubscription = BluetoothSdk.addListener("battery_status", (event) => {
99
+ setGlassesStatus((current) => ({
100
+ ...current,
101
+ batteryLevel: event.level,
102
+ charging: event.charging,
103
+ }));
104
+ });
105
+ const wifiSubscription = BluetoothSdk.addListener("wifi_status_change", (event) => {
106
+ setGlassesStatus((current) => ({ ...current, wifi: wifiStatusFromEvent(event) }));
107
+ });
108
+ const hotspotSubscription = BluetoothSdk.addListener("hotspot_status_change", (event) => {
109
+ setGlassesStatus((current) => ({ ...current, hotspot: hotspotStatusFromEvent(event) }));
110
+ });
111
+ const hotspotErrorSubscription = BluetoothSdk.addListener("hotspot_error", () => {
112
+ setGlassesStatus((current) => ({ ...current, hotspot: { state: "disabled" } }));
113
+ });
114
+ return () => {
115
+ mounted = false;
116
+ removeGlasses();
117
+ removeBluetooth();
118
+ batterySubscription.remove();
119
+ wifiSubscription.remove();
120
+ hotspotSubscription.remove();
121
+ hotspotErrorSubscription.remove();
122
+ };
123
+ }, [enabled]);
124
+ const connection = glassesStatus.connection;
125
+ const connected = connection ? isConnectedGlassesConnectionStatus(connection) : false;
126
+ const ready = connection ? isReadyGlassesConnectionStatus(connection) : false;
127
+ return {
128
+ bluetoothStatus,
129
+ connected,
130
+ error,
131
+ glassesStatus,
132
+ loading,
133
+ ready,
134
+ refresh,
135
+ };
136
+ }
137
+ //# sourceMappingURL=useBluetoothStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBluetoothStatus.js","sourceRoot":"","sources":["../../src/react/useBluetoothStatus.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef, useState} from \"react\"\n\nimport BluetoothSdk from \"../_private/BluetoothSdkModule\"\nimport {\n createDisconnectedGlassesStatus,\n isConnectedGlassesConnectionStatus,\n isReadyGlassesConnectionStatus,\n} from \"../BluetoothSdk.types\"\nimport type {\n BatteryStatusEvent,\n HotspotStatus,\n HotspotStatusChangeEvent,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n WifiStatus,\n WifiStatusChangeEvent,\n} from \"../BluetoothSdk.types\"\n\nexport type UseBluetoothStatusOptions = {\n enabled?: boolean\n onError?: (error: unknown) => void\n}\n\nexport type BluetoothStatusHookResult = {\n bluetoothStatus: Partial<PublicBluetoothStatus>\n connected: boolean\n error: unknown | null\n glassesStatus: Partial<PublicGlassesStatus>\n loading: boolean\n ready: boolean\n refresh: () => Promise<void>\n}\n\nfunction wifiStatusFromEvent(event: WifiStatusChangeEvent): WifiStatus {\n switch (event.state) {\n case \"connected\":\n return {state: \"connected\", ssid: event.ssid, localIp: event.localIp}\n case \"disconnected\":\n return {state: \"disconnected\"}\n }\n}\n\nfunction hotspotStatusFromEvent(event: HotspotStatusChangeEvent): HotspotStatus {\n if (event.state === \"enabled\") {\n return {\n state: \"enabled\",\n ssid: event.ssid,\n password: event.password,\n localIp: event.localIp,\n }\n }\n return {state: event.state}\n}\n\nfunction mergeGlassesStatus(\n current: Partial<PublicGlassesStatus>,\n changed: Partial<PublicGlassesStatus>,\n): Partial<PublicGlassesStatus> {\n if (changed.connection?.state === \"disconnected\") {\n return {...createDisconnectedGlassesStatus(), ...changed}\n }\n return {...current, ...changed}\n}\n\nexport function useBluetoothStatus(options: UseBluetoothStatusOptions = {}): BluetoothStatusHookResult {\n const enabled = options.enabled ?? true\n const onErrorRef = useRef(options.onError)\n const [bluetoothStatus, setBluetoothStatus] = useState<Partial<PublicBluetoothStatus>>({})\n const [error, setError] = useState<unknown | null>(null)\n const [glassesStatus, setGlassesStatus] = useState<Partial<PublicGlassesStatus>>(() =>\n createDisconnectedGlassesStatus(),\n )\n const [loading, setLoading] = useState(enabled)\n\n useEffect(() => {\n onErrorRef.current = options.onError\n }, [options.onError])\n\n async function refresh() {\n setLoading(true)\n try {\n const [nextGlassesStatus, nextBluetoothStatus] = await Promise.all([\n BluetoothSdk.getGlassesStatus(),\n BluetoothSdk.getBluetoothStatus(),\n ])\n setGlassesStatus(nextGlassesStatus)\n setBluetoothStatus(nextBluetoothStatus)\n setError(null)\n } catch (nextError) {\n setError(nextError)\n onErrorRef.current?.(nextError)\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n if (!enabled) {\n setLoading(false)\n return undefined\n }\n\n let mounted = true\n\n const loadInitialStatus = async () => {\n setLoading(true)\n try {\n const [nextGlassesStatus, nextBluetoothStatus] = await Promise.all([\n BluetoothSdk.getGlassesStatus(),\n BluetoothSdk.getBluetoothStatus(),\n ])\n if (!mounted) {\n return\n }\n setGlassesStatus(nextGlassesStatus)\n setBluetoothStatus(nextBluetoothStatus)\n setError(null)\n } catch (nextError) {\n if (!mounted) {\n return\n }\n setError(nextError)\n onErrorRef.current?.(nextError)\n } finally {\n if (mounted) {\n setLoading(false)\n }\n }\n }\n\n void loadInitialStatus()\n\n const removeGlasses = BluetoothSdk.onGlassesStatus((changed) => {\n setGlassesStatus((current) => mergeGlassesStatus(current, changed))\n })\n const removeBluetooth = BluetoothSdk.onBluetoothStatus((changed) => {\n setBluetoothStatus((current) => ({...current, ...changed}))\n })\n const batterySubscription = BluetoothSdk.addListener(\"battery_status\", (event: BatteryStatusEvent) => {\n setGlassesStatus((current) => ({\n ...current,\n batteryLevel: event.level,\n charging: event.charging,\n }))\n })\n const wifiSubscription = BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n setGlassesStatus((current) => ({...current, wifi: wifiStatusFromEvent(event)}))\n })\n const hotspotSubscription = BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n setGlassesStatus((current) => ({...current, hotspot: hotspotStatusFromEvent(event)}))\n })\n const hotspotErrorSubscription = BluetoothSdk.addListener(\"hotspot_error\", () => {\n setGlassesStatus((current) => ({...current, hotspot: {state: \"disabled\"}}))\n })\n\n return () => {\n mounted = false\n removeGlasses()\n removeBluetooth()\n batterySubscription.remove()\n wifiSubscription.remove()\n hotspotSubscription.remove()\n hotspotErrorSubscription.remove()\n }\n }, [enabled])\n\n const connection = glassesStatus.connection\n const connected = connection ? isConnectedGlassesConnectionStatus(connection) : false\n const ready = connection ? isReadyGlassesConnectionStatus(connection) : false\n\n return {\n bluetoothStatus,\n connected,\n error,\n glassesStatus,\n loading,\n ready,\n refresh,\n }\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import type { ConnectOptions, Device, DeviceModel } from "../BluetoothSdk.types";
2
+ import { type BluetoothScanHookResult } from "./useBluetoothScan";
3
+ import { type BluetoothStatusHookResult } from "./useBluetoothStatus";
4
+ export type DefaultDeviceStorage = {
5
+ load: () => Promise<Device | null>;
6
+ save: (device: Device | null) => Promise<void>;
7
+ };
8
+ export type GlassesConnectionAction = "idle" | "scanning" | "connecting" | "disconnecting" | "forgetting";
9
+ export type UseGlassesConnectionOptions = {
10
+ autoConnectDefault?: boolean;
11
+ defaultDeviceStorage?: DefaultDeviceStorage;
12
+ onError?: (error: unknown) => void;
13
+ scanModel?: DeviceModel;
14
+ scanTimeoutMs?: number;
15
+ };
16
+ export type GlassesConnectionHookResult = BluetoothStatusHookResult & {
17
+ action: GlassesConnectionAction;
18
+ busy: boolean;
19
+ clearDefaultDevice: () => Promise<void>;
20
+ connect: (device?: Device, options?: ConnectOptions) => Promise<void>;
21
+ connectDefault: (options?: ConnectOptions) => Promise<void>;
22
+ defaultDevice: Device | null;
23
+ disconnect: () => Promise<void>;
24
+ forget: () => Promise<void>;
25
+ scan: BluetoothScanHookResult;
26
+ setDefaultDevice: (device: Device | null) => Promise<void>;
27
+ };
28
+ export declare function useGlassesConnection(options?: UseGlassesConnectionOptions): GlassesConnectionHookResult;
29
+ //# sourceMappingURL=useGlassesConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGlassesConnection.d.ts","sourceRoot":"","sources":["../../src/react/useGlassesConnection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAA;AAE9E,OAAO,EAAmB,KAAK,uBAAuB,EAAC,MAAM,oBAAoB,CAAA;AACjF,OAAO,EAAqB,KAAK,yBAAyB,EAAC,MAAM,sBAAsB,CAAA;AAEvF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAClC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C,CAAA;AAED,MAAM,MAAM,uBAAuB,GAC/B,MAAM,GACN,UAAU,GACV,YAAY,GACZ,eAAe,GACf,YAAY,CAAA;AAEhB,MAAM,MAAM,2BAA2B,GAAG;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,SAAS,CAAC,EAAE,WAAW,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,GAAG;IACpE,MAAM,EAAE,uBAAuB,CAAA;IAC/B,IAAI,EAAE,OAAO,CAAA;IACb,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,EAAE,uBAAuB,CAAA;IAC7B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3D,CAAA;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC,GACxC,2BAA2B,CAqK7B"}
@@ -0,0 +1,153 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import BluetoothSdk from "../index";
3
+ import { useBluetoothScan } from "./useBluetoothScan";
4
+ import { useBluetoothStatus } from "./useBluetoothStatus";
5
+ export function useGlassesConnection(options = {}) {
6
+ const status = useBluetoothStatus({ onError: options.onError });
7
+ const scan = useBluetoothScan({
8
+ model: options.scanModel,
9
+ onError: options.onError,
10
+ timeoutMs: options.scanTimeoutMs,
11
+ });
12
+ const [action, setAction] = useState("idle");
13
+ const [defaultDevice, setDefaultDeviceState] = useState(null);
14
+ const [operationError, setOperationError] = useState(null);
15
+ const autoConnectAttemptedRef = useRef(false);
16
+ const defaultDeviceStorageRef = useRef(options.defaultDeviceStorage);
17
+ const onErrorRef = useRef(options.onError);
18
+ useEffect(() => {
19
+ defaultDeviceStorageRef.current = options.defaultDeviceStorage;
20
+ onErrorRef.current = options.onError;
21
+ }, [options.defaultDeviceStorage, options.onError]);
22
+ useEffect(() => {
23
+ let mounted = true;
24
+ const loadDefaultDevice = async () => {
25
+ try {
26
+ const [nativeDefaultDevice, storedDefaultDevice] = await Promise.all([
27
+ BluetoothSdk.getDefaultDevice(),
28
+ defaultDeviceStorageRef.current?.load() ?? Promise.resolve(null),
29
+ ]);
30
+ if (!mounted) {
31
+ return;
32
+ }
33
+ const nextDefaultDevice = storedDefaultDevice ?? nativeDefaultDevice;
34
+ if (nextDefaultDevice) {
35
+ await BluetoothSdk.setDefaultDevice(nextDefaultDevice);
36
+ }
37
+ if (mounted) {
38
+ setDefaultDeviceState(nextDefaultDevice);
39
+ setOperationError(null);
40
+ }
41
+ }
42
+ catch (nextError) {
43
+ if (!mounted) {
44
+ return;
45
+ }
46
+ setOperationError(nextError);
47
+ onErrorRef.current?.(nextError);
48
+ }
49
+ };
50
+ void loadDefaultDevice();
51
+ return () => {
52
+ mounted = false;
53
+ };
54
+ }, []);
55
+ useEffect(() => {
56
+ const subscription = BluetoothSdk.addListener("default_device_changed", (event) => {
57
+ const nextDefaultDevice = event.device ?? null;
58
+ setDefaultDeviceState(nextDefaultDevice);
59
+ void defaultDeviceStorageRef.current?.save(nextDefaultDevice).catch((nextError) => {
60
+ setOperationError(nextError);
61
+ onErrorRef.current?.(nextError);
62
+ });
63
+ });
64
+ return () => {
65
+ subscription.remove();
66
+ };
67
+ }, []);
68
+ useEffect(() => {
69
+ if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {
70
+ return;
71
+ }
72
+ autoConnectAttemptedRef.current = true;
73
+ void connectDefault().catch(() => undefined);
74
+ }, [defaultDevice, options.autoConnectDefault, status.connected]);
75
+ async function runConnectionAction(nextAction, operation) {
76
+ setAction(nextAction);
77
+ setOperationError(null);
78
+ try {
79
+ const result = await operation();
80
+ setOperationError(null);
81
+ return result;
82
+ }
83
+ catch (nextError) {
84
+ setOperationError(nextError);
85
+ onErrorRef.current?.(nextError);
86
+ throw nextError;
87
+ }
88
+ finally {
89
+ setAction("idle");
90
+ }
91
+ }
92
+ async function setDefaultDevice(device) {
93
+ await runConnectionAction("connecting", async () => {
94
+ await BluetoothSdk.setDefaultDevice(device);
95
+ await defaultDeviceStorageRef.current?.save(device);
96
+ setDefaultDeviceState(device);
97
+ });
98
+ }
99
+ async function clearDefaultDevice() {
100
+ await runConnectionAction("forgetting", async () => {
101
+ await BluetoothSdk.clearDefaultDevice();
102
+ await defaultDeviceStorageRef.current?.save(null);
103
+ setDefaultDeviceState(null);
104
+ scan.selectDevice(null);
105
+ });
106
+ }
107
+ async function connect(device, connectOptions) {
108
+ await runConnectionAction("connecting", async () => {
109
+ const targetDevice = device ?? scan.selectedDevice;
110
+ if (targetDevice) {
111
+ await BluetoothSdk.connect(targetDevice, connectOptions);
112
+ if (connectOptions?.saveAsDefault !== false) {
113
+ await defaultDeviceStorageRef.current?.save(targetDevice);
114
+ setDefaultDeviceState(targetDevice);
115
+ }
116
+ return;
117
+ }
118
+ await BluetoothSdk.connectDefault(connectOptions);
119
+ });
120
+ }
121
+ async function connectDefault(connectOptions) {
122
+ await runConnectionAction("connecting", async () => {
123
+ await BluetoothSdk.connectDefault(connectOptions);
124
+ });
125
+ }
126
+ async function disconnect() {
127
+ await runConnectionAction("disconnecting", async () => {
128
+ await BluetoothSdk.disconnect();
129
+ });
130
+ }
131
+ async function forget() {
132
+ await runConnectionAction("forgetting", async () => {
133
+ await BluetoothSdk.forget();
134
+ scan.clearResults();
135
+ });
136
+ }
137
+ const busy = action !== "idle" || scan.scanning || status.loading;
138
+ return {
139
+ ...status,
140
+ action: scan.scanning ? "scanning" : action,
141
+ busy,
142
+ clearDefaultDevice,
143
+ connect,
144
+ connectDefault,
145
+ defaultDevice,
146
+ disconnect,
147
+ error: operationError ?? scan.error ?? status.error,
148
+ forget,
149
+ scan,
150
+ setDefaultDevice,
151
+ };
152
+ }
153
+ //# sourceMappingURL=useGlassesConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGlassesConnection.js","sourceRoot":"","sources":["../../src/react/useGlassesConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAA;AAEjD,OAAO,YAAY,MAAM,UAAU,CAAA;AAGnC,OAAO,EAAC,gBAAgB,EAA+B,MAAM,oBAAoB,CAAA;AACjF,OAAO,EAAC,kBAAkB,EAAiC,MAAM,sBAAsB,CAAA;AAmCvF,MAAM,UAAU,oBAAoB,CAClC,UAAuC,EAAE;IAEzC,MAAM,MAAM,GAAG,kBAAkB,CAAC,EAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAC,CAAC,CAAA;IAC7D,MAAM,IAAI,GAAG,gBAAgB,CAAC;QAC5B,KAAK,EAAE,OAAO,CAAC,SAAS;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,aAAa;KACjC,CAAC,CAAA;IACF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA0B,MAAM,CAAC,CAAA;IACrE,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAC5E,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAA;IAC1E,MAAM,uBAAuB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACpE,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAE1C,SAAS,CAAC,GAAG,EAAE;QACb,uBAAuB,CAAC,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAA;QAC9D,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;IACtC,CAAC,EAAE,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;IAEnD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,GAAG,IAAI,CAAA;QAElB,MAAM,iBAAiB,GAAG,KAAK,IAAI,EAAE;YACnC,IAAI,CAAC;gBACH,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;oBACnE,YAAY,CAAC,gBAAgB,EAAE;oBAC/B,uBAAuB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;iBACjE,CAAC,CAAA;gBACF,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAM;gBACR,CAAC;gBACD,MAAM,iBAAiB,GAAG,mBAAmB,IAAI,mBAAmB,CAAA;gBACpE,IAAI,iBAAiB,EAAE,CAAC;oBACtB,MAAM,YAAY,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAA;gBACxD,CAAC;gBACD,IAAI,OAAO,EAAE,CAAC;oBACZ,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;oBACxC,iBAAiB,CAAC,IAAI,CAAC,CAAA;gBACzB,CAAC;YACH,CAAC;YAAC,OAAO,SAAS,EAAE,CAAC;gBACnB,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAM;gBACR,CAAC;gBACD,iBAAiB,CAAC,SAAS,CAAC,CAAA;gBAC5B,UAAU,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAA;YACjC,CAAC;QACH,CAAC,CAAA;QAED,KAAK,iBAAiB,EAAE,CAAA;QAExB,OAAO,GAAG,EAAE;YACV,OAAO,GAAG,KAAK,CAAA;QACjB,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,wBAAwB,EAAE,CAAC,KAAK,EAAE,EAAE;YAChF,MAAM,iBAAiB,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAA;YAC9C,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;YACxC,KAAK,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE;gBAChF,iBAAiB,CAAC,SAAS,CAAC,CAAA;gBAC5B,UAAU,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAA;YACjC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAA;QACvB,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,CAAC,kBAAkB,IAAI,uBAAuB,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;YACzG,OAAM;QACR,CAAC;QAED,uBAAuB,CAAC,OAAO,GAAG,IAAI,CAAA;QACtC,KAAK,cAAc,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC9C,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,kBAAkB,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;IAEjE,KAAK,UAAU,mBAAmB,CAChC,UAAiE,EACjE,SAA2B;QAE3B,SAAS,CAAC,UAAU,CAAC,CAAA;QACrB,iBAAiB,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;YAChC,iBAAiB,CAAC,IAAI,CAAC,CAAA;YACvB,OAAO,MAAM,CAAA;QACf,CAAC;QAAC,OAAO,SAAS,EAAE,CAAC;YACnB,iBAAiB,CAAC,SAAS,CAAC,CAAA;YAC5B,UAAU,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAA;YAC/B,MAAM,SAAS,CAAA;QACjB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,MAAM,CAAC,CAAA;QACnB,CAAC;IACH,CAAC;IAED,KAAK,UAAU,gBAAgB,CAAC,MAAqB;QACnD,MAAM,mBAAmB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,YAAY,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAA;YAC3C,MAAM,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;YACnD,qBAAqB,CAAC,MAAM,CAAC,CAAA;QAC/B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,UAAU,kBAAkB;QAC/B,MAAM,mBAAmB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,YAAY,CAAC,kBAAkB,EAAE,CAAA;YACvC,MAAM,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;YACjD,qBAAqB,CAAC,IAAI,CAAC,CAAA;YAC3B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,MAAe,EAAE,cAA+B;QACrE,MAAM,mBAAmB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,YAAY,GAAG,MAAM,IAAI,IAAI,CAAC,cAAc,CAAA;YAClD,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;gBACxD,IAAI,cAAc,EAAE,aAAa,KAAK,KAAK,EAAE,CAAC;oBAC5C,MAAM,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;oBACzD,qBAAqB,CAAC,YAAY,CAAC,CAAA;gBACrC,CAAC;gBACD,OAAM;YACR,CAAC;YACD,MAAM,YAAY,CAAC,cAAc,CAAC,cAAc,CAAC,CAAA;QACnD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,UAAU,cAAc,CAAC,cAA+B;QAC3D,MAAM,mBAAmB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,YAAY,CAAC,cAAc,CAAC,cAAc,CAAC,CAAA;QACnD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,UAAU,UAAU;QACvB,MAAM,mBAAmB,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,YAAY,CAAC,UAAU,EAAE,CAAA;QACjC,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,UAAU,MAAM;QACnB,MAAM,mBAAmB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,YAAY,CAAC,MAAM,EAAE,CAAA;YAC3B,IAAI,CAAC,YAAY,EAAE,CAAA;QACrB,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAA;IAEjE,OAAO;QACL,GAAG,MAAM;QACT,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;QAC3C,IAAI;QACJ,kBAAkB;QAClB,OAAO;QACP,cAAc;QACd,aAAa;QACb,UAAU;QACV,KAAK,EAAE,cAAc,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;QACnD,MAAM;QACN,IAAI;QACJ,gBAAgB;KACjB,CAAA;AACH,CAAC","sourcesContent":["import {useEffect, useRef, useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport type {ConnectOptions, Device, DeviceModel} from \"../BluetoothSdk.types\"\n\nimport {useBluetoothScan, type BluetoothScanHookResult} from \"./useBluetoothScan\"\nimport {useBluetoothStatus, type BluetoothStatusHookResult} from \"./useBluetoothStatus\"\n\nexport type DefaultDeviceStorage = {\n load: () => Promise<Device | null>\n save: (device: Device | null) => Promise<void>\n}\n\nexport type GlassesConnectionAction =\n | \"idle\"\n | \"scanning\"\n | \"connecting\"\n | \"disconnecting\"\n | \"forgetting\"\n\nexport type UseGlassesConnectionOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n onError?: (error: unknown) => void\n scanModel?: DeviceModel\n scanTimeoutMs?: number\n}\n\nexport type GlassesConnectionHookResult = BluetoothStatusHookResult & {\n action: GlassesConnectionAction\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n forget: () => Promise<void>\n scan: BluetoothScanHookResult\n setDefaultDevice: (device: Device | null) => Promise<void>\n}\n\nexport function useGlassesConnection(\n options: UseGlassesConnectionOptions = {},\n): GlassesConnectionHookResult {\n const status = useBluetoothStatus({onError: options.onError})\n const scan = useBluetoothScan({\n model: options.scanModel,\n onError: options.onError,\n timeoutMs: options.scanTimeoutMs,\n })\n const [action, setAction] = useState<GlassesConnectionAction>(\"idle\")\n const [defaultDevice, setDefaultDeviceState] = useState<Device | null>(null)\n const [operationError, setOperationError] = useState<unknown | null>(null)\n const autoConnectAttemptedRef = useRef(false)\n const defaultDeviceStorageRef = useRef(options.defaultDeviceStorage)\n const onErrorRef = useRef(options.onError)\n\n useEffect(() => {\n defaultDeviceStorageRef.current = options.defaultDeviceStorage\n onErrorRef.current = options.onError\n }, [options.defaultDeviceStorage, options.onError])\n\n useEffect(() => {\n let mounted = true\n\n const loadDefaultDevice = async () => {\n try {\n const [nativeDefaultDevice, storedDefaultDevice] = await Promise.all([\n BluetoothSdk.getDefaultDevice(),\n defaultDeviceStorageRef.current?.load() ?? Promise.resolve(null),\n ])\n if (!mounted) {\n return\n }\n const nextDefaultDevice = storedDefaultDevice ?? nativeDefaultDevice\n if (nextDefaultDevice) {\n await BluetoothSdk.setDefaultDevice(nextDefaultDevice)\n }\n if (mounted) {\n setDefaultDeviceState(nextDefaultDevice)\n setOperationError(null)\n }\n } catch (nextError) {\n if (!mounted) {\n return\n }\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n }\n }\n\n void loadDefaultDevice()\n\n return () => {\n mounted = false\n }\n }, [])\n\n useEffect(() => {\n const subscription = BluetoothSdk.addListener(\"default_device_changed\", (event) => {\n const nextDefaultDevice = event.device ?? null\n setDefaultDeviceState(nextDefaultDevice)\n void defaultDeviceStorageRef.current?.save(nextDefaultDevice).catch((nextError) => {\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n })\n })\n\n return () => {\n subscription.remove()\n }\n }, [])\n\n useEffect(() => {\n if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {\n return\n }\n\n autoConnectAttemptedRef.current = true\n void connectDefault().catch(() => undefined)\n }, [defaultDevice, options.autoConnectDefault, status.connected])\n\n async function runConnectionAction<T>(\n nextAction: Exclude<GlassesConnectionAction, \"idle\" | \"scanning\">,\n operation: () => Promise<T>,\n ): Promise<T> {\n setAction(nextAction)\n setOperationError(null)\n try {\n const result = await operation()\n setOperationError(null)\n return result\n } catch (nextError) {\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n throw nextError\n } finally {\n setAction(\"idle\")\n }\n }\n\n async function setDefaultDevice(device: Device | null) {\n await runConnectionAction(\"connecting\", async () => {\n await BluetoothSdk.setDefaultDevice(device)\n await defaultDeviceStorageRef.current?.save(device)\n setDefaultDeviceState(device)\n })\n }\n\n async function clearDefaultDevice() {\n await runConnectionAction(\"forgetting\", async () => {\n await BluetoothSdk.clearDefaultDevice()\n await defaultDeviceStorageRef.current?.save(null)\n setDefaultDeviceState(null)\n scan.selectDevice(null)\n })\n }\n\n async function connect(device?: Device, connectOptions?: ConnectOptions) {\n await runConnectionAction(\"connecting\", async () => {\n const targetDevice = device ?? scan.selectedDevice\n if (targetDevice) {\n await BluetoothSdk.connect(targetDevice, connectOptions)\n if (connectOptions?.saveAsDefault !== false) {\n await defaultDeviceStorageRef.current?.save(targetDevice)\n setDefaultDeviceState(targetDevice)\n }\n return\n }\n await BluetoothSdk.connectDefault(connectOptions)\n })\n }\n\n async function connectDefault(connectOptions?: ConnectOptions) {\n await runConnectionAction(\"connecting\", async () => {\n await BluetoothSdk.connectDefault(connectOptions)\n })\n }\n\n async function disconnect() {\n await runConnectionAction(\"disconnecting\", async () => {\n await BluetoothSdk.disconnect()\n })\n }\n\n async function forget() {\n await runConnectionAction(\"forgetting\", async () => {\n await BluetoothSdk.forget()\n scan.clearResults()\n })\n }\n\n const busy = action !== \"idle\" || scan.scanning || status.loading\n\n return {\n ...status,\n action: scan.scanning ? \"scanning\" : action,\n busy,\n clearDefaultDevice,\n connect,\n connectDefault,\n defaultDevice,\n disconnect,\n error: operationError ?? scan.error ?? status.error,\n forget,\n scan,\n setDefaultDevice,\n }\n}\n"]}
@@ -0,0 +1,98 @@
1
+ import type { ConnectOptions, Device, DeviceModel, GalleryMode, GlassesConnectionStatus, HotspotStatus, MicMode, WifiSearchResult, WifiStatus } from "../BluetoothSdk.types";
2
+ import { type DefaultDeviceStorage } from "./useGlassesConnection";
3
+ export type BatteryState = {
4
+ charging: boolean;
5
+ level: number | null;
6
+ };
7
+ export type ConnectedGlassesInfo = {
8
+ appVersion?: string;
9
+ bluetoothName?: string;
10
+ buildNumber?: string;
11
+ color?: string;
12
+ deviceModel?: string;
13
+ firmwareVersion?: string;
14
+ serialNumber?: string;
15
+ style?: string;
16
+ };
17
+ export type FirmwareInfo = {
18
+ appVersion?: string;
19
+ buildNumber?: string;
20
+ source: "app" | "bes" | "device" | "firmware" | "left" | "mtk" | "right" | "unknown";
21
+ version: string | null;
22
+ };
23
+ export type SignalState = {
24
+ strengthDbm: number | null;
25
+ updatedAt: number | null;
26
+ };
27
+ export type GlassesRuntimeState = {
28
+ connected: false;
29
+ connection: Exclude<GlassesConnectionStatus, {
30
+ state: "connected";
31
+ }>;
32
+ ready: false;
33
+ } | {
34
+ battery: BatteryState;
35
+ connected: true;
36
+ connection: Extract<GlassesConnectionStatus, {
37
+ state: "connected";
38
+ }>;
39
+ device: ConnectedGlassesInfo;
40
+ firmware: FirmwareInfo;
41
+ hotspot: HotspotStatus;
42
+ ready: boolean;
43
+ signal: SignalState;
44
+ wifi: WifiStatus;
45
+ };
46
+ export type GalleryModeState = {
47
+ applying: boolean;
48
+ desired: GalleryMode;
49
+ error: unknown | null;
50
+ };
51
+ export type PhoneSdkRuntimeState = {
52
+ currentMic: MicMode | null;
53
+ defaultDevice: Device | null;
54
+ galleryMode: GalleryModeState;
55
+ lastLog: string[];
56
+ micRanking: MicMode[];
57
+ otherBluetoothConnected: boolean;
58
+ searching: boolean;
59
+ searchingController: boolean;
60
+ systemMicUnavailable: boolean;
61
+ wifiScanResults: WifiSearchResult[];
62
+ };
63
+ export type ScanController = {
64
+ active: boolean;
65
+ clear: () => void;
66
+ devices: Device[];
67
+ error: unknown | null;
68
+ model: DeviceModel;
69
+ selectedDevice: Device | null;
70
+ selectDevice: (device: Device | null) => void;
71
+ setModel: (model: DeviceModel) => void;
72
+ start: (model?: DeviceModel) => Promise<Device[]>;
73
+ stop: () => Promise<void>;
74
+ };
75
+ export type UseMentraBluetoothOptions = {
76
+ autoConnectDefault?: boolean;
77
+ defaultDeviceStorage?: DefaultDeviceStorage;
78
+ defaultModel?: DeviceModel;
79
+ onError?: (error: unknown) => void;
80
+ scanTimeoutMs?: number;
81
+ };
82
+ export type MentraBluetoothSession = {
83
+ busy: boolean;
84
+ clearDefaultDevice: () => Promise<void>;
85
+ connect: (device?: Device, options?: ConnectOptions) => Promise<void>;
86
+ connectDefault: (options?: ConnectOptions) => Promise<void>;
87
+ defaultDevice: Device | null;
88
+ disconnect: () => Promise<void>;
89
+ error: unknown | null;
90
+ glasses: GlassesRuntimeState;
91
+ refresh: () => Promise<void>;
92
+ scan: ScanController;
93
+ sdk: PhoneSdkRuntimeState;
94
+ setDefaultDevice: (device: Device | null) => Promise<void>;
95
+ setGalleryMode: (mode: GalleryMode) => Promise<void>;
96
+ };
97
+ export declare function useMentraBluetooth(options?: UseMentraBluetoothOptions): MentraBluetoothSession;
98
+ //# sourceMappingURL=useMentraBluetooth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMentraBluetooth.d.ts","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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"}