From b868035bf59ed971418cac37da88a4853b1e3cd2 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 18 Nov 2023 22:21:15 +0100 Subject: [PATCH] Allow discovering on bluetooth adapter plugged in at runtime --- .../bluetoothdiscoveryreplyimplementation.cpp | 6 + .../bluetoothdiscoveryreplyimplementation.h | 1 + ...bluetoothlowenergydeviceimplementation.cpp | 45 ++--- .../bluetoothlowenergydeviceimplementation.h | 5 +- ...luetoothlowenergymanagerimplementation.cpp | 167 ++++-------------- .../bluetoothlowenergymanagerimplementation.h | 20 +-- .../nymeabluetoothagent.cpp | 7 + .../bluetoothlowenergy/nymeabluetoothagent.h | 16 ++ libnymea-core/libnymea-core.pro | 2 + .../bluetoothlowenergymanager.h | 1 + 10 files changed, 92 insertions(+), 178 deletions(-) create mode 100644 libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.cpp create mode 100644 libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.h diff --git a/libnymea-core/hardware/bluetoothlowenergy/bluetoothdiscoveryreplyimplementation.cpp b/libnymea-core/hardware/bluetoothlowenergy/bluetoothdiscoveryreplyimplementation.cpp index 10d00e22..6baf0531 100644 --- a/libnymea-core/hardware/bluetoothlowenergy/bluetoothdiscoveryreplyimplementation.cpp +++ b/libnymea-core/hardware/bluetoothlowenergy/bluetoothdiscoveryreplyimplementation.cpp @@ -68,11 +68,17 @@ void BluetoothDiscoveryReplyImplementation::setDiscoveredDevices(const QList &discoveredDevices); + void addDiscoveredDevice(const QBluetoothDeviceInfo &info); void setFinished(); }; diff --git a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.cpp b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.cpp index 3af752ab..adde47b4 100644 --- a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.cpp +++ b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.cpp @@ -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 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); } diff --git a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.h b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.h index ad602f13..0ece46a2 100644 --- a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.h +++ b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.h @@ -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(); diff --git a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.cpp b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.cpp index ac01df36..e547b32b 100644 --- a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.cpp +++ b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.cpp @@ -54,85 +54,58 @@ BluetoothLowEnergyManagerImplementation::BluetoothLowEnergyManagerImplementation return; } - // Check which bluetooth adapter are available - QList 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 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(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 bluetoothDevice, m_devices) { - bluetoothDevice->setEnabled(true); - } - qCDebug(dcBluetooth()) << "Hardware resource enabled."; - return true; -} - -bool BluetoothLowEnergyManagerImplementation::disable() -{ - foreach (QPointer bluetoothDevice, m_devices) { - bluetoothDevice->setEnabled(false); - } - qCDebug(dcBluetooth()) << "Hardware resource disabled."; - return true; -} - } diff --git a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h index b4ee2cc3..9e30c9e5 100644 --- a/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h +++ b/libnymea-core/hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h @@ -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> m_devices; bool m_available = false; bool m_enabled = false; - QList m_bluetoothDiscoveryAgents; - QList m_discoveredDevices; QPointer 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(); - }; } diff --git a/libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.cpp b/libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.cpp new file mode 100644 index 00000000..1139e0ea --- /dev/null +++ b/libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.cpp @@ -0,0 +1,7 @@ +#include "nymeabluetoothagent.h" + +NymeaBluetoothAgent::NymeaBluetoothAgent(QObject *parent) + : QObject{parent} +{ + +} diff --git a/libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.h b/libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.h new file mode 100644 index 00000000..0aad0f55 --- /dev/null +++ b/libnymea-core/hardware/bluetoothlowenergy/nymeabluetoothagent.h @@ -0,0 +1,16 @@ +#ifndef NYMEABLUETOOTHAGENT_H +#define NYMEABLUETOOTHAGENT_H + +#include + +class NymeaBluetoothAgent : public QObject +{ + Q_OBJECT +public: + explicit NymeaBluetoothAgent(QObject *parent = nullptr); + +signals: + +}; + +#endif // NYMEABLUETOOTHAGENT_H diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index b003abb6..7b7800a0 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -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 \ diff --git a/libnymea/hardware/bluetoothlowenergy/bluetoothlowenergymanager.h b/libnymea/hardware/bluetoothlowenergy/bluetoothlowenergymanager.h index f681f6eb..d6b31a71 100644 --- a/libnymea/hardware/bluetoothlowenergy/bluetoothlowenergymanager.h +++ b/libnymea/hardware/bluetoothlowenergy/bluetoothlowenergymanager.h @@ -53,6 +53,7 @@ public: explicit BluetoothLowEnergyManager(QObject *parent = nullptr); virtual ~BluetoothLowEnergyManager() = default; + virtual BluetoothDiscoveryReply *discoverDevices(int interval = 5000) = 0; // Bluetooth device registration methods