Skip to content

Commit 5943a10

Browse files
Handle bluetooth connecting, reconnecting, success, and failures (#18)
1 parent 802f5ea commit 5943a10

File tree

6 files changed

+52
-9
lines changed

6 files changed

+52
-9
lines changed

lib/bluetooth-device-wrapper.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ class ServiceInfo<T extends Service> {
9494
}
9595
}
9696

97+
interface ConnectCallbacks {
98+
onConnecting: () => void;
99+
onReconnecting: () => void;
100+
onFail: () => void;
101+
onSuccess: () => void;
102+
}
103+
97104
export class BluetoothDeviceWrapper {
98105
// Used to avoid automatic reconnection during user triggered connect/disconnect
99106
// or reconnection itself.
@@ -136,6 +143,7 @@ export class BluetoothDeviceWrapper {
136143
private dispatchTypedEvent: TypedServiceEventDispatcher,
137144
// We recreate this for the same connection and need to re-setup notifications for the old events
138145
private events: Record<keyof ServiceConnectionEventMap, boolean>,
146+
private callbacks: ConnectCallbacks,
139147
) {
140148
device.addEventListener(
141149
"gattserverdisconnected",
@@ -158,6 +166,11 @@ export class BluetoothDeviceWrapper {
158166
await this.gattConnectPromise;
159167
return;
160168
}
169+
if (this.isReconnect) {
170+
this.callbacks.onReconnecting();
171+
} else {
172+
this.callbacks.onConnecting();
173+
}
161174
this.duringExplicitConnectDisconnect++;
162175
if (this.device.gatt === undefined) {
163176
throw new Error(
@@ -233,16 +246,20 @@ export class BluetoothDeviceWrapper {
233246
type: this.isReconnect ? "Reconnect" : "Connect",
234247
message: "Bluetooth connect success",
235248
});
249+
this.callbacks.onSuccess();
236250
} catch (e) {
237251
this.logging.error("Bluetooth connect error", e);
238252
this.logging.event({
239253
type: this.isReconnect ? "Reconnect" : "Connect",
240254
message: "Bluetooth connect failed",
241255
});
242256
await this.disconnectInternal(false);
257+
this.callbacks.onFail();
243258
throw new Error("Failed to establish a connection!");
244259
} finally {
245260
this.duringExplicitConnectDisconnect--;
261+
// Reset isReconnect for next time
262+
this.isReconnect = false;
246263
}
247264
}
248265

@@ -433,6 +450,7 @@ export const createBluetoothDeviceWrapper = async (
433450
logging: Logging,
434451
dispatchTypedEvent: TypedServiceEventDispatcher,
435452
addedServiceListeners: Record<keyof ServiceConnectionEventMap, boolean>,
453+
callbacks: ConnectCallbacks,
436454
): Promise<BluetoothDeviceWrapper | undefined> => {
437455
try {
438456
// Reuse our connection objects for the same device as they
@@ -444,6 +462,7 @@ export const createBluetoothDeviceWrapper = async (
444462
logging,
445463
dispatchTypedEvent,
446464
addedServiceListeners,
465+
callbacks,
447466
);
448467
deviceIdToWrapper.set(device.id, bluetooth);
449468
await bluetooth.connect();

lib/bluetooth.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class MicrobitWebBluetoothConnection
122122
});
123123
} finally {
124124
this.connection = undefined;
125-
this.setStatus(ConnectionStatus.NOT_CONNECTED);
125+
this.setStatus(ConnectionStatus.DISCONNECTED);
126126
this.logging.log("Disconnection complete");
127127
this.logging.event({
128128
type: "Bluetooth-info",
@@ -154,14 +154,25 @@ export class MicrobitWebBluetoothConnection
154154
if (!this.connection) {
155155
const device = await this.chooseDevice();
156156
if (!device) {
157+
this.setStatus(ConnectionStatus.NO_AUTHORIZED_DEVICE);
157158
return;
158159
}
159160
this.connection = await createBluetoothDeviceWrapper(
160161
device,
161162
this.logging,
162163
this.dispatchTypedEvent.bind(this),
163164
this.activeEvents,
165+
{
166+
onConnecting: () => this.setStatus(ConnectionStatus.CONNECTING),
167+
onReconnecting: () => this.setStatus(ConnectionStatus.RECONNECTING),
168+
onSuccess: () => this.setStatus(ConnectionStatus.CONNECTED),
169+
onFail: () => {
170+
this.setStatus(ConnectionStatus.DISCONNECTED);
171+
this.connection = undefined;
172+
},
173+
},
164174
);
175+
return;
165176
}
166177
// TODO: timeout unification?
167178
// Connection happens inside createBluetoothDeviceWrapper.

lib/device.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,27 @@ export enum ConnectionStatus {
8181
* but has not been connected via the browser security UI.
8282
*/
8383
NO_AUTHORIZED_DEVICE = "NO_AUTHORIZED_DEVICE",
84+
/**
85+
* Disconnecting.
86+
*/
87+
DISCONNECTING = "DISCONNECTING",
8488
/**
8589
* Authorized device available but we haven't connected to it.
8690
*/
87-
NOT_CONNECTED = "NOT_CONNECTED",
91+
DISCONNECTED = "DISCONNECTED",
8892
/**
8993
* Connected.
9094
*/
9195
CONNECTED = "CONNECTED",
96+
/**
97+
* Connecting.
98+
*/
99+
CONNECTING = "CONNECTING",
100+
/**
101+
* Reconnecting. When there is unexpected disruption in the connection,
102+
* a reconnection is attempted.
103+
*/
104+
RECONNECTING = "RECONNECTING",
92105
}
93106

94107
export class FlashDataError extends Error {}

lib/usb-radio-bridge.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ export class MicrobitRadioBridgeConnection
5454
this.setStatus(e.status);
5555
this.serialSession?.dispose();
5656
} else {
57-
this.status = ConnectionStatus.NOT_CONNECTED;
57+
this.status = ConnectionStatus.DISCONNECTED;
5858
if (
59-
currentStatus === ConnectionStatus.NOT_CONNECTED &&
59+
currentStatus === ConnectionStatus.DISCONNECTED &&
6060
this.serialSessionOpen
6161
) {
6262
this.serialSession?.connect();
@@ -181,7 +181,7 @@ export class MicrobitRadioBridgeConnection
181181

182182
private statusFromDelegate(): ConnectionStatus {
183183
return this.delegate.status == ConnectionStatus.CONNECTED
184-
? ConnectionStatus.NOT_CONNECTED
184+
? ConnectionStatus.DISCONNECTED
185185
: this.delegate.status;
186186
}
187187
}
@@ -336,7 +336,7 @@ class RadioBridgeSerialSession {
336336
this.delegate.removeEventListener("serialerror", this.serialErrorListener);
337337
await this.delegate.softwareReset();
338338

339-
this.onStatusChanged(ConnectionStatus.NOT_CONNECTED);
339+
this.onStatusChanged(ConnectionStatus.DISCONNECTED);
340340
}
341341

342342
private async sendCmdWaitResponse(

lib/usb.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ describeDeviceOnly("MicrobitWebUSBConnection (WebUSB supported)", () => {
7070
await connection.disconnect();
7171
connection.dispose();
7272

73-
expect(connection.status).toEqual(ConnectionStatus.NOT_CONNECTED);
73+
expect(connection.status).toEqual(ConnectionStatus.DISCONNECTED);
7474
expect(events).toEqual([
7575
ConnectionStatus.CONNECTED,
76-
ConnectionStatus.NOT_CONNECTED,
76+
ConnectionStatus.DISCONNECTED,
7777
]);
7878
});
7979
});

lib/usb.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ export class MicrobitWebUSBConnection
339339
});
340340
} finally {
341341
this.connection = undefined;
342-
this.setStatus(ConnectionStatus.NOT_CONNECTED);
342+
this.setStatus(ConnectionStatus.DISCONNECTED);
343343
this.logging.log("Disconnection complete");
344344
this.logging.event({
345345
type: "WebUSB-info",

0 commit comments

Comments
 (0)