According to the docs isDeviceConnected should return either true or false depending on whether the BLE device is connected to the mobile/app
Despite of running isDeviceConnected on a device that it's actually connected it always returns false.
I need to check by using the built-in RN AppState component whether a device the user was using before switching of apps (or after coming back from background) it's still connected...
Every time I call isDeviceConnected with a connected device Id, I always get Device AA:BB:CC:DD:EE:FF is NOT connected!
const isBleDeviceConnected = async (deviceId = null) => {
if (!deviceId) return false;
const isConnected = await bleManager.isDeviceConnected(deviceId);
if (isConnected) {
console.log(`Device ${deviceId} is connected!`);
} else {
console.log(`Device ${deviceId} is NOT connected!`);
}
return isConnected;
};
Is there any fix in progress or a workaround available?
Thanks in advance!
Federico
I can confirm this is a bug and it's major. If a user clicks the back button and the App goes to the background the connection is 'muted' but kept open. I've verified this on my bluetooth board.
The only thing I can think about doing is hitting a characteristic as a workaround and handling the error as a disconnect that way.
Further investigation
Hitting a characteristic didn't work either. The plx 'device' thinks it's disconnected but BLE_GAP is still connected.
At first attempt I get 'BleError: Device ? is already connected'. After this it hangs on scanning, which I'd expect it to as my BLE is still connected and not advertising.
@andrewcharnley could you prepare a minimal example repo?
I've semi solved it (only tested on Android).
Rather than let the hardware back button push the app to the background you have to use BackHandler.exitApp(). Oddly this React Native feature is also broken - the app doesn't exit but retains it's state in the background in a suspended state, however the BLE device is still connected and crucially isConnected() works to indicate it as such. For some reason this is different than without using exitApp.
Without using BackHandler.exitApp there is no way to get back to the BLE device without killing the app completely (not just using back button).
I haven't tested the Home button yet.
How do you store the BLEManager instance?
It's at file level outside of React's component function, so there is only the one, and it isn't affected by re-renders.
I see nothing in the code that should justify such behaviour. Could you create a minimal example project?
I assume the issue is visible only on Android?
@dariuszseweryn unfortunately I can confirm this happens on iOS as well...
Could you create a minimal example project which I could compile myself and dig into it?
Although I have my workaround I've got a possibly related bug where if I leave the app on searching after a amount of time (which I hope to quantify) it hangs without finding the device when I set it to advertise. If I then disable bluetooth in android and re-enable the search then finds the device.
I'm not knowledgeable on how this library interacts with Android native but it looks like it's missing an event handler or similar for some type of sleep state. Android does rate limit how much hunting an app can do if it keeps at it which may be part of the problem. I'm just theorising though.
https://pastebin.com/eZjzhjBi
https://pastebin.com/6jNAX6UN
https://pastebin.com/tcgiMzrw
This should be enough. Change service and characteristics to a known device and stick a console.log in the Promise so you know when it's connected (I've stripped all my business logic out of it).
Finally I had some time to double check what is going on. Using release 2.0.1 and below code:
manager.startDeviceScan(
null,
null,
(error, device) => {
if (error) {
console.log('error', JSON.stringify(error));
} else if (device) {
if (device.localName === 'MyTestDevice') {
manager.stopDeviceScan();
localScanChange(false);
device
.isConnected()
.then(bool => console.log('isConnected0', bool));
manager
.isDeviceConnected(device.id)
.then(bool => console.log('isConnectedM0', bool));
device
.connect()
.then(() => {
device
.isConnected()
.then(bool => console.log('isConnected1', bool));
manager
.isDeviceConnected(device.id)
.then(bool => console.log('isConnectedM1', bool));
return device.discoverAllServicesAndCharacteristics();
})
.then(() => {
setConnectedDevice(device);
console.log('connected');
});
const sub = device.onDisconnected((error, device) => {
console.log('disconnected');
setConnectedDevice(null);
sub.remove();
});
}
}
},
);
I got a seemingly valid logs on Android:
[Tue Jun 30 2020 16:15:14.274] LOG isConnected0 false
[Tue Jun 30 2020 16:15:14.277] LOG isConnectedM0 false
[Tue Jun 30 2020 16:15:14.839] LOG isConnected1 true
[Tue Jun 30 2020 16:15:14.850] LOG isConnectedM1 true
[Tue Jun 30 2020 16:15:15.430] LOG connected
And iOS:
2020-06-30 16:23:28.148 [info][tid:com.facebook.react.JavaScript] 'isConnected0', false
2020-06-30 16:23:28.148 [info][tid:com.facebook.react.JavaScript] 'isConnectedM0', false
2020-06-30 16:23:28.694 [info][tid:com.facebook.react.JavaScript] 'isConnected1', true
2020-06-30 16:23:28.695 [info][tid:com.facebook.react.JavaScript] 'isConnectedM0', true
2020-06-30 16:23:29.290 [info][tid:com.facebook.react.JavaScript] connected
Could you retest with 2.0.1?
It doesn't occur on a Android 9 device (I was using a 6 device). I'll investigate more but it might be down to poor hardware/software implementation on the device side.
I think that your example is working because you call the isConnected() function before you assign the connected device with setConnectedDevice() function.
Can you try to call the isConnected() function with your connectedDevice outside this listener ? It should return only false.
I remark that the device._manager._uniqueId is not the same before and after you call the setConnecteDevice() function. You should log the device object in each case to notice that.
Here is an example.
const [connectedDevice, setConnectedDevice] = useState(null);
const scan = () => {
manager.startDeviceScan(
null,
null,
(error, device) => {
if (error) {
console.log('error', JSON.stringify(error));
} else if (device) {
if (device.localName === 'MyTestDevice') {
manager.stopDeviceScan();
localScanChange(false);
device
.isConnected()
.then(bool => console.log('isConnected0', bool, device));
manager
.isDeviceConnected(device.id)
.then(bool => console.log('isConnectedM0', bool, device));
device
.connect()
.then(() => {
device
.isConnected()
.then(bool => console.log('isConnected1', bool, device));
manager
.isDeviceConnected(device.id)
.then(bool => console.log('isConnectedM1', bool, device));
return device.discoverAllServicesAndCharacteristics();
})
.then(() => {
setConnectedDevice(device);
console.log('connected');
});
const sub = device.onDisconnected((error, device) => {
console.log('disconnected');
setConnectedDevice(null);
sub.remove();
});
}
}
},
);
}
const checkIfDeviceConnected = () => {
// RETURN FALSE EACH TIME EVEN IF THE DEVICE IS CONNECTED !
connectedDevice.isConnected().then(bool => console.log('isConnected', bool, device));
}
@dariuszseweryn Any news about this issue ?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I am facing the same issue. Are there any updates on this?
It should not be closed if it is not resolved.
Can you please re-open this issue?
My guess is that you are misusing React Hooks. Could you edit to contain information according to the issue template? I am especially interested in native logs.
I'm having the same issue...
Our workaround is to try to read an arbitrary characteristic from a device with try/catch and if it throws it's not connected.
Could anyone prepare and post a full (but minimal) example of code that exhibits this behaviour? Complete example including creation of the manager.
Most helpful comment
I think that your example is working because you call the
isConnected()function before you assign the connected device withsetConnectedDevice()function.Can you try to call the
isConnected()function with your connectedDevice outside this listener ? It should return only false.I remark that the
device._manager._uniqueIdis not the same before and after you call thesetConnecteDevice()function. You should log the device object in each case to notice that.Here is an example.