@@ -46,6 +46,11 @@ class WebBluetooth extends EventTarget {
46
46
this . bluetooth . addEventListener ( "gatserverdisconnected" , ( e ) => this . handleRemovedDevice ( e . target ) ) ;
47
47
48
48
this . loadDevices ( ) ;
49
+
50
+ // Properly bind all event handlers ONCE
51
+ this . boundHandleDisconnect = this . handleDisconnect . bind ( this ) ;
52
+ this . boundHandleNotification = this . handleNotification . bind ( this ) ;
53
+ this . boundHandleReceiveBytes = this . handleReceiveBytes . bind ( this ) ;
49
54
}
50
55
51
56
handleNewDevice ( device ) {
@@ -135,7 +140,8 @@ class WebBluetooth extends EventTarget {
135
140
136
141
console . log ( `${ this . logHead } Opening connection with ID: ${ path } , Baud: ${ options . baudRate } ` ) ;
137
142
138
- this . device . addEventListener ( "gattserverdisconnected" , this . handleDisconnect . bind ( this ) ) ;
143
+ // Use bound method references
144
+ this . device . addEventListener ( "gattserverdisconnected" , this . boundHandleDisconnect ) ;
139
145
140
146
try {
141
147
console . log ( `${ this . logHead } Connecting to GATT Server` ) ;
@@ -163,8 +169,9 @@ class WebBluetooth extends EventTarget {
163
169
this . failed = 0 ;
164
170
this . openRequested = false ;
165
171
166
- this . device . addEventListener ( "disconnect" , this . handleDisconnect . bind ( this ) ) ;
167
- this . addEventListener ( "receive" , this . handleReceiveBytes ) ;
172
+ // Use bound references here too
173
+ this . device . addEventListener ( "disconnect" , this . boundHandleDisconnect ) ;
174
+ this . addEventListener ( "receive" , this . boundHandleReceiveBytes ) ;
168
175
169
176
console . log ( `${ this . logHead } Connection opened with ID: ${ this . connectionId } , Baud: ${ options . baudRate } ` ) ;
170
177
@@ -221,47 +228,69 @@ class WebBluetooth extends EventTarget {
221
228
}
222
229
223
230
async getCharacteristics ( ) {
224
- const characteristics = await this . service . getCharacteristics ( ) ;
231
+ try {
232
+ const characteristics = await this . service . getCharacteristics ( ) ;
225
233
226
- characteristics . forEach ( ( characteristic ) => {
227
- // console.log("Characteristic: ", characteristic);
228
- if ( characteristic . uuid == this . deviceDescription . writeCharacteristic ) {
229
- this . writeCharacteristic = characteristic ;
234
+ if ( ! characteristics || characteristics . length === 0 ) {
235
+ throw new Error ( "No characteristics found" ) ;
230
236
}
231
237
232
- if ( characteristic . uuid == this . deviceDescription . readCharacteristic ) {
233
- this . readCharacteristic = characteristic ;
238
+ // Reset characteristics
239
+ this . writeCharacteristic = null ;
240
+ this . readCharacteristic = null ;
241
+
242
+ for ( const characteristic of characteristics ) {
243
+ if ( characteristic . uuid === this . deviceDescription . writeCharacteristic ) {
244
+ this . writeCharacteristic = characteristic ;
245
+ }
246
+
247
+ if ( characteristic . uuid === this . deviceDescription . readCharacteristic ) {
248
+ this . readCharacteristic = characteristic ;
249
+ }
250
+
251
+ if ( this . writeCharacteristic && this . readCharacteristic ) {
252
+ break ;
253
+ }
234
254
}
235
- return this . writeCharacteristic && this . readCharacteristic ;
236
- } ) ;
237
255
238
- if ( ! this . writeCharacteristic ) {
239
- throw new Error (
240
- "Unexpected write characteristic found - should be" ,
241
- this . deviceDescription . writeCharacteristic ,
242
- ) ;
243
- }
256
+ if ( ! this . writeCharacteristic ) {
257
+ throw new Error ( `Write characteristic not found: ${ this . deviceDescription . writeCharacteristic } ` ) ;
258
+ }
244
259
245
- if ( ! this . readCharacteristic ) {
246
- throw new Error (
247
- "Unexpected read characteristic found - should be" ,
248
- this . deviceDescription . readCharacteristic ,
249
- ) ;
250
- }
260
+ if ( ! this . readCharacteristic ) {
261
+ throw new Error ( `Read characteristic not found: ${ this . deviceDescription . readCharacteristic } ` ) ;
262
+ }
251
263
252
- this . readCharacteristic . addEventListener ( "characteristicvaluechanged" , this . handleNotification . bind ( this ) ) ;
264
+ // Use the bound method for the event listener
265
+ this . readCharacteristic . addEventListener ( "characteristicvaluechanged" , this . boundHandleNotification ) ;
253
266
254
- return await this . readCharacteristic . readValue ( ) ;
267
+ return await this . readCharacteristic . readValue ( ) ;
268
+ } catch ( error ) {
269
+ console . error ( `${ this . logHead } Error getting characteristics:` , error ) ;
270
+ throw error ;
271
+ }
255
272
}
256
273
257
274
handleNotification ( event ) {
258
- const buffer = new Uint8Array ( event . target . value . byteLength ) ;
275
+ try {
276
+ if ( ! event . target . value ) {
277
+ console . warn ( `${ this . logHead } Empty notification received` ) ;
278
+ return ;
279
+ }
259
280
260
- for ( let i = 0 ; i < event . target . value . byteLength ; i ++ ) {
261
- buffer [ i ] = event . target . value . getUint8 ( i ) ;
262
- }
281
+ const buffer = new Uint8Array ( event . target . value . byteLength ) ;
282
+
283
+ // Copy data with validation
284
+ for ( let i = 0 ; i < event . target . value . byteLength ; i ++ ) {
285
+ buffer [ i ] = event . target . value . getUint8 ( i ) ;
286
+ }
263
287
264
- this . dispatchEvent ( new CustomEvent ( "receive" , { detail : buffer } ) ) ;
288
+ if ( buffer . length > 0 ) {
289
+ this . dispatchEvent ( new CustomEvent ( "receive" , { detail : buffer } ) ) ;
290
+ }
291
+ } catch ( error ) {
292
+ console . error ( `${ this . logHead } Error handling notification:` , error ) ;
293
+ }
265
294
}
266
295
267
296
startNotifications ( ) {
@@ -287,48 +316,50 @@ class WebBluetooth extends EventTarget {
287
316
return ;
288
317
}
289
318
290
- const doCleanup = async ( ) => {
291
- this . removeEventListener ( "receive" , this . handleReceiveBytes ) ;
319
+ this . closeRequested = true ; // Set this to prevent reentry
320
+
321
+ try {
322
+ this . removeEventListener ( "receive" , this . boundHandleReceiveBytes ) ;
292
323
293
324
if ( this . device ) {
294
- this . device . removeEventListener ( "disconnect" , this . handleDisconnect . bind ( this ) ) ;
295
- this . device . removeEventListener ( "gattserverdisconnected" , this . handleDisconnect ) ;
296
- this . readCharacteristic . removeEventListener (
297
- "characteristicvaluechanged" ,
298
- this . handleNotification . bind ( this ) ,
299
- ) ;
300
-
301
- if ( this . device . gatt . connected ) {
302
- this . device . gatt . disconnect ( ) ;
325
+ // Use the properly bound references
326
+ this . device . removeEventListener ( "disconnect" , this . boundHandleDisconnect ) ;
327
+ this . device . removeEventListener ( "gattserverdisconnected" , this . boundHandleDisconnect ) ;
328
+
329
+ if ( this . readCharacteristic ) {
330
+ try {
331
+ // Stop notifications first to avoid errors
332
+ await this . readCharacteristic . stopNotifications ( ) ;
333
+ this . readCharacteristic . removeEventListener (
334
+ "characteristicvaluechanged" ,
335
+ this . boundHandleNotification ,
336
+ ) ;
337
+ } catch ( err ) {
338
+ console . warn ( `${ this . logHead } Error stopping notifications:` , err ) ;
339
+ }
303
340
}
304
341
305
- this . writeCharacteristic = false ;
306
- this . readCharacteristic = false ;
307
- this . deviceDescription = false ;
308
- this . device = null ;
309
- }
310
- } ;
311
-
312
- try {
313
- await doCleanup ( ) ;
342
+ // Safely disconnect GATT
343
+ if ( this . device . gatt ?. connected ) {
344
+ await this . device . gatt . disconnect ( ) ;
345
+ }
314
346
315
- console . log (
316
- `${ this . logHead } Connection with ID: ${ this . connectionId } closed, Sent: ${ this . bytesSent } bytes, Received: ${ this . bytesReceived } bytes` ,
317
- ) ;
347
+ // Clear references
348
+ this . writeCharacteristic = null ;
349
+ this . readCharacteristic = null ;
350
+ this . deviceDescription = null ;
351
+ }
318
352
353
+ console . log ( `${ this . logHead } Connection closed successfully` ) ;
319
354
this . connectionId = false ;
320
- this . bitrate = 0 ;
355
+ this . device = null ;
321
356
this . dispatchEvent ( new CustomEvent ( "disconnect" , { detail : true } ) ) ;
322
357
} catch ( error ) {
323
- console . error ( error ) ;
324
- console . error (
325
- `${ this . logHead } Failed to close connection with ID: ${ this . connectionId } closed, Sent: ${ this . bytesSent } bytes, Received: ${ this . bytesReceived } bytes` ,
326
- ) ;
358
+ console . error ( `${ this . logHead } Error during disconnect:` , error ) ;
327
359
this . dispatchEvent ( new CustomEvent ( "disconnect" , { detail : false } ) ) ;
328
360
} finally {
329
- if ( this . openCanceled ) {
330
- this . openCanceled = false ;
331
- }
361
+ this . closeRequested = false ;
362
+ this . openCanceled = false ;
332
363
}
333
364
}
334
365
0 commit comments