6
6
7
7
import { AccelerometerService } from "./accelerometer-service.js" ;
8
8
import { profile } from "./bluetooth-profile.js" ;
9
+ import { ButtonService } from "./button-service.js" ;
9
10
import { BoardVersion , DeviceError } from "./device.js" ;
10
11
import { Logging , NullLogging } from "./logging.js" ;
11
12
import {
12
13
ServiceConnectionEventMap ,
14
+ TypedServiceEvent ,
13
15
TypedServiceEventDispatcher ,
14
16
} from "./service-events.js" ;
15
17
@@ -48,6 +50,50 @@ function findPlatform(): string | undefined {
48
50
const platform = findPlatform ( ) ;
49
51
const isWindowsOS = platform && / ^ W i n / . test ( platform ) ;
50
52
53
+ export interface Service {
54
+ startNotifications ( type : TypedServiceEvent ) : Promise < void > ;
55
+ stopNotifications ( type : TypedServiceEvent ) : Promise < void > ;
56
+ }
57
+
58
+ class ServiceInfo < T extends Service > {
59
+ private service : T | undefined ;
60
+
61
+ constructor (
62
+ private serviceFactory : (
63
+ gattServer : BluetoothRemoteGATTServer ,
64
+ dispatcher : TypedServiceEventDispatcher ,
65
+ queueGattOperation : ( gattOperation : GattOperation ) => void ,
66
+ listenerInit : boolean ,
67
+ ) => Promise < T | undefined > ,
68
+ public events : TypedServiceEvent [ ] ,
69
+ ) { }
70
+
71
+ get ( ) : T | undefined {
72
+ return this . service ;
73
+ }
74
+
75
+ async createIfNeeded (
76
+ gattServer : BluetoothRemoteGATTServer ,
77
+ dispatcher : TypedServiceEventDispatcher ,
78
+ queueGattOperation : ( gattOperation : GattOperation ) => void ,
79
+ listenerInit : boolean ,
80
+ ) : Promise < T | undefined > {
81
+ this . service =
82
+ this . service ??
83
+ ( await this . serviceFactory (
84
+ gattServer ,
85
+ dispatcher ,
86
+ queueGattOperation ,
87
+ listenerInit ,
88
+ ) ) ;
89
+ return this . service ;
90
+ }
91
+
92
+ dispose ( ) {
93
+ this . service = undefined ;
94
+ }
95
+ }
96
+
51
97
export class BluetoothDeviceWrapper {
52
98
// Used to avoid automatic reconnection during user triggered connect/disconnect
53
99
// or reconnection itself.
@@ -67,15 +113,17 @@ export class BluetoothDeviceWrapper {
67
113
private connecting = false ;
68
114
private isReconnect = false ;
69
115
private reconnectReadyPromise : Promise < void > | undefined ;
70
- private accelerometerService : AccelerometerService | undefined ;
116
+
117
+ private accelerometer = new ServiceInfo ( AccelerometerService . createService , [
118
+ "accelerometerdatachanged" ,
119
+ ] ) ;
120
+ private buttons = new ServiceInfo ( ButtonService . createService , [
121
+ "buttonachanged" ,
122
+ "buttonbchanged" ,
123
+ ] ) ;
124
+ private serviceInfo = [ this . accelerometer , this . buttons ] ;
71
125
72
126
boardVersion : BoardVersion | undefined ;
73
- serviceListenerState = {
74
- accelerometerdatachanged : {
75
- notifying : false ,
76
- service : this . getAccelerometerService ,
77
- } ,
78
- } ;
79
127
80
128
private gattOperations : GattOperations = {
81
129
busy : false ,
@@ -86,20 +134,13 @@ export class BluetoothDeviceWrapper {
86
134
public readonly device : BluetoothDevice ,
87
135
private logging : Logging = new NullLogging ( ) ,
88
136
private dispatchTypedEvent : TypedServiceEventDispatcher ,
89
- private addedServiceListeners : Record <
90
- keyof ServiceConnectionEventMap ,
91
- boolean
92
- > ,
137
+ // We recreate this for the same connection and need to re-setup notifications for the old events
138
+ private events : Record < keyof ServiceConnectionEventMap , boolean > ,
93
139
) {
94
140
device . addEventListener (
95
141
"gattserverdisconnected" ,
96
142
this . handleDisconnectEvent ,
97
143
) ;
98
- for ( const [ key , value ] of Object . entries ( this . addedServiceListeners ) ) {
99
- this . serviceListenerState [
100
- key as keyof ServiceConnectionEventMap
101
- ] . notifying = value ;
102
- }
103
144
}
104
145
105
146
async connect ( ) : Promise < void > {
@@ -184,13 +225,9 @@ export class BluetoothDeviceWrapper {
184
225
this . connecting = false ;
185
226
}
186
227
187
- // Restart notifications for services and characteristics
188
- // the user has listened to.
189
- for ( const serviceListener of Object . values ( this . serviceListenerState ) ) {
190
- if ( serviceListener . notifying ) {
191
- serviceListener . service . call ( this , { listenerInit : true } ) ;
192
- }
193
- }
228
+ Object . keys ( this . events ) . forEach ( ( e ) =>
229
+ this . startNotifications ( e as TypedServiceEvent ) ,
230
+ ) ;
194
231
195
232
this . logging . event ( {
196
233
type : this . isReconnect ? "Reconnect" : "Connect" ,
@@ -354,26 +391,39 @@ export class BluetoothDeviceWrapper {
354
391
this . gattOperations = { busy : false , queue : [ ] } ;
355
392
}
356
393
357
- async getAccelerometerService (
358
- options : {
359
- listenerInit : boolean ;
360
- } = { listenerInit : false } ,
361
- ) : Promise < AccelerometerService | undefined > {
362
- if ( ! this . accelerometerService ) {
363
- const gattServer = this . assertGattServer ( ) ;
364
- this . accelerometerService = await AccelerometerService . createService (
365
- gattServer ,
366
- this . dispatchTypedEvent ,
367
- this . serviceListenerState . accelerometerdatachanged . notifying ,
368
- this . queueGattOperation . bind ( this ) ,
369
- options ?. listenerInit ,
370
- ) ;
394
+ private createIfNeeded < T extends Service > (
395
+ info : ServiceInfo < T > ,
396
+ listenerInit : boolean ,
397
+ ) : Promise < T | undefined > {
398
+ const gattServer = this . assertGattServer ( ) ;
399
+ return info . createIfNeeded (
400
+ gattServer ,
401
+ this . dispatchTypedEvent ,
402
+ this . queueGattOperation . bind ( this ) ,
403
+ listenerInit ,
404
+ ) ;
405
+ }
406
+
407
+ async getAccelerometerService ( ) : Promise < AccelerometerService | undefined > {
408
+ return this . createIfNeeded ( this . accelerometer , false ) ;
409
+ }
410
+
411
+ async startNotifications ( type : TypedServiceEvent ) {
412
+ const serviceInfo = this . serviceInfo . find ( ( s ) => s . events . includes ( type ) ) ;
413
+ if ( serviceInfo ) {
414
+ // TODO: type cheat! why?
415
+ const service = await this . createIfNeeded ( serviceInfo as any , true ) ;
416
+ service ?. startNotifications ( type ) ;
371
417
}
372
- return this . accelerometerService ;
418
+ }
419
+
420
+ async stopNotifications ( type : TypedServiceEvent ) {
421
+ const serviceInfo = this . serviceInfo . find ( ( s ) => s . events . includes ( type ) ) ;
422
+ serviceInfo ?. get ( ) ?. stopNotifications ( type ) ;
373
423
}
374
424
375
425
private disposeServices ( ) {
376
- this . accelerometerService = undefined ;
426
+ this . serviceInfo . forEach ( ( s ) => s . dispose ( ) ) ;
377
427
this . clearGattQueueOnDisconnect ( ) ;
378
428
}
379
429
}
0 commit comments