mirror of
https://github.com/usatiuk/door-thing.git
synced 2025-10-26 19:17:49 +01:00
143 lines
4.2 KiB
TypeScript
143 lines
4.2 KiB
TypeScript
import { useEffect, useState } from "preact/hooks";
|
|
|
|
const doorServiceUUIDString = "0x1811";
|
|
const doorServiceUUID: BluetoothServiceUUID = 0x1811;
|
|
const doorSwitchUUID: BluetoothCharacteristicUUID = 0x2ae2;
|
|
|
|
export function App() {
|
|
const [connecting, setConnecting] = useState(false);
|
|
const [device, setDevice] = useState<{
|
|
device: BluetoothDevice;
|
|
service: BluetoothRemoteGATTService;
|
|
characteristic: BluetoothRemoteGATTCharacteristic;
|
|
} | null>(null);
|
|
const [doorOpen, setDoorOpen] = useState<boolean | null>(null);
|
|
const [autoConnect, setAutoConnect] = useState(false);
|
|
|
|
useEffect(() => {
|
|
window.api.send("read-setting", "autoConnect");
|
|
window.api.receive(
|
|
"read-setting-success",
|
|
(res: { key: string; value: any }) => {
|
|
if (res.key == "autoConnect") {
|
|
setAutoConnect(res.value);
|
|
}
|
|
},
|
|
);
|
|
window.api.receive(
|
|
"user-gesture-reply",
|
|
async (res: { type: string; promise: Promise<any> }) => {
|
|
if (res.type == "find-device") {
|
|
try {
|
|
const foundDevice = await window.newDevice;
|
|
console.log("Found device:");
|
|
console.log(foundDevice);
|
|
|
|
await foundDevice.gatt.connect();
|
|
foundDevice.ongattserverdisconnected = onDisconnect;
|
|
|
|
const doorService = await foundDevice.gatt.getPrimaryService(
|
|
doorServiceUUID,
|
|
);
|
|
const doorSwitch = await doorService.getCharacteristic(
|
|
doorSwitchUUID,
|
|
);
|
|
setDevice({
|
|
device: foundDevice,
|
|
service: doorService,
|
|
characteristic: doorSwitch,
|
|
});
|
|
|
|
const data = await (await doorSwitch.readValue()).getUint8(0);
|
|
setDoorOpen(Boolean(data));
|
|
|
|
doorSwitch.oncharacteristicvaluechanged = onDoorUpdate;
|
|
doorSwitch.startNotifications();
|
|
} catch (error) {
|
|
console.log("Error connecting:");
|
|
console.log(error);
|
|
} finally {
|
|
setConnecting(false);
|
|
window.newDevice = null;
|
|
}
|
|
}
|
|
},
|
|
);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (autoConnect && !device && !connecting) {
|
|
tryConnect();
|
|
}
|
|
}, [autoConnect, device, connecting]);
|
|
|
|
function onAutoConnectChange(e: Event) {
|
|
const newVal = (e.currentTarget as any).checked;
|
|
setAutoConnect(newVal);
|
|
window.api.send("write-setting", { key: "autoConnect", value: newVal });
|
|
}
|
|
|
|
const tryConnect = async () => {
|
|
setConnecting(true);
|
|
//we actually need all this, because otherwise it gives "Must be handling a user gesture..." error
|
|
//and there doesn't seem to be a way to circumvent this yet
|
|
//todo: make this at least somewhat less ugly?
|
|
//similar issue: https://github.com/electron/electron/issues/27625
|
|
window.api.send("exec-user-gesture", {
|
|
type: "find-device",
|
|
function: `
|
|
window.newDevice = navigator.bluetooth.requestDevice({
|
|
filters: [
|
|
{
|
|
namePrefix: "Nano",
|
|
},
|
|
],
|
|
optionalServices: [${doorServiceUUIDString}],
|
|
})`,
|
|
});
|
|
};
|
|
|
|
const onDoorUpdate = async (e: Event) => {
|
|
const characteristic = e.target as BluetoothRemoteGATTCharacteristic;
|
|
const parsed = Boolean(await characteristic.value.getUint8(0));
|
|
setDoorOpen(parsed);
|
|
new Notification("Door update", { body: parsed ? "Open" : "Closed" });
|
|
};
|
|
|
|
const onDisconnect = async () => {
|
|
setDevice(null);
|
|
setDoorOpen(null);
|
|
};
|
|
|
|
const onDisconnectClick = async () => {
|
|
try {
|
|
await device.characteristic.stopNotifications();
|
|
device.device.gatt.disconnect();
|
|
} catch (error) {
|
|
console.log("Error disconnecting:");
|
|
console.log(error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<input
|
|
onChange={onAutoConnectChange}
|
|
checked={autoConnect}
|
|
type="checkbox"
|
|
/>
|
|
{connecting && !device && <span>Trying to connect</span>}
|
|
{device == null && !connecting && (
|
|
<button onClick={tryConnect}>Connect</button>
|
|
)}
|
|
{device && (
|
|
<>
|
|
<span>connected</span>
|
|
<button onClick={onDisconnectClick}>Disconnect</button>
|
|
{doorOpen != null && <span>{doorOpen ? "open" : "closed"}</span>}
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|