@microbit/microbit-connection 0.0.0-alpha.3 → 0.0.0-alpha.31

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 (70) hide show
  1. package/build/accelerometer-service.d.ts +8 -9
  2. package/build/accelerometer-service.js +24 -43
  3. package/build/accelerometer-service.js.map +1 -1
  4. package/build/bluetooth-device-wrapper.d.ts +30 -26
  5. package/build/bluetooth-device-wrapper.js +147 -89
  6. package/build/bluetooth-device-wrapper.js.map +1 -1
  7. package/build/bluetooth.d.ts +15 -11
  8. package/build/bluetooth.js +74 -91
  9. package/build/bluetooth.js.map +1 -1
  10. package/build/board-id.d.ts +1 -0
  11. package/build/board-id.js +8 -0
  12. package/build/board-id.js.map +1 -1
  13. package/build/button-service.d.ts +13 -0
  14. package/build/button-service.js +76 -0
  15. package/build/button-service.js.map +1 -0
  16. package/build/buttons.d.ts +10 -0
  17. package/build/buttons.js +18 -0
  18. package/build/buttons.js.map +1 -0
  19. package/build/constants.d.ts +1 -0
  20. package/build/constants.js +1 -0
  21. package/build/constants.js.map +1 -1
  22. package/build/device.d.ts +32 -39
  23. package/build/device.js +18 -3
  24. package/build/device.js.map +1 -1
  25. package/build/events.d.ts +11 -1
  26. package/build/events.js +86 -1
  27. package/build/events.js.map +1 -1
  28. package/build/hex-flash-data-source.d.ts +6 -8
  29. package/build/hex-flash-data-source.js +16 -45
  30. package/build/hex-flash-data-source.js.map +1 -1
  31. package/build/index.d.ts +10 -5
  32. package/build/index.js +7 -4
  33. package/build/index.js.map +1 -1
  34. package/build/led-service.d.ts +20 -0
  35. package/build/led-service.js +116 -0
  36. package/build/led-service.js.map +1 -0
  37. package/build/led.d.ts +6 -0
  38. package/build/led.js +2 -0
  39. package/build/led.js.map +1 -0
  40. package/build/logging.js +3 -1
  41. package/build/logging.js.map +1 -1
  42. package/build/promise-queue.d.ts +27 -0
  43. package/build/promise-queue.js +74 -0
  44. package/build/promise-queue.js.map +1 -0
  45. package/build/service-events.d.ts +5 -0
  46. package/build/service-events.js +18 -0
  47. package/build/service-events.js.map +1 -1
  48. package/build/uart-service.d.ts +13 -0
  49. package/build/uart-service.js +72 -0
  50. package/build/uart-service.js.map +1 -0
  51. package/build/uart.d.ts +4 -0
  52. package/build/uart.js +12 -0
  53. package/build/uart.js.map +1 -0
  54. package/build/usb-device-wrapper.d.ts +7 -1
  55. package/build/usb-device-wrapper.js +40 -3
  56. package/build/usb-device-wrapper.js.map +1 -1
  57. package/build/usb-partial-flashing.d.ts +11 -5
  58. package/build/usb-partial-flashing.js +53 -10
  59. package/build/usb-partial-flashing.js.map +1 -1
  60. package/build/usb-radio-bridge.d.ts +31 -18
  61. package/build/usb-radio-bridge.js +351 -187
  62. package/build/usb-radio-bridge.js.map +1 -1
  63. package/build/usb.d.ts +14 -17
  64. package/build/usb.js +111 -47
  65. package/build/usb.js.map +1 -1
  66. package/package.json +5 -3
  67. package/vite.config.ts +9 -7
  68. package/build/bluetooth-utils.d.ts +0 -5
  69. package/build/bluetooth-utils.js +0 -14
  70. package/build/bluetooth-utils.js.map +0 -1
@@ -1,19 +1,18 @@
1
1
  import { AccelerometerData } from "./accelerometer.js";
2
- import { GattOperation } from "./bluetooth-device-wrapper.js";
3
- import { TypedServiceEventDispatcher } from "./service-events.js";
4
- export declare class AccelerometerService {
2
+ import { Service } from "./bluetooth-device-wrapper.js";
3
+ import { TypedServiceEvent, TypedServiceEventDispatcher } from "./service-events.js";
4
+ export declare class AccelerometerService implements Service {
5
5
  private accelerometerDataCharacteristic;
6
6
  private accelerometerPeriodCharacteristic;
7
7
  private dispatchTypedEvent;
8
- private isNotifying;
9
8
  private queueGattOperation;
10
- constructor(accelerometerDataCharacteristic: BluetoothRemoteGATTCharacteristic, accelerometerPeriodCharacteristic: BluetoothRemoteGATTCharacteristic, dispatchTypedEvent: TypedServiceEventDispatcher, isNotifying: boolean, queueGattOperation: (gattOperation: GattOperation) => void);
11
- static createService(gattServer: BluetoothRemoteGATTServer, dispatcher: TypedServiceEventDispatcher, isNotifying: boolean, queueGattOperation: (gattOperation: GattOperation) => void, listenerInit: boolean): Promise<AccelerometerService | undefined>;
9
+ constructor(accelerometerDataCharacteristic: BluetoothRemoteGATTCharacteristic, accelerometerPeriodCharacteristic: BluetoothRemoteGATTCharacteristic, dispatchTypedEvent: TypedServiceEventDispatcher, queueGattOperation: <R>(action: () => Promise<R>) => Promise<R>);
10
+ static createService(gattServer: BluetoothRemoteGATTServer, dispatcher: TypedServiceEventDispatcher, queueGattOperation: <R>(action: () => Promise<R>) => Promise<R>, listenerInit: boolean): Promise<AccelerometerService | undefined>;
12
11
  private dataViewToData;
13
12
  getData(): Promise<AccelerometerData>;
14
13
  getPeriod(): Promise<number>;
15
14
  setPeriod(value: number): Promise<void>;
16
- private addDataEventListener;
17
- startNotifications(): void;
18
- stopNotifications(): void;
15
+ startNotifications(type: TypedServiceEvent): Promise<void>;
16
+ stopNotifications(type: TypedServiceEvent): Promise<void>;
17
+ private characteristicForEvent;
19
18
  }
@@ -1,9 +1,8 @@
1
1
  import { AccelerometerDataEvent } from "./accelerometer.js";
2
2
  import { profile } from "./bluetooth-profile.js";
3
- import { createGattOperationPromise } from "./bluetooth-utils.js";
4
3
  import { BackgroundErrorEvent, DeviceError } from "./device.js";
5
4
  export class AccelerometerService {
6
- constructor(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatchTypedEvent, isNotifying, queueGattOperation) {
5
+ constructor(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatchTypedEvent, queueGattOperation) {
7
6
  Object.defineProperty(this, "accelerometerDataCharacteristic", {
8
7
  enumerable: true,
9
8
  configurable: true,
@@ -22,24 +21,19 @@ export class AccelerometerService {
22
21
  writable: true,
23
22
  value: dispatchTypedEvent
24
23
  });
25
- Object.defineProperty(this, "isNotifying", {
26
- enumerable: true,
27
- configurable: true,
28
- writable: true,
29
- value: isNotifying
30
- });
31
24
  Object.defineProperty(this, "queueGattOperation", {
32
25
  enumerable: true,
33
26
  configurable: true,
34
27
  writable: true,
35
28
  value: queueGattOperation
36
29
  });
37
- this.addDataEventListener();
38
- if (this.isNotifying) {
39
- this.startNotifications();
40
- }
30
+ this.accelerometerDataCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
31
+ const target = event.target;
32
+ const data = this.dataViewToData(target.value);
33
+ this.dispatchTypedEvent("accelerometerdatachanged", new AccelerometerDataEvent(data));
34
+ });
41
35
  }
42
- static async createService(gattServer, dispatcher, isNotifying, queueGattOperation, listenerInit) {
36
+ static async createService(gattServer, dispatcher, queueGattOperation, listenerInit) {
43
37
  let accelerometerService;
44
38
  try {
45
39
  accelerometerService = await gattServer.getPrimaryService(profile.accelerometer.id);
@@ -58,7 +52,7 @@ export class AccelerometerService {
58
52
  }
59
53
  const accelerometerDataCharacteristic = await accelerometerService.getCharacteristic(profile.accelerometer.characteristics.data.id);
60
54
  const accelerometerPeriodCharacteristic = await accelerometerService.getCharacteristic(profile.accelerometer.characteristics.period.id);
61
- return new AccelerometerService(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatcher, isNotifying, queueGattOperation);
55
+ return new AccelerometerService(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatcher, queueGattOperation);
62
56
  }
63
57
  dataViewToData(dataView) {
64
58
  return {
@@ -68,21 +62,11 @@ export class AccelerometerService {
68
62
  };
69
63
  }
70
64
  async getData() {
71
- const { callback, gattOperationPromise } = createGattOperationPromise();
72
- this.queueGattOperation({
73
- callback,
74
- operation: () => this.accelerometerDataCharacteristic.readValue(),
75
- });
76
- const dataView = (await gattOperationPromise);
65
+ const dataView = await this.queueGattOperation(() => this.accelerometerDataCharacteristic.readValue());
77
66
  return this.dataViewToData(dataView);
78
67
  }
79
68
  async getPeriod() {
80
- const { callback, gattOperationPromise } = createGattOperationPromise();
81
- this.queueGattOperation({
82
- callback,
83
- operation: () => this.accelerometerPeriodCharacteristic.readValue(),
84
- });
85
- const dataView = (await gattOperationPromise);
69
+ const dataView = await this.queueGattOperation(() => this.accelerometerPeriodCharacteristic.readValue());
86
70
  return dataView.getUint16(0, true);
87
71
  }
88
72
  async setPeriod(value) {
@@ -94,28 +78,25 @@ export class AccelerometerService {
94
78
  // Values passed are rounded up to the allowed values on device.
95
79
  // Documentation for allowed values looks wrong.
96
80
  // https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html
97
- const { callback } = createGattOperationPromise();
98
81
  const dataView = new DataView(new ArrayBuffer(2));
99
82
  dataView.setUint16(0, value, true);
100
- this.queueGattOperation({
101
- callback,
102
- operation: () => this.accelerometerPeriodCharacteristic.writeValueWithoutResponse(dataView),
103
- });
83
+ return this.queueGattOperation(() => this.accelerometerPeriodCharacteristic.writeValue(dataView));
104
84
  }
105
- addDataEventListener() {
106
- this.accelerometerDataCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
107
- const target = event.target;
108
- const data = this.dataViewToData(target.value);
109
- this.dispatchTypedEvent("accelerometerdatachanged", new AccelerometerDataEvent(data));
110
- });
85
+ async startNotifications(type) {
86
+ await this.characteristicForEvent(type)?.startNotifications();
111
87
  }
112
- startNotifications() {
113
- this.accelerometerDataCharacteristic.startNotifications();
114
- this.isNotifying = true;
88
+ async stopNotifications(type) {
89
+ await this.characteristicForEvent(type)?.stopNotifications();
115
90
  }
116
- stopNotifications() {
117
- this.isNotifying = false;
118
- this.accelerometerDataCharacteristic.stopNotifications();
91
+ characteristicForEvent(type) {
92
+ switch (type) {
93
+ case "accelerometerdatachanged": {
94
+ return this.accelerometerDataCharacteristic;
95
+ }
96
+ default: {
97
+ return undefined;
98
+ }
99
+ }
119
100
  }
120
101
  }
121
102
  //# sourceMappingURL=accelerometer-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"accelerometer-service.js","sourceRoot":"","sources":["../lib/accelerometer-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"accelerometer-service.js","sourceRoot":"","sources":["../lib/accelerometer-service.ts"],"names":[],"mappings":"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"}
@@ -4,40 +4,42 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  import { AccelerometerService } from "./accelerometer-service.js";
7
- import { BoardVersion, DeviceError } from "./device.js";
7
+ import { BoardVersion } from "./device.js";
8
+ import { LedService } from "./led-service.js";
8
9
  import { Logging } from "./logging.js";
9
- import { ServiceConnectionEventMap, TypedServiceEventDispatcher } from "./service-events.js";
10
- export interface GattOperationCallback {
11
- resolve: (result: DataView | void) => void;
12
- reject: (error: DeviceError) => void;
10
+ import { ServiceConnectionEventMap, TypedServiceEvent, TypedServiceEventDispatcher } from "./service-events.js";
11
+ import { UARTService } from "./uart-service.js";
12
+ export interface Service {
13
+ startNotifications(type: TypedServiceEvent): Promise<void>;
14
+ stopNotifications(type: TypedServiceEvent): Promise<void>;
13
15
  }
14
- export interface GattOperation {
15
- operation: () => Promise<DataView | void>;
16
- callback: GattOperationCallback;
16
+ interface ConnectCallbacks {
17
+ onConnecting: () => void;
18
+ onReconnecting: () => void;
19
+ onFail: () => void;
20
+ onSuccess: () => void;
17
21
  }
18
22
  export declare class BluetoothDeviceWrapper {
19
23
  readonly device: BluetoothDevice;
20
24
  private logging;
21
25
  private dispatchTypedEvent;
22
- private addedServiceListeners;
26
+ private currentEvents;
27
+ private callbacks;
23
28
  private duringExplicitConnectDisconnect;
24
29
  private gattConnectPromise;
25
30
  private disconnectPromise;
26
31
  private connecting;
27
32
  private isReconnect;
28
- private reconnectReadyPromise;
29
- private accelerometerService;
33
+ private connectReadyPromise;
34
+ private accelerometer;
35
+ private buttons;
36
+ private led;
37
+ private uart;
38
+ private serviceInfo;
30
39
  boardVersion: BoardVersion | undefined;
31
- serviceListenerState: {
32
- accelerometerdatachanged: {
33
- notifying: boolean;
34
- service: (options?: {
35
- listenerInit: boolean;
36
- }) => Promise<AccelerometerService | undefined>;
37
- };
38
- };
40
+ private disconnectedRejectionErrorFactory;
39
41
  private gattOperations;
40
- constructor(device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher, addedServiceListeners: Record<keyof ServiceConnectionEventMap, boolean>);
42
+ constructor(device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher, currentEvents: () => Array<keyof ServiceConnectionEventMap>, callbacks: ConnectCallbacks);
41
43
  connect(): Promise<void>;
42
44
  disconnect(): Promise<void>;
43
45
  private disconnectInternal;
@@ -46,11 +48,13 @@ export declare class BluetoothDeviceWrapper {
46
48
  private assertGattServer;
47
49
  private getBoardVersion;
48
50
  private queueGattOperation;
49
- private processGattOperationQueue;
50
- private clearGattQueueOnDisconnect;
51
- getAccelerometerService(options?: {
52
- listenerInit: boolean;
53
- }): Promise<AccelerometerService | undefined>;
51
+ private createIfNeeded;
52
+ getAccelerometerService(): Promise<AccelerometerService | undefined>;
53
+ getLedService(): Promise<LedService | undefined>;
54
+ getUARTService(): Promise<UARTService | undefined>;
55
+ startNotifications(type: TypedServiceEvent): Promise<void>;
56
+ stopNotifications(type: TypedServiceEvent): Promise<void>;
54
57
  private disposeServices;
55
58
  }
56
- export declare const createBluetoothDeviceWrapper: (device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher, addedServiceListeners: Record<keyof ServiceConnectionEventMap, boolean>) => Promise<BluetoothDeviceWrapper | undefined>;
59
+ export declare const createBluetoothDeviceWrapper: (device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher, currentEvents: () => Array<keyof ServiceConnectionEventMap>, callbacks: ConnectCallbacks) => Promise<BluetoothDeviceWrapper | undefined>;
60
+ export {};
@@ -5,8 +5,12 @@
5
5
  */
6
6
  import { AccelerometerService } from "./accelerometer-service.js";
7
7
  import { profile } from "./bluetooth-profile.js";
8
+ import { ButtonService } from "./button-service.js";
8
9
  import { DeviceError } from "./device.js";
10
+ import { LedService } from "./led-service.js";
9
11
  import { NullLogging } from "./logging.js";
12
+ import { PromiseQueue } from "./promise-queue.js";
13
+ import { UARTService } from "./uart-service.js";
10
14
  const deviceIdToWrapper = new Map();
11
15
  const connectTimeoutDuration = 10000;
12
16
  function findPlatform() {
@@ -23,8 +27,42 @@ function findPlatform() {
23
27
  }
24
28
  const platform = findPlatform();
25
29
  const isWindowsOS = platform && /^Win/.test(platform);
30
+ class ServiceInfo {
31
+ constructor(serviceFactory, events) {
32
+ Object.defineProperty(this, "serviceFactory", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: serviceFactory
37
+ });
38
+ Object.defineProperty(this, "events", {
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true,
42
+ value: events
43
+ });
44
+ Object.defineProperty(this, "service", {
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ value: void 0
49
+ });
50
+ }
51
+ get() {
52
+ return this.service;
53
+ }
54
+ async createIfNeeded(gattServer, dispatcher, queueGattOperation, listenerInit) {
55
+ this.service =
56
+ this.service ??
57
+ (await this.serviceFactory(gattServer, dispatcher, queueGattOperation, listenerInit));
58
+ return this.service;
59
+ }
60
+ dispose() {
61
+ this.service = undefined;
62
+ }
63
+ }
26
64
  export class BluetoothDeviceWrapper {
27
- constructor(device, logging = new NullLogging(), dispatchTypedEvent, addedServiceListeners) {
65
+ constructor(device, logging = new NullLogging(), dispatchTypedEvent, currentEvents, callbacks) {
28
66
  Object.defineProperty(this, "device", {
29
67
  enumerable: true,
30
68
  configurable: true,
@@ -43,11 +81,17 @@ export class BluetoothDeviceWrapper {
43
81
  writable: true,
44
82
  value: dispatchTypedEvent
45
83
  });
46
- Object.defineProperty(this, "addedServiceListeners", {
84
+ Object.defineProperty(this, "currentEvents", {
47
85
  enumerable: true,
48
86
  configurable: true,
49
87
  writable: true,
50
- value: addedServiceListeners
88
+ value: currentEvents
89
+ });
90
+ Object.defineProperty(this, "callbacks", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: callbacks
51
95
  });
52
96
  // Used to avoid automatic reconnection during user triggered connect/disconnect
53
97
  // or reconnection itself.
@@ -90,17 +134,46 @@ export class BluetoothDeviceWrapper {
90
134
  writable: true,
91
135
  value: false
92
136
  });
93
- Object.defineProperty(this, "reconnectReadyPromise", {
137
+ Object.defineProperty(this, "connectReadyPromise", {
94
138
  enumerable: true,
95
139
  configurable: true,
96
140
  writable: true,
97
141
  value: void 0
98
142
  });
99
- Object.defineProperty(this, "accelerometerService", {
143
+ Object.defineProperty(this, "accelerometer", {
100
144
  enumerable: true,
101
145
  configurable: true,
102
146
  writable: true,
103
- value: void 0
147
+ value: new ServiceInfo(AccelerometerService.createService, [
148
+ "accelerometerdatachanged",
149
+ ])
150
+ });
151
+ Object.defineProperty(this, "buttons", {
152
+ enumerable: true,
153
+ configurable: true,
154
+ writable: true,
155
+ value: new ServiceInfo(ButtonService.createService, [
156
+ "buttonachanged",
157
+ "buttonbchanged",
158
+ ])
159
+ });
160
+ Object.defineProperty(this, "led", {
161
+ enumerable: true,
162
+ configurable: true,
163
+ writable: true,
164
+ value: new ServiceInfo(LedService.createService, [])
165
+ });
166
+ Object.defineProperty(this, "uart", {
167
+ enumerable: true,
168
+ configurable: true,
169
+ writable: true,
170
+ value: new ServiceInfo(UARTService.createService, ["uartdata"])
171
+ });
172
+ Object.defineProperty(this, "serviceInfo", {
173
+ enumerable: true,
174
+ configurable: true,
175
+ writable: true,
176
+ value: [this.accelerometer, this.buttons, this.led, this.uart]
104
177
  });
105
178
  Object.defineProperty(this, "boardVersion", {
106
179
  enumerable: true,
@@ -108,25 +181,29 @@ export class BluetoothDeviceWrapper {
108
181
  writable: true,
109
182
  value: void 0
110
183
  });
111
- Object.defineProperty(this, "serviceListenerState", {
184
+ Object.defineProperty(this, "disconnectedRejectionErrorFactory", {
112
185
  enumerable: true,
113
186
  configurable: true,
114
187
  writable: true,
115
- value: {
116
- accelerometerdatachanged: {
117
- notifying: false,
118
- service: this.getAccelerometerService,
119
- },
188
+ value: () => {
189
+ return new DeviceError({
190
+ code: "device-disconnected",
191
+ message: "Error processing gatt operations queue - device disconnected",
192
+ });
120
193
  }
121
194
  });
122
195
  Object.defineProperty(this, "gattOperations", {
123
196
  enumerable: true,
124
197
  configurable: true,
125
198
  writable: true,
126
- value: {
127
- busy: false,
128
- queue: [],
129
- }
199
+ value: new PromiseQueue({
200
+ abortCheck: () => {
201
+ if (!this.device.gatt?.connected) {
202
+ return this.disconnectedRejectionErrorFactory;
203
+ }
204
+ return undefined;
205
+ },
206
+ })
130
207
  });
131
208
  Object.defineProperty(this, "handleDisconnectEvent", {
132
209
  enumerable: true,
@@ -149,45 +226,7 @@ export class BluetoothDeviceWrapper {
149
226
  }
150
227
  }
151
228
  });
152
- Object.defineProperty(this, "processGattOperationQueue", {
153
- enumerable: true,
154
- configurable: true,
155
- writable: true,
156
- value: () => {
157
- if (!this.device.gatt?.connected) {
158
- // No longer connected. Drop queue.
159
- this.clearGattQueueOnDisconnect();
160
- return;
161
- }
162
- if (this.gattOperations.busy) {
163
- // We will finish processing the current operation, then
164
- // pick up processing the queue in the finally block.
165
- return;
166
- }
167
- const gattOperation = this.gattOperations.queue.shift();
168
- if (!gattOperation) {
169
- return;
170
- }
171
- this.gattOperations.busy = true;
172
- gattOperation
173
- .operation()
174
- .then((result) => {
175
- gattOperation.callback.resolve(result);
176
- })
177
- .catch((err) => {
178
- gattOperation.callback.reject(new DeviceError({ code: "background-comms-error", message: err }));
179
- this.logging.error("Error processing gatt operations queue", err);
180
- })
181
- .finally(() => {
182
- this.gattOperations.busy = false;
183
- this.processGattOperationQueue();
184
- });
185
- }
186
- });
187
229
  device.addEventListener("gattserverdisconnected", this.handleDisconnectEvent);
188
- for (const [key, value] of Object.entries(this.addedServiceListeners)) {
189
- this.serviceListenerState[key].notifying = value;
190
- }
191
230
  }
192
231
  async connect() {
193
232
  this.logging.event({
@@ -202,10 +241,23 @@ export class BluetoothDeviceWrapper {
202
241
  await this.gattConnectPromise;
203
242
  return;
204
243
  }
244
+ if (this.isReconnect) {
245
+ this.callbacks.onReconnecting();
246
+ }
247
+ else {
248
+ this.callbacks.onConnecting();
249
+ }
205
250
  this.duringExplicitConnectDisconnect++;
206
251
  if (this.device.gatt === undefined) {
207
252
  throw new Error("BluetoothRemoteGATTServer for micro:bit device is undefined");
208
253
  }
254
+ if (isWindowsOS) {
255
+ // On Windows, the micro:bit can take around 3 seconds to respond to gatt.disconnect().
256
+ // Attempting to connect/reconnect before the micro:bit has responded results in another
257
+ // gattserverdisconnected event being fired. We then fail to get primaryService on a
258
+ // disconnected GATT server.
259
+ await this.connectReadyPromise;
260
+ }
209
261
  try {
210
262
  // A previous connect might have completed in the background as a device was replugged etc.
211
263
  await this.disconnectPromise;
@@ -259,17 +311,12 @@ export class BluetoothDeviceWrapper {
259
311
  finally {
260
312
  this.connecting = false;
261
313
  }
262
- // Restart notifications for services and characteristics
263
- // the user has listened to.
264
- for (const serviceListener of Object.values(this.serviceListenerState)) {
265
- if (serviceListener.notifying) {
266
- serviceListener.service.call(this, { listenerInit: true });
267
- }
268
- }
314
+ this.currentEvents().forEach((e) => this.startNotifications(e));
269
315
  this.logging.event({
270
316
  type: this.isReconnect ? "Reconnect" : "Connect",
271
317
  message: "Bluetooth connect success",
272
318
  });
319
+ this.callbacks.onSuccess();
273
320
  }
274
321
  catch (e) {
275
322
  this.logging.error("Bluetooth connect error", e);
@@ -278,10 +325,13 @@ export class BluetoothDeviceWrapper {
278
325
  message: "Bluetooth connect failed",
279
326
  });
280
327
  await this.disconnectInternal(false);
328
+ this.callbacks.onFail();
281
329
  throw new Error("Failed to establish a connection!");
282
330
  }
283
331
  finally {
284
332
  this.duringExplicitConnectDisconnect--;
333
+ // Reset isReconnect for next time
334
+ this.isReconnect = false;
285
335
  }
286
336
  }
287
337
  async disconnect() {
@@ -303,18 +353,11 @@ export class BluetoothDeviceWrapper {
303
353
  this.disposeServices();
304
354
  this.duringExplicitConnectDisconnect--;
305
355
  }
306
- this.reconnectReadyPromise = new Promise((resolve) => setTimeout(resolve, 3_500));
356
+ this.connectReadyPromise = new Promise((resolve) => setTimeout(resolve, 3_500));
307
357
  }
308
358
  async reconnect() {
309
359
  this.logging.log("Bluetooth reconnect");
310
360
  this.isReconnect = true;
311
- if (isWindowsOS) {
312
- // On Windows, the micro:bit can take around 3 seconds to respond to gatt.disconnect().
313
- // Attempting to reconnect before the micro:bit has responded results in another
314
- // gattserverdisconnected event being fired. We then fail to get primaryService on a
315
- // disconnected GATT server.
316
- await this.reconnectReadyPromise;
317
- }
318
361
  await this.connect();
319
362
  }
320
363
  assertGattServer() {
@@ -344,42 +387,57 @@ export class BluetoothDeviceWrapper {
344
387
  throw new Error("Could not read model number");
345
388
  }
346
389
  }
347
- queueGattOperation(gattOperation) {
348
- this.gattOperations.queue.push(gattOperation);
349
- this.processGattOperationQueue();
390
+ queueGattOperation(action) {
391
+ // Previously we wrapped rejections with:
392
+ // new DeviceError({ code: "background-comms-error", message: err }),
393
+ return this.gattOperations.add(action);
350
394
  }
351
- clearGattQueueOnDisconnect() {
352
- this.gattOperations.queue.forEach((op) => {
353
- op.callback.reject(new DeviceError({
354
- code: "device-disconnected",
355
- message: "Error processing gatt operations queue - device disconnected",
356
- }));
357
- });
358
- this.gattOperations = { busy: false, queue: [] };
395
+ createIfNeeded(info, listenerInit) {
396
+ const gattServer = this.assertGattServer();
397
+ return info.createIfNeeded(gattServer, this.dispatchTypedEvent, this.queueGattOperation.bind(this), listenerInit);
398
+ }
399
+ async getAccelerometerService() {
400
+ return this.createIfNeeded(this.accelerometer, false);
401
+ }
402
+ async getLedService() {
403
+ return this.createIfNeeded(this.led, false);
359
404
  }
360
- async getAccelerometerService(options = { listenerInit: false }) {
361
- if (!this.accelerometerService) {
362
- const gattServer = this.assertGattServer();
363
- this.accelerometerService = await AccelerometerService.createService(gattServer, this.dispatchTypedEvent, this.serviceListenerState.accelerometerdatachanged.notifying, this.queueGattOperation.bind(this), options?.listenerInit);
405
+ async getUARTService() {
406
+ return this.createIfNeeded(this.uart, false);
407
+ }
408
+ async startNotifications(type) {
409
+ const serviceInfo = this.serviceInfo.find((s) => s.events.includes(type));
410
+ if (serviceInfo) {
411
+ this.queueGattOperation(async () => {
412
+ // TODO: type cheat! why?
413
+ const service = await this.createIfNeeded(serviceInfo, true);
414
+ await service?.startNotifications(type);
415
+ });
364
416
  }
365
- return this.accelerometerService;
417
+ }
418
+ async stopNotifications(type) {
419
+ this.queueGattOperation(async () => {
420
+ const serviceInfo = this.serviceInfo.find((s) => s.events.includes(type));
421
+ await serviceInfo?.get()?.stopNotifications(type);
422
+ });
366
423
  }
367
424
  disposeServices() {
368
- this.accelerometerService = undefined;
369
- this.clearGattQueueOnDisconnect();
425
+ this.serviceInfo.forEach((s) => s.dispose());
426
+ this.gattOperations.clear(this.disconnectedRejectionErrorFactory);
370
427
  }
371
428
  }
372
- export const createBluetoothDeviceWrapper = async (device, logging, dispatchTypedEvent, addedServiceListeners) => {
429
+ export const createBluetoothDeviceWrapper = async (device, logging, dispatchTypedEvent, currentEvents, callbacks) => {
373
430
  try {
374
431
  // Reuse our connection objects for the same device as they
375
432
  // track the GATT connect promise that never resolves.
376
433
  const bluetooth = deviceIdToWrapper.get(device.id) ??
377
- new BluetoothDeviceWrapper(device, logging, dispatchTypedEvent, addedServiceListeners);
434
+ new BluetoothDeviceWrapper(device, logging, dispatchTypedEvent, currentEvents, callbacks);
378
435
  deviceIdToWrapper.set(device.id, bluetooth);
379
436
  await bluetooth.connect();
380
437
  return bluetooth;
381
438
  }
382
439
  catch (e) {
440
+ logging.error("Bluetooth connect error", e);
383
441
  return undefined;
384
442
  }
385
443
  };