Allow discovering on bluetooth adapter plugged in at runtime

This commit is contained in:
Michael Zanetti 2023-11-18 22:21:15 +01:00
parent 4305c76471
commit b868035bf5
10 changed files with 92 additions and 178 deletions

View File

@ -68,11 +68,17 @@ void BluetoothDiscoveryReplyImplementation::setDiscoveredDevices(const QList<QBl
m_discoveredDevices = discoveredDevices;
}
void BluetoothDiscoveryReplyImplementation::addDiscoveredDevice(const QBluetoothDeviceInfo &info)
{
m_discoveredDevices.append(info);
}
void BluetoothDiscoveryReplyImplementation::setFinished()
{
m_finished = true;
// Note: this makes sure the finished signal will be processed in the next event loop
QTimer::singleShot(0, this, &BluetoothDiscoveryReplyImplementation::finished);
deleteLater();
}
}

View File

@ -58,6 +58,7 @@ private:
void setError(const BluetoothDiscoveryReplyError &error);
void setDiscoveredDevices(const QList<QBluetoothDeviceInfo> &discoveredDevices);
void addDiscoveredDevice(const QBluetoothDeviceInfo &info);
void setFinished();
};

View File

@ -33,6 +33,20 @@
namespace nymeaserver {
BluetoothLowEnergyDeviceImplementation::BluetoothLowEnergyDeviceImplementation(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType, QObject *parent) :
BluetoothLowEnergyDevice(parent),
m_deviceInfo(deviceInfo)
{
m_controller = new QLowEnergyController(address(), this);
m_controller->setRemoteAddressType(addressType);
connect(m_controller, &QLowEnergyController::connected, this, &BluetoothLowEnergyDeviceImplementation::onConnected);
connect(m_controller, &QLowEnergyController::disconnected, this, &BluetoothLowEnergyDeviceImplementation::onDisconnected);
connect(m_controller, &QLowEnergyController::discoveryFinished, this, &BluetoothLowEnergyDeviceImplementation::onServiceDiscoveryFinished);
connect(m_controller, &QLowEnergyController::stateChanged, this, &BluetoothLowEnergyDeviceImplementation::onStateChanged);
connect(m_controller, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(onDeviceError(QLowEnergyController::Error)));
}
QString BluetoothLowEnergyDeviceImplementation::name() const
{
return m_deviceInfo.name();
@ -48,21 +62,8 @@ QLowEnergyController *BluetoothLowEnergyDeviceImplementation::controller() const
return m_controller;
}
BluetoothLowEnergyDeviceImplementation::BluetoothLowEnergyDeviceImplementation(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType, QObject *parent) :
BluetoothLowEnergyDevice(parent),
m_deviceInfo(deviceInfo)
{
m_controller = new QLowEnergyController(address(), this);
m_controller->setRemoteAddressType(addressType);
connect(m_controller, &QLowEnergyController::connected, this, &BluetoothLowEnergyDeviceImplementation::onConnected);
connect(m_controller, &QLowEnergyController::disconnected, this, &BluetoothLowEnergyDeviceImplementation::onDisconnected);
connect(m_controller, &QLowEnergyController::discoveryFinished, this, &BluetoothLowEnergyDeviceImplementation::onServiceDiscoveryFinished);
connect(m_controller, &QLowEnergyController::stateChanged, this, &BluetoothLowEnergyDeviceImplementation::onStateChanged);
connect(m_controller, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(onDeviceError(QLowEnergyController::Error)));
}
void BluetoothLowEnergyDeviceImplementation::setConnected(const bool &connected)
void BluetoothLowEnergyDeviceImplementation::setConnected(bool connected)
{
if (m_connected != connected) {
m_connected = connected;
@ -71,19 +72,6 @@ void BluetoothLowEnergyDeviceImplementation::setConnected(const bool &connected)
}
}
void BluetoothLowEnergyDeviceImplementation::setEnabled(const bool &enabled)
{
m_enabled = enabled;
if (!m_enabled) {
m_controller->disconnectFromDevice();
} else {
if (m_autoConnecting) {
m_controller->connectToDevice();
}
}
}
void BluetoothLowEnergyDeviceImplementation::onConnected()
{
setConnected(true);
@ -162,8 +150,7 @@ QList<QBluetoothUuid> BluetoothLowEnergyDeviceImplementation::serviceUuids() con
void BluetoothLowEnergyDeviceImplementation::onDeviceError(const QLowEnergyController::Error &error)
{
if (connected())
qCWarning(dcBluetooth()) << "Device error:" << name() << address().toString() << ": " << error << m_controller->errorString();
qCWarning(dcBluetooth()) << "Device error:" << name() << address().toString() << ": " << error << m_controller->errorString();
emit errorOccurred(error);
}

View File

@ -75,10 +75,7 @@ private:
bool m_discovered = false;
bool m_enabled = true;
void setConnected(const bool &connected);
// Methods called from BluetoothLowEnergyManager
void setEnabled(const bool &enabled);
void setConnected(bool connected);
private slots:
void onConnected();

View File

@ -54,85 +54,58 @@ BluetoothLowEnergyManagerImplementation::BluetoothLowEnergyManagerImplementation
return;
}
// Check which bluetooth adapter are available
QList<QBluetoothHostInfo> bluetoothAdapters = QBluetoothLocalDevice::allDevices();
if (bluetoothAdapters.isEmpty()) {
qCWarning(dcBluetooth()) << "No bluetooth adapter found. Resource not available.";
return;
}
// Create a scanner for each adapter
foreach (const QBluetoothHostInfo &hostInfo, bluetoothAdapters) {
qCDebug(dcBluetooth()) << "Using adapter:" << hostInfo.name() << hostInfo.address().toString();
foreach (const QBluetoothHostInfo &hostInfo, QBluetoothLocalDevice::allDevices()) {
qCDebug(dcBluetooth()) << "Enalbing bluetooth adapter:" << hostInfo.name() << hostInfo.address().toString();
QBluetoothLocalDevice localDevice(hostInfo.address());
localDevice.powerOn();
localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
QBluetoothDeviceDiscoveryAgent *discoveryAgent = new QBluetoothDeviceDiscoveryAgent(hostInfo.address(), this);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothLowEnergyManagerImplementation::onDeviceDiscovered);
connect(discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)));
m_bluetoothDiscoveryAgents.append(discoveryAgent);
}
// Discovery timer, interval depends on discovery call
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
connect(m_timer, &QTimer::timeout, this, &BluetoothLowEnergyManagerImplementation::onDiscoveryTimeout);
// Reconnect timer
connect(m_reconnectTimer, &PluginTimer::timeout, this, &BluetoothLowEnergyManagerImplementation::onReconnectTimeout);
qCDebug(dcHardware()) << "-->" << name() << "created successfully.";
m_available = true;
}
BluetoothDiscoveryReply *BluetoothLowEnergyManagerImplementation::discoverDevices(int interval)
BluetoothDiscoveryReply *BluetoothLowEnergyManagerImplementation::discoverDevices(int timeout)
{
// Create the reply for this discovery request
QPointer<BluetoothDiscoveryReplyImplementation> reply = new BluetoothDiscoveryReplyImplementation(this);
BluetoothDiscoveryReplyImplementation *reply = new BluetoothDiscoveryReplyImplementation(this);
if (!available()) {
qCWarning(dcBluetooth()) << "is not avilable.";
reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorNotAvailable);
reply->setFinished();
return reply.data();
return reply;
}
if (!enabled()) {
qCWarning(dcBluetooth()) << "is not enabled.";
reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorNotEnabled);
reply->setFinished();
return reply.data();
return reply;
}
if (!m_currentReply.isNull()) {
qCWarning(dcBluetooth()) << "resource busy. There is already a discovery running.";
reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorBusy);
reply->setFinished();
return reply.data();
}
// Create a scanner for each adapter
foreach (const QBluetoothHostInfo &hostInfo, QBluetoothLocalDevice::allDevices()) {
qCDebug(dcBluetooth()) << "Starting discovery on adapter:" << hostInfo.name() << hostInfo.address().toString();
QBluetoothLocalDevice localDevice(hostInfo.address());
localDevice.powerOn();
localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
QBluetoothDeviceDiscoveryAgent *discoveryAgent = new QBluetoothDeviceDiscoveryAgent(hostInfo.address(), reply);
discoveryAgent->setLowEnergyDiscoveryTimeout(qMax(5000, qMin(30000, timeout)));
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, reply, [=](const QBluetoothDeviceInfo &info){
// Note: only show low energy devices
if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
qCDebug(dcBluetooth()) << "device discovered" << info.name() << info.address().toString();
reply->addDiscoveredDevice(info);
}
});
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, reply, [=](){
qCDebug(dcBluetooth()) << "Discovery finished";
reply->setFinished();
});
// connect(discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)));
m_currentReply = reply;
m_discoveredDevices.clear();
// Start discovery on all adapters
qCDebug(dcBluetooth()) << "Start bluetooth discovery";
foreach (QBluetoothDeviceDiscoveryAgent *discoveryAgent, m_bluetoothDiscoveryAgents) {
discoveryAgent->start();
}
// Prevent blocking the hardware resource from plugins
int finalInterval = interval;
if (finalInterval > 30000) {
qCWarning(dcBluetooth()) << "Discovery interval out of range. Reset to 30 seconds.";
finalInterval = 30000;
}
if (finalInterval <= 0) {
qCWarning(dcBluetooth()) << "Discovery interval out of range. Reset to 5 seconds.";
finalInterval = 5000;
}
m_timer->start(finalInterval);
return reply.data();
return reply;
}
BluetoothLowEnergyDevice *BluetoothLowEnergyManagerImplementation::registerDevice(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType)
@ -182,17 +155,17 @@ void BluetoothLowEnergyManagerImplementation::setEnabled(bool enabled)
return;
}
bool success = false;
if (enabled) {
success = enable();
} else {
success = disable();
foreach (QBluetoothHostInfo info, QBluetoothLocalDevice::allDevices()) {
QBluetoothLocalDevice localDevice(info.address());
if (enabled) {
localDevice.setHostMode(QBluetoothLocalDevice::HostConnectable);
} else {
localDevice.setHostMode(QBluetoothLocalDevice::HostPoweredOff);
}
}
if (success) {
m_enabled = enabled;
emit enabledChanged(m_enabled);
}
m_enabled = enabled;
emit enabledChanged(m_enabled);
}
void BluetoothLowEnergyManagerImplementation::onReconnectTimeout()
@ -205,72 +178,4 @@ void BluetoothLowEnergyManagerImplementation::onReconnectTimeout()
}
}
void BluetoothLowEnergyManagerImplementation::onDiscoveryTimeout()
{
// Stop discovery on all adapters
qCDebug(dcBluetooth()) << "Stop bluetooth discovery";
foreach (QBluetoothDeviceDiscoveryAgent *discoveryAgent, m_bluetoothDiscoveryAgents) {
discoveryAgent->stop();
}
qCDebug(dcBluetooth()) << "Discovery finished. Found" << m_discoveredDevices.count() << "bluetooth devices.";
if (m_currentReply.isNull()) {
qCWarning(dcBluetooth()) << "Reply does not exist any more. Please don't delete the reply before it has finished.";
m_currentReply.clear();
return;
}
m_currentReply->setError(BluetoothDiscoveryReply::BluetoothDiscoveryReplyErrorNoError);
m_currentReply->setDiscoveredDevices(m_discoveredDevices);
m_currentReply->setFinished();
m_currentReply.clear();
}
void BluetoothLowEnergyManagerImplementation::onDeviceDiscovered(const QBluetoothDeviceInfo &deviceInfo)
{
// Add the device to the list if not already added
bool alreadyAdded = false;
foreach (const QBluetoothDeviceInfo &device, m_discoveredDevices) {
if (device.address() == deviceInfo.address()) {
alreadyAdded = true;
}
}
// Note: only show low energy devices
if (!alreadyAdded && deviceInfo.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
qCDebug(dcBluetooth()) << "device discovered" << deviceInfo.name() << deviceInfo.address().toString();
m_discoveredDevices.append(deviceInfo);
}
}
void BluetoothLowEnergyManagerImplementation::onDiscoveryError(const QBluetoothDeviceDiscoveryAgent::Error &error)
{
QBluetoothDeviceDiscoveryAgent *discoveryAgent = static_cast<QBluetoothDeviceDiscoveryAgent *>(sender());
qCWarning(dcBluetooth()) << "Discovery error:" << error << discoveryAgent->errorString();
}
bool BluetoothLowEnergyManagerImplementation::enable()
{
if (!available()) {
qCWarning(dcBluetooth()) << "Bluetooth hardware not available. Cannot enable Hardware resource";
return false;
}
foreach (QPointer<BluetoothLowEnergyDeviceImplementation> bluetoothDevice, m_devices) {
bluetoothDevice->setEnabled(true);
}
qCDebug(dcBluetooth()) << "Hardware resource enabled.";
return true;
}
bool BluetoothLowEnergyManagerImplementation::disable()
{
foreach (QPointer<BluetoothLowEnergyDeviceImplementation> bluetoothDevice, m_devices) {
bluetoothDevice->setEnabled(false);
}
qCDebug(dcBluetooth()) << "Hardware resource disabled.";
return true;
}
}

View File

@ -55,7 +55,7 @@ class BluetoothLowEnergyManagerImplementation : public BluetoothLowEnergyManager
public:
explicit BluetoothLowEnergyManagerImplementation(PluginTimer *reconnectTimer, QObject *parent = nullptr);
BluetoothDiscoveryReply *discoverDevices(int interval = 5000) override;
BluetoothDiscoveryReply *discoverDevices(int timeout = 5000) override;
// Bluetooth device registration methods
BluetoothLowEnergyDevice *registerDevice(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType = QLowEnergyController::RandomAddress) override;
@ -64,31 +64,23 @@ public:
bool available() const override;
bool enabled() const override;
protected:
void setEnabled(bool enabled) override;
private slots:
void onReconnectTimeout();
private:
PluginTimer *m_reconnectTimer = nullptr;
QTimer *m_timer = nullptr;
QList<QPointer<BluetoothLowEnergyDeviceImplementation>> m_devices;
bool m_available = false;
bool m_enabled = false;
QList<QBluetoothDeviceDiscoveryAgent *> m_bluetoothDiscoveryAgents;
QList<QBluetoothDeviceInfo> m_discoveredDevices;
QPointer<BluetoothDiscoveryReplyImplementation> m_currentReply;
private slots:
void onReconnectTimeout();
void onDeviceDiscovered(const QBluetoothDeviceInfo &deviceInfo);
void onDiscoveryError(const QBluetoothDeviceDiscoveryAgent::Error &error);
void onDiscoveryTimeout();
public slots:
bool enable();
bool disable();
};
}

View File

@ -0,0 +1,7 @@
#include "nymeabluetoothagent.h"
NymeaBluetoothAgent::NymeaBluetoothAgent(QObject *parent)
: QObject{parent}
{
}

View File

@ -0,0 +1,16 @@
#ifndef NYMEABLUETOOTHAGENT_H
#define NYMEABLUETOOTHAGENT_H
#include <QObject>
class NymeaBluetoothAgent : public QObject
{
Q_OBJECT
public:
explicit NymeaBluetoothAgent(QObject *parent = nullptr);
signals:
};
#endif // NYMEABLUETOOTHAGENT_H

View File

@ -58,6 +58,7 @@ RESOURCES += $$top_srcdir/icons.qrc \
$$top_srcdir/data/debug-interface/debug-interface.qrc
HEADERS += nymeacore.h \
hardware/bluetoothlowenergy/nymeabluetoothagent.h \
hardware/network/macaddressdatabasereplyimpl.h \
hardware/serialport/serialportmonitor.h \
hardware/zwave/zwavehardwareresourceimplementation.h \
@ -165,6 +166,7 @@ HEADERS += nymeacore.h \
SOURCES += nymeacore.cpp \
hardware/bluetoothlowenergy/nymeabluetoothagent.cpp \
hardware/network/macaddressdatabasereplyimpl.cpp \
hardware/serialport/serialportmonitor.cpp \
hardware/zwave/zwavehardwareresourceimplementation.cpp \

View File

@ -53,6 +53,7 @@ public:
explicit BluetoothLowEnergyManager(QObject *parent = nullptr);
virtual ~BluetoothLowEnergyManager() = default;
virtual BluetoothDiscoveryReply *discoverDevices(int interval = 5000) = 0;
// Bluetooth device registration methods