@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.
- package/build/accelerometer-service.d.ts +8 -9
- package/build/accelerometer-service.js +24 -43
- package/build/accelerometer-service.js.map +1 -1
- package/build/bluetooth-device-wrapper.d.ts +30 -26
- package/build/bluetooth-device-wrapper.js +147 -89
- package/build/bluetooth-device-wrapper.js.map +1 -1
- package/build/bluetooth.d.ts +15 -11
- package/build/bluetooth.js +74 -91
- package/build/bluetooth.js.map +1 -1
- package/build/board-id.d.ts +1 -0
- package/build/board-id.js +8 -0
- package/build/board-id.js.map +1 -1
- package/build/button-service.d.ts +13 -0
- package/build/button-service.js +76 -0
- package/build/button-service.js.map +1 -0
- package/build/buttons.d.ts +10 -0
- package/build/buttons.js +18 -0
- package/build/buttons.js.map +1 -0
- package/build/constants.d.ts +1 -0
- package/build/constants.js +1 -0
- package/build/constants.js.map +1 -1
- package/build/device.d.ts +32 -39
- package/build/device.js +18 -3
- package/build/device.js.map +1 -1
- package/build/events.d.ts +11 -1
- package/build/events.js +86 -1
- package/build/events.js.map +1 -1
- package/build/hex-flash-data-source.d.ts +6 -8
- package/build/hex-flash-data-source.js +16 -45
- package/build/hex-flash-data-source.js.map +1 -1
- package/build/index.d.ts +10 -5
- package/build/index.js +7 -4
- package/build/index.js.map +1 -1
- package/build/led-service.d.ts +20 -0
- package/build/led-service.js +116 -0
- package/build/led-service.js.map +1 -0
- package/build/led.d.ts +6 -0
- package/build/led.js +2 -0
- package/build/led.js.map +1 -0
- package/build/logging.js +3 -1
- package/build/logging.js.map +1 -1
- package/build/promise-queue.d.ts +27 -0
- package/build/promise-queue.js +74 -0
- package/build/promise-queue.js.map +1 -0
- package/build/service-events.d.ts +5 -0
- package/build/service-events.js +18 -0
- package/build/service-events.js.map +1 -1
- package/build/uart-service.d.ts +13 -0
- package/build/uart-service.js +72 -0
- package/build/uart-service.js.map +1 -0
- package/build/uart.d.ts +4 -0
- package/build/uart.js +12 -0
- package/build/uart.js.map +1 -0
- package/build/usb-device-wrapper.d.ts +7 -1
- package/build/usb-device-wrapper.js +40 -3
- package/build/usb-device-wrapper.js.map +1 -1
- package/build/usb-partial-flashing.d.ts +11 -5
- package/build/usb-partial-flashing.js +53 -10
- package/build/usb-partial-flashing.js.map +1 -1
- package/build/usb-radio-bridge.d.ts +31 -18
- package/build/usb-radio-bridge.js +351 -187
- package/build/usb-radio-bridge.js.map +1 -1
- package/build/usb.d.ts +14 -17
- package/build/usb.js +111 -47
- package/build/usb.js.map +1 -1
- package/package.json +5 -3
- package/vite.config.ts +9 -7
- package/build/bluetooth-utils.d.ts +0 -5
- package/build/bluetooth-utils.js +0 -14
- package/build/bluetooth-utils.js.map +0 -1
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { AccelerometerData } from "./accelerometer.js";
|
|
2
|
-
import {
|
|
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,
|
|
11
|
-
static createService(gattServer: BluetoothRemoteGATTServer, dispatcher: TypedServiceEventDispatcher,
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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,
|
|
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.
|
|
38
|
-
|
|
39
|
-
this.
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
106
|
-
this.
|
|
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
|
-
|
|
113
|
-
this.
|
|
114
|
-
this.isNotifying = true;
|
|
88
|
+
async stopNotifications(type) {
|
|
89
|
+
await this.characteristicForEvent(type)?.stopNotifications();
|
|
115
90
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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":"AAAA,OAAO,EAAqB,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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
|
|
29
|
-
private
|
|
33
|
+
private connectReadyPromise;
|
|
34
|
+
private accelerometer;
|
|
35
|
+
private buttons;
|
|
36
|
+
private led;
|
|
37
|
+
private uart;
|
|
38
|
+
private serviceInfo;
|
|
30
39
|
boardVersion: BoardVersion | undefined;
|
|
31
|
-
|
|
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,
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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,
|
|
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,
|
|
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, "
|
|
84
|
+
Object.defineProperty(this, "currentEvents", {
|
|
47
85
|
enumerable: true,
|
|
48
86
|
configurable: true,
|
|
49
87
|
writable: true,
|
|
50
|
-
value:
|
|
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, "
|
|
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, "
|
|
143
|
+
Object.defineProperty(this, "accelerometer", {
|
|
100
144
|
enumerable: true,
|
|
101
145
|
configurable: true,
|
|
102
146
|
writable: true,
|
|
103
|
-
value:
|
|
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, "
|
|
184
|
+
Object.defineProperty(this, "disconnectedRejectionErrorFactory", {
|
|
112
185
|
enumerable: true,
|
|
113
186
|
configurable: true,
|
|
114
187
|
writable: true,
|
|
115
|
-
value: {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
352
|
-
this.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
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.
|
|
369
|
-
this.
|
|
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,
|
|
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,
|
|
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
|
};
|