Skip to content

Commit 272eaf0

Browse files
authored
Merge pull request #224 from GorillaDevq/feat/useBluetooth
[feat]: useBluetooth
2 parents dabe072 + 3078161 commit 272eaf0

File tree

5 files changed

+148
-2
lines changed

5 files changed

+148
-2
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"@types/node": "^22.13.1",
8282
"@types/react": "^18.3.12",
8383
"@types/react-dom": "^18.3.2",
84+
"@types/web-bluetooth": "^0.0.20",
8485
"comment-parser": "^1.4.1",
8586
"core-js": "^3.40.0",
8687
"doctrine": "^3.0.0",

src/hooks/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,4 @@ export * from './useWindowEvent/useWindowEvent';
110110
export * from './useWindowFocus/useWindowFocus';
111111
export * from './useWindowScroll/useWindowScroll';
112112
export * from './useWindowSize/useWindowSize';
113-
export * from './useWizard/useWizard';
113+
export * from './useWizard/useWizard';
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useBluetooth } from './useBluetooth';
2+
3+
const Demo = () => {
4+
const { isSupported, requestDevice, error } = useBluetooth({
5+
acceptAllDevices: true
6+
});
7+
8+
if (!isSupported) {
9+
return (
10+
<p>
11+
Bluetooth Web API:
12+
<code>not supported</code>
13+
</p>
14+
);
15+
}
16+
17+
return (
18+
<>
19+
<p>
20+
Bluetooth Web API: <code>supported</code>
21+
</p>
22+
<button onClick={requestDevice}>Click</button>
23+
<p>{error && `Error: ${error}`}</p>
24+
</>
25+
);
26+
};
27+
28+
export default Demo;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { useCallback, useEffect, useState } from 'react';
2+
3+
/** State for hook UseBluetooth */
4+
export interface UseBluetoothReturn {
5+
/** Indicates if Bluetooth API is supported by the browser */
6+
isSupported: boolean;
7+
/** Indicates if Bluetooth device is currently connected */
8+
isConnected: boolean;
9+
/** Describe connected Bluetooth device */
10+
device: BluetoothDevice | undefined;
11+
/** Function to request Bluetooth device from the user */
12+
requestDevice: () => Promise<void>;
13+
/** The GATT server for connected Bluetooth device */
14+
server: BluetoothRemoteGATTServer | undefined;
15+
/** Any error that may have occurred */
16+
error: string | null;
17+
}
18+
19+
/** Params for useBluetooth hook */
20+
export interface UseBluetoothOptions {
21+
/** If true, hook will request all available Bluetooth devices */
22+
acceptAllDevices?: boolean;
23+
/** Array of filters to apply when scanning Bluetooth devices */
24+
filters?: BluetoothLEScanFilter[] | undefined;
25+
/** Array of optional services that the application can use */
26+
optionalServices?: BluetoothServiceUUID[] | undefined;
27+
}
28+
29+
/**
30+
* @name useBluetooth
31+
* @description - Hook for getting information about bluetooth
32+
* @category Browser
33+
*
34+
* @param {boolean} [options.acceptAllDevices=false] The options to request all Bluetooth devices
35+
* @param {Array<keyof BluetoothLEScanFilter>} [options.filters=undefined] Array of filters to apply when scanning Bluetooth devices
36+
* @param {Array<keyof BluetoothServiceUUID>} [options.optionalServices=undefined] Array of optional services that the application can use
37+
* @returns {UseBluetoothReturn} Object containing battery information & Battery API support
38+
*
39+
* @example
40+
* const { isSupported, requestDevice } = useBluetooth({ acceptAllDevices: true });
41+
*/
42+
43+
export const useBluetooth = (options?: UseBluetoothOptions): UseBluetoothReturn => {
44+
const {
45+
acceptAllDevices = false,
46+
filters = undefined,
47+
optionalServices = undefined
48+
} = options || {};
49+
50+
const [isSupported, setIsSupported] = useState(false);
51+
const [isConnected, setIsConnected] = useState(false);
52+
const [device, setDevice] = useState<BluetoothDevice | undefined>(undefined);
53+
const [server, setServer] = useState<BluetoothRemoteGATTServer | undefined>(undefined);
54+
const [error, setError] = useState<string | null>(null);
55+
56+
useEffect(() => {
57+
setIsSupported(navigator && 'bluetooth' in navigator);
58+
}, [navigator]);
59+
60+
const requestDevice = useCallback(async () => {
61+
if (!isSupported || !navigator?.bluetooth) {
62+
return;
63+
}
64+
65+
setError(null);
66+
67+
try {
68+
const selectedDevice = await navigator.bluetooth.requestDevice({
69+
acceptAllDevices,
70+
filters,
71+
optionalServices
72+
});
73+
74+
setDevice(selectedDevice);
75+
} catch (err) {
76+
if (err instanceof Error) setError(err.message);
77+
else setError('Unknown error');
78+
}
79+
}, [acceptAllDevices, filters, optionalServices, isSupported, navigator]);
80+
81+
// Connect to the GATT server
82+
useEffect(() => {
83+
const connectToBluetoothGATTServer = async () => {
84+
if (device?.gatt) {
85+
setError(null);
86+
87+
try {
88+
const gattServer = await device.gatt.connect();
89+
setServer(gattServer);
90+
setIsConnected(gattServer.connected);
91+
} catch (err) {
92+
if (err instanceof Error) setError(err.message);
93+
else setError('Unknown error');
94+
}
95+
}
96+
};
97+
98+
if (device) {
99+
device.addEventListener('gattserverdisconnected', () => setIsConnected(false));
100+
connectToBluetoothGATTServer();
101+
102+
return () => {
103+
device.removeEventListener('gattserverdisconnected', () => setIsConnected(false));
104+
server?.disconnect();
105+
};
106+
}
107+
}, [device]);
108+
109+
return {
110+
isSupported,
111+
isConnected,
112+
device,
113+
requestDevice,
114+
server,
115+
error
116+
};
117+
};

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"@/tests": ["./tests"]
1212
},
1313
"resolveJsonModule": true,
14-
"types": ["vitest/globals"],
14+
"types": ["vitest/globals", "@types/web-bluetooth"],
1515
"allowJs": true,
1616
"strict": true,
1717
"noFallthroughCasesInSwitch": true,

0 commit comments

Comments
 (0)