Skip to content

Commit 1075bee

Browse files
Migrate to capacitor's BleClient
Quite a simplication as the service/characteristic lookups are deferred to point of use and the interactions are internally queued. I can't see how to filter with more than once namePrefix. That might require a patch but can be deferred for now. This works well via the demo for web. It'll need flashing code integrating before it's useful for the app. Reconnection logic feels like something that's going to conflict with flashing (where disconnects happen as part of pairing) so we need to bottom that out as part of the integration. But it's super-useful for typical use.
1 parent 6ae045f commit 1075bee

File tree

9 files changed

+300
-492
lines changed

9 files changed

+300
-492
lines changed

lib/accelerometer-service.ts

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,27 @@
1+
import { BleClient } from "@capacitor-community/bluetooth-le";
12
import { AccelerometerData, AccelerometerDataEvent } from "./accelerometer.js";
23
import { Service } from "./bluetooth-device-wrapper.js";
3-
import { profile } from "./bluetooth-profile.js";
4-
import { BackgroundErrorEvent, DeviceError } from "./device.js";
54
import {
6-
CharacteristicDataTarget,
75
TypedServiceEvent,
86
TypedServiceEventDispatcher,
97
} from "./service-events.js";
8+
import { profile } from "./bluetooth-profile.js";
109

1110
export class AccelerometerService implements Service {
11+
static createService(
12+
deviceId: string,
13+
dispatchTypedEvent: TypedServiceEventDispatcher,
14+
): AccelerometerService {
15+
return new AccelerometerService(deviceId, dispatchTypedEvent);
16+
}
17+
1218
constructor(
13-
private accelerometerDataCharacteristic: BluetoothRemoteGATTCharacteristic,
14-
private accelerometerPeriodCharacteristic: BluetoothRemoteGATTCharacteristic,
19+
private deviceId: string,
1520
private dispatchTypedEvent: TypedServiceEventDispatcher,
16-
private queueGattOperation: <R>(action: () => Promise<R>) => Promise<R>,
17-
) {
18-
this.accelerometerDataCharacteristic.addEventListener(
19-
"characteristicvaluechanged",
20-
(event: Event) => {
21-
const target = event.target as CharacteristicDataTarget;
22-
const data = this.dataViewToData(target.value);
23-
this.dispatchTypedEvent(
24-
"accelerometerdatachanged",
25-
new AccelerometerDataEvent(data),
26-
);
27-
},
28-
);
29-
}
21+
) {}
3022

31-
static async createService(
32-
gattServer: BluetoothRemoteGATTServer,
33-
dispatcher: TypedServiceEventDispatcher,
34-
queueGattOperation: <R>(action: () => Promise<R>) => Promise<R>,
35-
listenerInit: boolean,
36-
): Promise<AccelerometerService | undefined> {
37-
let accelerometerService: BluetoothRemoteGATTService;
38-
try {
39-
accelerometerService = await gattServer.getPrimaryService(
40-
profile.accelerometer.id,
41-
);
42-
} catch (err) {
43-
if (listenerInit) {
44-
dispatcher("backgrounderror", new BackgroundErrorEvent(err as string));
45-
return;
46-
} else {
47-
throw new DeviceError({
48-
code: "service-missing",
49-
message: err as string,
50-
});
51-
}
52-
}
53-
const accelerometerDataCharacteristic =
54-
await accelerometerService.getCharacteristic(
55-
profile.accelerometer.characteristics.data.id,
56-
);
57-
const accelerometerPeriodCharacteristic =
58-
await accelerometerService.getCharacteristic(
59-
profile.accelerometer.characteristics.period.id,
60-
);
61-
return new AccelerometerService(
62-
accelerometerDataCharacteristic,
63-
accelerometerPeriodCharacteristic,
64-
dispatcher,
65-
queueGattOperation,
66-
);
23+
getRelevantEvents(): TypedServiceEvent[] {
24+
return ["accelerometerdatachanged"];
6725
}
6826

6927
private dataViewToData(dataView: DataView): AccelerometerData {
@@ -75,15 +33,19 @@ export class AccelerometerService implements Service {
7533
}
7634

7735
async getData(): Promise<AccelerometerData> {
78-
const dataView = await this.queueGattOperation(() =>
79-
this.accelerometerDataCharacteristic.readValue(),
36+
const dataView = await BleClient.read(
37+
this.deviceId,
38+
profile.accelerometer.id,
39+
profile.accelerometer.characteristics.data.id,
8040
);
8141
return this.dataViewToData(dataView);
8242
}
8343

8444
async getPeriod(): Promise<number> {
85-
const dataView = await this.queueGattOperation(() =>
86-
this.accelerometerPeriodCharacteristic.readValue(),
45+
const dataView = await BleClient.read(
46+
this.deviceId,
47+
profile.accelerometer.id,
48+
profile.accelerometer.characteristics.period.id,
8749
);
8850
return dataView.getUint16(0, true);
8951
}
@@ -99,23 +61,48 @@ export class AccelerometerService implements Service {
9961
// https://lancaster-university.github.io/microbit-docs/ble/profile/#about-the-accelerometer-service
10062
const dataView = new DataView(new ArrayBuffer(2));
10163
dataView.setUint16(0, value, true);
102-
return this.queueGattOperation(() =>
103-
this.accelerometerPeriodCharacteristic.writeValue(dataView),
64+
await BleClient.write(
65+
this.deviceId,
66+
profile.accelerometer.id,
67+
profile.accelerometer.characteristics.period.id,
68+
dataView,
10469
);
10570
}
10671

10772
async startNotifications(type: TypedServiceEvent): Promise<void> {
108-
await this.characteristicForEvent(type)?.startNotifications();
73+
const result = this.characteristicForEvent(type);
74+
if (result) {
75+
const { service, characteristic } = result;
76+
BleClient.startNotifications(
77+
this.deviceId,
78+
service,
79+
characteristic,
80+
(value) => {
81+
const data = this.dataViewToData(value);
82+
this.dispatchTypedEvent(
83+
"accelerometerdatachanged",
84+
new AccelerometerDataEvent(data),
85+
);
86+
},
87+
);
88+
}
10989
}
11090

11191
async stopNotifications(type: TypedServiceEvent): Promise<void> {
112-
await this.characteristicForEvent(type)?.stopNotifications();
92+
const result = this.characteristicForEvent(type);
93+
if (result) {
94+
const { service, characteristic } = result;
95+
BleClient.stopNotifications(this.deviceId, service, characteristic);
96+
}
11397
}
11498

11599
private characteristicForEvent(type: TypedServiceEvent) {
116100
switch (type) {
117101
case "accelerometerdatachanged": {
118-
return this.accelerometerDataCharacteristic;
102+
return {
103+
service: profile.accelerometer.id,
104+
characteristic: profile.accelerometer.characteristics.data.id,
105+
};
119106
}
120107
default: {
121108
return undefined;

0 commit comments

Comments
 (0)