@microbit/microbit-connection 0.0.0-alpha.1

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 (68) hide show
  1. package/README.md +36 -0
  2. package/build/accelerometer-service.d.ts +17 -0
  3. package/build/accelerometer-service.js +84 -0
  4. package/build/accelerometer-service.js.map +1 -0
  5. package/build/accelerometer.d.ts +9 -0
  6. package/build/accelerometer.js +12 -0
  7. package/build/accelerometer.js.map +1 -0
  8. package/build/async-util.d.ts +13 -0
  9. package/build/async-util.js +22 -0
  10. package/build/async-util.js.map +1 -0
  11. package/build/bluetooth-device-wrapper.d.ts +39 -0
  12. package/build/bluetooth-device-wrapper.js +316 -0
  13. package/build/bluetooth-device-wrapper.js.map +1 -0
  14. package/build/bluetooth-profile.d.ts +139 -0
  15. package/build/bluetooth-profile.js +83 -0
  16. package/build/bluetooth-profile.js.map +1 -0
  17. package/build/bluetooth.d.ts +50 -0
  18. package/build/bluetooth.js +247 -0
  19. package/build/bluetooth.js.map +1 -0
  20. package/build/board-id.d.ts +35 -0
  21. package/build/board-id.js +69 -0
  22. package/build/board-id.js.map +1 -0
  23. package/build/board-serial-info.d.ts +14 -0
  24. package/build/board-serial-info.js +47 -0
  25. package/build/board-serial-info.js.map +1 -0
  26. package/build/constants.d.ts +47 -0
  27. package/build/constants.js +69 -0
  28. package/build/constants.js.map +1 -0
  29. package/build/device.d.ts +202 -0
  30. package/build/device.js +153 -0
  31. package/build/device.js.map +1 -0
  32. package/build/events.d.ts +101 -0
  33. package/build/events.js +19 -0
  34. package/build/events.js.map +1 -0
  35. package/build/hex-flash-data-source.d.ts +9 -0
  36. package/build/hex-flash-data-source.js +50 -0
  37. package/build/hex-flash-data-source.js.map +1 -0
  38. package/build/index.d.ts +7 -0
  39. package/build/index.js +7 -0
  40. package/build/index.js.map +1 -0
  41. package/build/logging.d.ts +21 -0
  42. package/build/logging.js +10 -0
  43. package/build/logging.js.map +1 -0
  44. package/build/service-events.d.ts +9 -0
  45. package/build/service-events.js +11 -0
  46. package/build/service-events.js.map +1 -0
  47. package/build/setupTests.d.ts +6 -0
  48. package/build/setupTests.js.map +1 -0
  49. package/build/usb-device-wrapper.d.ts +46 -0
  50. package/build/usb-device-wrapper.js +347 -0
  51. package/build/usb-device-wrapper.js.map +1 -0
  52. package/build/usb-partial-flashing-utils.d.ts +17 -0
  53. package/build/usb-partial-flashing-utils.js +122 -0
  54. package/build/usb-partial-flashing-utils.js.map +1 -0
  55. package/build/usb-partial-flashing.d.ts +63 -0
  56. package/build/usb-partial-flashing.js +270 -0
  57. package/build/usb-partial-flashing.js.map +1 -0
  58. package/build/usb-radio-bridge.d.ts +28 -0
  59. package/build/usb-radio-bridge.js +318 -0
  60. package/build/usb-radio-bridge.js.map +1 -0
  61. package/build/usb-serial-protocol.d.ts +66 -0
  62. package/build/usb-serial-protocol.js +171 -0
  63. package/build/usb-serial-protocol.js.map +1 -0
  64. package/build/usb.d.ts +61 -0
  65. package/build/usb.js +451 -0
  66. package/build/usb.js.map +1 -0
  67. package/package.json +32 -0
  68. package/vite.config.ts +32 -0
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # JavaScript library for micro:bit connections in browsers via USB and Bluetooth
2
+
3
+ This project is a work in progress. We are extracting WebUSB and Web Bluetooth code from the [micro:bit Python Editor](https://github.com/microbit-foundation/python-editor-v3/) and other projects.
4
+
5
+ It is intended to be used by other Micro:bit Educational Foundation projects that need to connect to a BBC micro:bit.
6
+
7
+ The API is not stable and it's not yet recommended that third parties use this project.
8
+
9
+ ## License
10
+
11
+ This software is under the MIT open source license.
12
+
13
+ [SPDX-License-Identifier: MIT](LICENSE)
14
+
15
+ We use dependencies via the NPM registry as specified by the package.json file under common Open Source licenses.
16
+
17
+ Full details of each package can be found by running `license-checker`:
18
+
19
+ ```bash
20
+ $ npx license-checker --direct --summary --production
21
+ ```
22
+
23
+ Omit the flags as desired to obtain more detail.
24
+
25
+ ## Code of Conduct
26
+
27
+ Trust, partnership, simplicity and passion are our core values we live and
28
+ breathe in our daily work life and within our projects. Our open-source
29
+ projects are no exception. We have an active community which spans the globe
30
+ and we welcome and encourage participation and contributions to our projects
31
+ by everyone. We work to foster a positive, open, inclusive and supportive
32
+ environment and trust that our community respects the micro:bit code of
33
+ conduct. Please see our [code of conduct](https://microbit.org/safeguarding/)
34
+ which outlines our expectations for all those that participate in our
35
+ community and details on how to report any concerns and what would happen
36
+ should breaches occur.
@@ -0,0 +1,17 @@
1
+ import { AccelerometerData } from "./accelerometer";
2
+ import { TypedServiceEventDispatcher } from "./service-events";
3
+ export declare class AccelerometerService {
4
+ private accelerometerDataCharacteristic;
5
+ private accelerometerPeriodCharacteristic;
6
+ private dispatchTypedEvent;
7
+ private isNotifying;
8
+ constructor(accelerometerDataCharacteristic: BluetoothRemoteGATTCharacteristic, accelerometerPeriodCharacteristic: BluetoothRemoteGATTCharacteristic, dispatchTypedEvent: TypedServiceEventDispatcher, isNotifying: boolean);
9
+ static createService(gattServer: BluetoothRemoteGATTServer, dispatcher: TypedServiceEventDispatcher, isNotifying: boolean): Promise<AccelerometerService>;
10
+ private dataViewToData;
11
+ getData(): Promise<AccelerometerData>;
12
+ getPeriod(): Promise<number>;
13
+ setPeriod(value: number): Promise<void>;
14
+ private addDataEventListener;
15
+ startNotifications(): void;
16
+ stopNotifications(): void;
17
+ }
@@ -0,0 +1,84 @@
1
+ import { AccelerometerDataEvent } from "./accelerometer";
2
+ import { profile } from "./bluetooth-profile";
3
+ export class AccelerometerService {
4
+ constructor(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatchTypedEvent, isNotifying) {
5
+ Object.defineProperty(this, "accelerometerDataCharacteristic", {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value: accelerometerDataCharacteristic
10
+ });
11
+ Object.defineProperty(this, "accelerometerPeriodCharacteristic", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: accelerometerPeriodCharacteristic
16
+ });
17
+ Object.defineProperty(this, "dispatchTypedEvent", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: dispatchTypedEvent
22
+ });
23
+ Object.defineProperty(this, "isNotifying", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: isNotifying
28
+ });
29
+ this.addDataEventListener();
30
+ if (this.isNotifying) {
31
+ this.startNotifications();
32
+ }
33
+ }
34
+ static async createService(gattServer, dispatcher, isNotifying) {
35
+ const accelerometerService = await gattServer.getPrimaryService(profile.accelerometer.id);
36
+ const accelerometerDataCharacteristic = await accelerometerService.getCharacteristic(profile.accelerometer.characteristics.data.id);
37
+ const accelerometerPeriodCharacteristic = await accelerometerService.getCharacteristic(profile.accelerometer.characteristics.period.id);
38
+ return new AccelerometerService(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatcher, isNotifying);
39
+ }
40
+ dataViewToData(dataView) {
41
+ return {
42
+ x: dataView.getInt16(0, true),
43
+ y: dataView.getInt16(2, true),
44
+ z: dataView.getInt16(4, true),
45
+ };
46
+ }
47
+ async getData() {
48
+ const dataView = await this.accelerometerDataCharacteristic.readValue();
49
+ return this.dataViewToData(dataView);
50
+ }
51
+ async getPeriod() {
52
+ const dataView = await this.accelerometerPeriodCharacteristic.readValue();
53
+ return dataView.getUint16(0, true);
54
+ }
55
+ async setPeriod(value) {
56
+ if (value === 0) {
57
+ // Writing 0 causes the device to crash.
58
+ return;
59
+ }
60
+ // Allowed values: 2, 5, 10, 20, 40, 100, 1000
61
+ // Values passed are rounded up to the allowed values on device.
62
+ // Documentation for allowed values looks wrong.
63
+ // https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html
64
+ const dataView = new DataView(new ArrayBuffer(2));
65
+ dataView.setUint16(0, value, true);
66
+ await this.accelerometerPeriodCharacteristic.writeValueWithoutResponse(dataView);
67
+ }
68
+ addDataEventListener() {
69
+ this.accelerometerDataCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
70
+ const target = event.target;
71
+ const data = this.dataViewToData(target.value);
72
+ this.dispatchTypedEvent("accelerometerdatachanged", new AccelerometerDataEvent(data));
73
+ });
74
+ }
75
+ startNotifications() {
76
+ this.accelerometerDataCharacteristic.startNotifications();
77
+ this.isNotifying = true;
78
+ }
79
+ stopNotifications() {
80
+ this.isNotifying = false;
81
+ this.accelerometerDataCharacteristic.stopNotifications();
82
+ }
83
+ }
84
+ //# sourceMappingURL=accelerometer-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accelerometer-service.js","sourceRoot":"","sources":["../lib/accelerometer-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAM9C,MAAM,OAAO,oBAAoB;IAC/B,YACU,+BAAkE,EAClE,iCAAoE,EACpE,kBAA+C,EAC/C,WAAoB;QAH5B;;;;mBAAQ,+BAA+B;WAAmC;QAC1E;;;;mBAAQ,iCAAiC;WAAmC;QAC5E;;;;mBAAQ,kBAAkB;WAA6B;QACvD;;;;mBAAQ,WAAW;WAAS;QAE5B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,aAAa,CACxB,UAAqC,EACrC,UAAuC,EACvC,WAAoB;QAEpB,MAAM,oBAAoB,GAAG,MAAM,UAAU,CAAC,iBAAiB,CAC7D,OAAO,CAAC,aAAa,CAAC,EAAE,CACzB,CAAC;QACF,MAAM,+BAA+B,GACnC,MAAM,oBAAoB,CAAC,iBAAiB,CAC1C,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAC9C,CAAC;QACJ,MAAM,iCAAiC,GACrC,MAAM,oBAAoB,CAAC,iBAAiB,CAC1C,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAChD,CAAC;QACJ,OAAO,IAAI,oBAAoB,CAC7B,+BAA+B,EAC/B,iCAAiC,EACjC,UAAU,EACV,WAAW,CACZ,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,QAAkB;QACvC,OAAO;YACL,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;YAC7B,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;YAC7B,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;SAC9B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,+BAA+B,CAAC,SAAS,EAAE,CAAC;QACxE,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iCAAiC,CAAC,SAAS,EAAE,CAAC;QAC1E,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,wCAAwC;YACxC,OAAO;QACT,CAAC;QACD,8CAA8C;QAC9C,gEAAgE;QAChE,gDAAgD;QAChD,kGAAkG;QAClG,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,iCAAiC,CAAC,yBAAyB,CACpE,QAAQ,CACT,CAAC;IACJ,CAAC;IAEO,oBAAoB;QAC1B,IAAI,CAAC,+BAA+B,CAAC,gBAAgB,CACnD,4BAA4B,EAC5B,CAAC,KAAY,EAAE,EAAE;YACf,MAAM,MAAM,GAAG,KAAK,CAAC,MAAkC,CAAC;YACxD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/C,IAAI,CAAC,kBAAkB,CACrB,0BAA0B,EAC1B,IAAI,sBAAsB,CAAC,IAAI,CAAC,CACjC,CAAC;QACJ,CAAC,CACF,CAAC;IACJ,CAAC;IAED,kBAAkB;QAChB,IAAI,CAAC,+BAA+B,CAAC,kBAAkB,EAAE,CAAC;QAC1D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,+BAA+B,CAAC,iBAAiB,EAAE,CAAC;IAC3D,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ export interface AccelerometerData {
2
+ x: number;
3
+ y: number;
4
+ z: number;
5
+ }
6
+ export declare class AccelerometerDataEvent extends Event {
7
+ readonly data: AccelerometerData;
8
+ constructor(data: AccelerometerData);
9
+ }
@@ -0,0 +1,12 @@
1
+ export class AccelerometerDataEvent extends Event {
2
+ constructor(data) {
3
+ super("accelerometerdatachanged");
4
+ Object.defineProperty(this, "data", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: data
9
+ });
10
+ }
11
+ }
12
+ //# sourceMappingURL=accelerometer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accelerometer.js","sourceRoot":"","sources":["../lib/accelerometer.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAA4B,IAAuB;QACjD,KAAK,CAAC,0BAA0B,CAAC,CAAC;QADxB;;;;mBAAgB,IAAI;WAAmB;IAEnD,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * (c) 2021, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export declare class TimeoutError extends Error {
7
+ }
8
+ /**
9
+ * Utility to time out an action after a delay.
10
+ *
11
+ * The action cannot be cancelled; it may still proceed after the timeout.
12
+ */
13
+ export declare function withTimeout<T>(actionPromise: Promise<T>, timeout: number): Promise<T>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * (c) 2021, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export class TimeoutError extends Error {
7
+ }
8
+ /**
9
+ * Utility to time out an action after a delay.
10
+ *
11
+ * The action cannot be cancelled; it may still proceed after the timeout.
12
+ */
13
+ export async function withTimeout(actionPromise, timeout) {
14
+ const timeoutPromise = new Promise((_, reject) => {
15
+ setTimeout(() => {
16
+ reject(new TimeoutError());
17
+ }, timeout);
18
+ });
19
+ // timeoutPromise never resolves so result must be from action
20
+ return Promise.race([actionPromise, timeoutPromise]);
21
+ }
22
+ //# sourceMappingURL=async-util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-util.js","sourceRoot":"","sources":["../lib/async-util.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG;AAE1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,aAAyB,EACzB,OAAe;IAEf,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QAC/C,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QAC7B,CAAC,EAAE,OAAO,CAAC,CAAC;IACd,CAAC,CAAC,CAAC;IACH,8DAA8D;IAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAe,CAAC;AACrE,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { AccelerometerService } from "./accelerometer-service";
7
+ import { BoardVersion } from "./device";
8
+ import { Logging } from "./logging";
9
+ import { TypedServiceEventDispatcher } from "./service-events";
10
+ export declare class BluetoothDeviceWrapper {
11
+ readonly device: BluetoothDevice;
12
+ private logging;
13
+ private dispatchTypedEvent;
14
+ private duringExplicitConnectDisconnect;
15
+ private gattConnectPromise;
16
+ private disconnectPromise;
17
+ private connecting;
18
+ private isReconnect;
19
+ private reconnectReadyPromise;
20
+ private accelerometerService;
21
+ boardVersion: BoardVersion | undefined;
22
+ serviceListeners: {
23
+ accelerometerdatachanged: {
24
+ notifying: boolean;
25
+ service: () => Promise<AccelerometerService>;
26
+ };
27
+ };
28
+ constructor(device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher);
29
+ connect(): Promise<void>;
30
+ disconnect(): Promise<void>;
31
+ private disconnectInternal;
32
+ reconnect(): Promise<void>;
33
+ handleDisconnectEvent: () => Promise<void>;
34
+ private assertGattServer;
35
+ private getBoardVersion;
36
+ getAccelerometerService(): Promise<AccelerometerService>;
37
+ private disposeServices;
38
+ }
39
+ export declare const createBluetoothDeviceWrapper: (device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher) => Promise<BluetoothDeviceWrapper | undefined>;
@@ -0,0 +1,316 @@
1
+ /**
2
+ * (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { AccelerometerService } from "./accelerometer-service";
7
+ import { profile } from "./bluetooth-profile";
8
+ import { NullLogging } from "./logging";
9
+ const deviceIdToWrapper = new Map();
10
+ const connectTimeoutDuration = 10000;
11
+ function findPlatform() {
12
+ const navigator = window.navigator;
13
+ const platform = navigator.userAgentData?.platform;
14
+ if (platform) {
15
+ return platform;
16
+ }
17
+ const isAndroid = /android/.test(navigator.userAgent.toLowerCase());
18
+ return isAndroid ? "android" : navigator.platform ?? "unknown";
19
+ }
20
+ const platform = findPlatform();
21
+ const isWindowsOS = platform && /^Win/.test(platform);
22
+ export class BluetoothDeviceWrapper {
23
+ constructor(device, logging = new NullLogging(), dispatchTypedEvent) {
24
+ Object.defineProperty(this, "device", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: device
29
+ });
30
+ Object.defineProperty(this, "logging", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: logging
35
+ });
36
+ Object.defineProperty(this, "dispatchTypedEvent", {
37
+ enumerable: true,
38
+ configurable: true,
39
+ writable: true,
40
+ value: dispatchTypedEvent
41
+ });
42
+ // Used to avoid automatic reconnection during user triggered connect/disconnect
43
+ // or reconnection itself.
44
+ Object.defineProperty(this, "duringExplicitConnectDisconnect", {
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ value: 0
49
+ });
50
+ // On ChromeOS and Mac there's no timeout and no clear way to abort
51
+ // device.gatt.connect(), so we accept that sometimes we'll still
52
+ // be trying to connect when we'd rather not be. If it succeeds when
53
+ // we no longer intend to be connected then we disconnect at that
54
+ // point. If we try to connect when a previous connection attempt is
55
+ // still around then we wait for it for our timeout period.
56
+ //
57
+ // On Windows it times out after 7s.
58
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=684073
59
+ Object.defineProperty(this, "gattConnectPromise", {
60
+ enumerable: true,
61
+ configurable: true,
62
+ writable: true,
63
+ value: void 0
64
+ });
65
+ Object.defineProperty(this, "disconnectPromise", {
66
+ enumerable: true,
67
+ configurable: true,
68
+ writable: true,
69
+ value: void 0
70
+ });
71
+ Object.defineProperty(this, "connecting", {
72
+ enumerable: true,
73
+ configurable: true,
74
+ writable: true,
75
+ value: false
76
+ });
77
+ Object.defineProperty(this, "isReconnect", {
78
+ enumerable: true,
79
+ configurable: true,
80
+ writable: true,
81
+ value: false
82
+ });
83
+ Object.defineProperty(this, "reconnectReadyPromise", {
84
+ enumerable: true,
85
+ configurable: true,
86
+ writable: true,
87
+ value: void 0
88
+ });
89
+ Object.defineProperty(this, "accelerometerService", {
90
+ enumerable: true,
91
+ configurable: true,
92
+ writable: true,
93
+ value: void 0
94
+ });
95
+ Object.defineProperty(this, "boardVersion", {
96
+ enumerable: true,
97
+ configurable: true,
98
+ writable: true,
99
+ value: void 0
100
+ });
101
+ Object.defineProperty(this, "serviceListeners", {
102
+ enumerable: true,
103
+ configurable: true,
104
+ writable: true,
105
+ value: {
106
+ accelerometerdatachanged: {
107
+ notifying: false,
108
+ service: this.getAccelerometerService,
109
+ },
110
+ }
111
+ });
112
+ Object.defineProperty(this, "handleDisconnectEvent", {
113
+ enumerable: true,
114
+ configurable: true,
115
+ writable: true,
116
+ value: async () => {
117
+ // this.outputWriteQueue = { busy: false, queue: [] };
118
+ try {
119
+ if (!this.duringExplicitConnectDisconnect) {
120
+ this.logging.log("Bluetooth GATT disconnected... automatically trying reconnect");
121
+ // stateOnReconnectionAttempt();
122
+ this.disposeServices();
123
+ await this.reconnect();
124
+ }
125
+ else {
126
+ this.logging.log("Bluetooth GATT disconnect ignored during explicit disconnect");
127
+ }
128
+ }
129
+ catch (e) {
130
+ this.logging.error("Bluetooth connect triggered by disconnect listener failed", e);
131
+ }
132
+ }
133
+ });
134
+ device.addEventListener("gattserverdisconnected", this.handleDisconnectEvent);
135
+ }
136
+ async connect() {
137
+ this.logging.event({
138
+ type: this.isReconnect ? "Reconnect" : "Connect",
139
+ message: "Bluetooth connect start",
140
+ });
141
+ if (this.duringExplicitConnectDisconnect) {
142
+ this.logging.log("Skipping connect attempt when one is already in progress");
143
+ // Wait for the gattConnectPromise while showing a "connecting" dialog.
144
+ // If the user clicks disconnect while the automatic reconnect is in progress,
145
+ // then clicks reconnect, we need to wait rather than return immediately.
146
+ await this.gattConnectPromise;
147
+ return;
148
+ }
149
+ this.duringExplicitConnectDisconnect++;
150
+ if (this.device.gatt === undefined) {
151
+ throw new Error("BluetoothRemoteGATTServer for micro:bit device is undefined");
152
+ }
153
+ try {
154
+ // A previous connect might have completed in the background as a device was replugged etc.
155
+ await this.disconnectPromise;
156
+ this.gattConnectPromise =
157
+ this.gattConnectPromise ??
158
+ this.device.gatt
159
+ .connect()
160
+ .then(async () => {
161
+ // We always do this even if we might immediately disconnect as disconnecting
162
+ // without using services causes getPrimaryService calls to hang on subsequent
163
+ // reconnect - probably a device-side issue.
164
+ this.boardVersion = await this.getBoardVersion();
165
+ // This connection could be arbitrarily later when our manual timeout may have passed.
166
+ // Do we still want to be connected?
167
+ if (!this.connecting) {
168
+ this.logging.log("Bluetooth GATT server connect after timeout, triggering disconnect");
169
+ this.disconnectPromise = (async () => {
170
+ await this.disconnectInternal(false);
171
+ this.disconnectPromise = undefined;
172
+ })();
173
+ }
174
+ else {
175
+ this.logging.log("Bluetooth GATT server connected when connecting");
176
+ }
177
+ })
178
+ .catch((e) => {
179
+ if (this.connecting) {
180
+ // Error will be logged by main connect error handling.
181
+ throw e;
182
+ }
183
+ else {
184
+ this.logging.error("Bluetooth GATT server connect error after our timeout", e);
185
+ return undefined;
186
+ }
187
+ })
188
+ .finally(() => {
189
+ this.logging.log("Bluetooth GATT server promise field cleared");
190
+ this.gattConnectPromise = undefined;
191
+ });
192
+ this.connecting = true;
193
+ try {
194
+ const gattConnectResult = await Promise.race([
195
+ this.gattConnectPromise,
196
+ new Promise((resolve) => setTimeout(() => resolve("timeout"), connectTimeoutDuration)),
197
+ ]);
198
+ if (gattConnectResult === "timeout") {
199
+ this.logging.log("Bluetooth GATT server connect timeout");
200
+ throw new Error("Bluetooth GATT server connect timeout");
201
+ }
202
+ }
203
+ finally {
204
+ this.connecting = false;
205
+ }
206
+ // Restart notifications for services and characteristics
207
+ // the user has listened to.
208
+ for (const serviceListener of Object.values(this.serviceListeners)) {
209
+ if (serviceListener.notifying) {
210
+ serviceListener.service.call(this);
211
+ }
212
+ }
213
+ this.logging.event({
214
+ type: this.isReconnect ? "Reconnect" : "Connect",
215
+ message: "Bluetooth connect success",
216
+ });
217
+ }
218
+ catch (e) {
219
+ this.logging.error("Bluetooth connect error", e);
220
+ this.logging.event({
221
+ type: this.isReconnect ? "Reconnect" : "Connect",
222
+ message: "Bluetooth connect failed",
223
+ });
224
+ await this.disconnectInternal(false);
225
+ throw new Error("Failed to establish a connection!");
226
+ }
227
+ finally {
228
+ this.duringExplicitConnectDisconnect--;
229
+ }
230
+ }
231
+ async disconnect() {
232
+ return this.disconnectInternal(true);
233
+ }
234
+ async disconnectInternal(userTriggered) {
235
+ this.logging.log(`Bluetooth disconnect ${userTriggered ? "(user triggered)" : "(programmatic)"}`);
236
+ this.duringExplicitConnectDisconnect++;
237
+ try {
238
+ if (this.device.gatt?.connected) {
239
+ this.device.gatt?.disconnect();
240
+ }
241
+ }
242
+ catch (e) {
243
+ this.logging.error("Bluetooth GATT disconnect error (ignored)", e);
244
+ // We might have already lost the connection.
245
+ }
246
+ finally {
247
+ this.disposeServices();
248
+ this.duringExplicitConnectDisconnect--;
249
+ }
250
+ this.reconnectReadyPromise = new Promise((resolve) => setTimeout(resolve, 3_500));
251
+ }
252
+ async reconnect() {
253
+ this.logging.log("Bluetooth reconnect");
254
+ this.isReconnect = true;
255
+ if (isWindowsOS) {
256
+ // On Windows, the micro:bit can take around 3 seconds to respond to gatt.disconnect().
257
+ // Attempting to reconnect before the micro:bit has responded results in another
258
+ // gattserverdisconnected event being fired. We then fail to get primaryService on a
259
+ // disconnected GATT server.
260
+ await this.reconnectReadyPromise;
261
+ }
262
+ await this.connect();
263
+ }
264
+ assertGattServer() {
265
+ if (!this.device.gatt?.connected) {
266
+ throw new Error("Could not listen to services, no microbit connected!");
267
+ }
268
+ return this.device.gatt;
269
+ }
270
+ async getBoardVersion() {
271
+ this.assertGattServer();
272
+ const serviceMeta = profile.deviceInformation;
273
+ try {
274
+ const deviceInfo = await this.assertGattServer().getPrimaryService(serviceMeta.id);
275
+ const characteristic = await deviceInfo.getCharacteristic(serviceMeta.characteristics.modelNumber.id);
276
+ const modelNumberBytes = await characteristic.readValue();
277
+ const modelNumber = new TextDecoder().decode(modelNumberBytes);
278
+ if (modelNumber.toLowerCase() === "BBC micro:bit".toLowerCase()) {
279
+ return "V1";
280
+ }
281
+ if (modelNumber.toLowerCase().includes("BBC micro:bit v2".toLowerCase())) {
282
+ return "V2";
283
+ }
284
+ throw new Error(`Unexpected model number ${modelNumber}`);
285
+ }
286
+ catch (e) {
287
+ this.logging.error("Could not read model number", e);
288
+ throw new Error("Could not read model number");
289
+ }
290
+ }
291
+ async getAccelerometerService() {
292
+ if (!this.accelerometerService) {
293
+ const gattServer = this.assertGattServer();
294
+ this.accelerometerService = await AccelerometerService.createService(gattServer, this.dispatchTypedEvent, this.serviceListeners.accelerometerdatachanged.notifying);
295
+ }
296
+ return this.accelerometerService;
297
+ }
298
+ disposeServices() {
299
+ this.accelerometerService = undefined;
300
+ }
301
+ }
302
+ export const createBluetoothDeviceWrapper = async (device, logging, dispatchTypedEvent) => {
303
+ try {
304
+ // Reuse our connection objects for the same device as they
305
+ // track the GATT connect promise that never resolves.
306
+ const bluetooth = deviceIdToWrapper.get(device.id) ??
307
+ new BluetoothDeviceWrapper(device, logging, dispatchTypedEvent);
308
+ deviceIdToWrapper.set(device.id, bluetooth);
309
+ await bluetooth.connect();
310
+ return bluetooth;
311
+ }
312
+ catch (e) {
313
+ return undefined;
314
+ }
315
+ };
316
+ //# sourceMappingURL=bluetooth-device-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bluetooth-device-wrapper.js","sourceRoot":"","sources":["../lib/bluetooth-device-wrapper.ts"],"names":[],"mappings":"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"}