66
77import { AccelerometerService } from "./accelerometer-service.js" ;
88import { profile } from "./bluetooth-profile.js" ;
9+ import { ButtonService } from "./button-service.js" ;
910import { BoardVersion , DeviceError } from "./device.js" ;
1011import { Logging , NullLogging } from "./logging.js" ;
1112import {
1213 ServiceConnectionEventMap ,
14+ TypedServiceEvent ,
1315 TypedServiceEventDispatcher ,
1416} from "./service-events.js" ;
1517
@@ -48,6 +50,50 @@ function findPlatform(): string | undefined {
4850const platform = findPlatform ( ) ;
4951const isWindowsOS = platform && / ^ W i n / . test ( platform ) ;
5052
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+
5197export class BluetoothDeviceWrapper {
5298 // Used to avoid automatic reconnection during user triggered connect/disconnect
5399 // or reconnection itself.
@@ -67,15 +113,17 @@ export class BluetoothDeviceWrapper {
67113 private connecting = false ;
68114 private isReconnect = false ;
69115 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 ] ;
71125
72126 boardVersion : BoardVersion | undefined ;
73- serviceListenerState = {
74- accelerometerdatachanged : {
75- notifying : false ,
76- service : this . getAccelerometerService ,
77- } ,
78- } ;
79127
80128 private gattOperations : GattOperations = {
81129 busy : false ,
@@ -86,20 +134,13 @@ export class BluetoothDeviceWrapper {
86134 public readonly device : BluetoothDevice ,
87135 private logging : Logging = new NullLogging ( ) ,
88136 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 > ,
93139 ) {
94140 device . addEventListener (
95141 "gattserverdisconnected" ,
96142 this . handleDisconnectEvent ,
97143 ) ;
98- for ( const [ key , value ] of Object . entries ( this . addedServiceListeners ) ) {
99- this . serviceListenerState [
100- key as keyof ServiceConnectionEventMap
101- ] . notifying = value ;
102- }
103144 }
104145
105146 async connect ( ) : Promise < void > {
@@ -184,13 +225,9 @@ export class BluetoothDeviceWrapper {
184225 this . connecting = false ;
185226 }
186227
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+ ) ;
194231
195232 this . logging . event ( {
196233 type : this . isReconnect ? "Reconnect" : "Connect" ,
@@ -354,26 +391,39 @@ export class BluetoothDeviceWrapper {
354391 this . gattOperations = { busy : false , queue : [ ] } ;
355392 }
356393
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 ) ;
371417 }
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 ) ;
373423 }
374424
375425 private disposeServices ( ) {
376- this . accelerometerService = undefined ;
426+ this . serviceInfo . forEach ( ( s ) => s . dispose ( ) ) ;
377427 this . clearGattQueueOnDisconnect ( ) ;
378428 }
379429}
0 commit comments