mirror of https://github.com/nymea/nymea.git
Merge PR #657: Add support for Bluetooth pairing during thing setup
commit
89615140eb
|
|
@ -50,7 +50,7 @@ BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyError BluetoothDis
|
|||
return m_error;
|
||||
}
|
||||
|
||||
QList<QBluetoothDeviceInfo> BluetoothDiscoveryReplyImplementation::discoveredDevices() const
|
||||
QList<QPair<QBluetoothDeviceInfo, QBluetoothHostInfo>> BluetoothDiscoveryReplyImplementation::discoveredDevices() const
|
||||
{
|
||||
return m_discoveredDevices;
|
||||
}
|
||||
|
|
@ -63,16 +63,22 @@ void BluetoothDiscoveryReplyImplementation::setError(const BluetoothDiscoveryRep
|
|||
}
|
||||
}
|
||||
|
||||
void BluetoothDiscoveryReplyImplementation::setDiscoveredDevices(const QList<QBluetoothDeviceInfo> &discoveredDevices)
|
||||
void BluetoothDiscoveryReplyImplementation::setDiscoveredDevices(const QList<QPair<QBluetoothDeviceInfo, QBluetoothHostInfo>> &discoveredDevices)
|
||||
{
|
||||
m_discoveredDevices = discoveredDevices;
|
||||
}
|
||||
|
||||
void BluetoothDiscoveryReplyImplementation::addDiscoveredDevice(const QBluetoothDeviceInfo &info, const QBluetoothHostInfo &hostInfo)
|
||||
{
|
||||
m_discoveredDevices.append(qMakePair<QBluetoothDeviceInfo, QBluetoothHostInfo>(info, hostInfo));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QBluetoothDeviceInfo>
|
||||
#include <QBluetoothLocalDevice>
|
||||
|
||||
#include "hardware/bluetoothlowenergy/bluetoothdiscoveryreply.h"
|
||||
|
||||
|
|
@ -49,15 +50,16 @@ public:
|
|||
|
||||
bool isFinished() const override;
|
||||
BluetoothDiscoveryReplyError error() const override;
|
||||
QList<QBluetoothDeviceInfo> discoveredDevices() const override;
|
||||
QList<QPair<QBluetoothDeviceInfo, QBluetoothHostInfo> > discoveredDevices() const override;
|
||||
|
||||
private:
|
||||
bool m_finished = false;
|
||||
BluetoothDiscoveryReplyError m_error = BluetoothDiscoveryReplyErrorNoError;
|
||||
QList<QBluetoothDeviceInfo> m_discoveredDevices;
|
||||
QList<QPair<QBluetoothDeviceInfo, QBluetoothHostInfo>> m_discoveredDevices;
|
||||
|
||||
void setError(const BluetoothDiscoveryReplyError &error);
|
||||
void setDiscoveredDevices(const QList<QBluetoothDeviceInfo> &discoveredDevices);
|
||||
void setDiscoveredDevices(const QList<QPair<QBluetoothDeviceInfo, QBluetoothHostInfo> > &discoveredDevices);
|
||||
void addDiscoveredDevice(const QBluetoothDeviceInfo &info, const QBluetoothHostInfo &hostInfo);
|
||||
void setFinished();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,7 +150,6 @@ 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();
|
||||
|
||||
emit errorOccurred(error);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -30,10 +30,13 @@
|
|||
|
||||
#include "bluetoothlowenergymanagerimplementation.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "nymeabluetoothagent.h"
|
||||
#include "bluetoothpairingjobimplementation.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusInterface>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
|
|
@ -54,85 +57,114 @@ 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;
|
||||
}
|
||||
m_agent = new NymeaBluetoothAgent(this);
|
||||
|
||||
// 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()) << "Enabling 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);
|
||||
if (QBluetoothLocalDevice::allDevices().isEmpty()) {
|
||||
qCWarning(dcBluetooth()) << "No Bluetooth adapter available.";
|
||||
reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorNotAvailable);
|
||||
reply->setFinished();
|
||||
return reply.data();
|
||||
return reply;
|
||||
}
|
||||
|
||||
m_currentReply = reply;
|
||||
m_discoveredDevices.clear();
|
||||
// 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, hostInfo);
|
||||
}
|
||||
});
|
||||
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, reply, [=](){
|
||||
qCDebug(dcBluetooth()) << "Discovery finished";
|
||||
reply->setFinished();
|
||||
});
|
||||
// connect(discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)));
|
||||
|
||||
// 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;
|
||||
return reply;
|
||||
}
|
||||
|
||||
BluetoothPairingJob *BluetoothLowEnergyManagerImplementation::pairDevice(const QBluetoothAddress &device, const QBluetoothAddress &adapter)
|
||||
{
|
||||
qCDebug(dcBluetooth()) << "pairDevice";
|
||||
BluetoothPairingJobImplementation *job = new BluetoothPairingJobImplementation(m_agent, device, this);
|
||||
|
||||
QBluetoothLocalDevice *localDevice = nullptr;
|
||||
if (adapter.isNull()) {
|
||||
localDevice = new QBluetoothLocalDevice(this);
|
||||
} else {
|
||||
localDevice = new QBluetoothLocalDevice(adapter, this);
|
||||
}
|
||||
|
||||
if (finalInterval <= 0) {
|
||||
qCWarning(dcBluetooth()) << "Discovery interval out of range. Reset to 5 seconds.";
|
||||
finalInterval = 5000;
|
||||
if (!localDevice->isValid()) {
|
||||
qCWarning(dcBluetooth()) << "Local device" << adapter.toString() << "is not valid";
|
||||
job->finish(false);
|
||||
return job;
|
||||
}
|
||||
localDevice->requestPairing(device, QBluetoothLocalDevice::AuthorizedPaired);
|
||||
connect(localDevice, &QBluetoothLocalDevice::error, job, [=](QBluetoothLocalDevice::Error error){
|
||||
qCDebug(dcBluetooth()) << "Pairing error" << error;
|
||||
job->finish(false);
|
||||
localDevice->deleteLater();
|
||||
});
|
||||
connect(localDevice, &QBluetoothLocalDevice::pairingFinished, job, [=](const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing){
|
||||
qCDebug(dcBluetooth()) << "Pairing finished" << address.toString() << pairing;
|
||||
job->finish(true);
|
||||
localDevice->deleteLater();
|
||||
});
|
||||
return job;
|
||||
}
|
||||
|
||||
void BluetoothLowEnergyManagerImplementation::unpairDevice(const QBluetoothAddress &device, const QBluetoothAddress &adapter)
|
||||
{
|
||||
QBluetoothLocalDevice *localDevice = nullptr;
|
||||
if (adapter.isNull()) {
|
||||
localDevice = new QBluetoothLocalDevice(this);
|
||||
} else {
|
||||
localDevice = new QBluetoothLocalDevice(adapter, this);
|
||||
}
|
||||
|
||||
m_timer->start(finalInterval);
|
||||
return reply.data();
|
||||
if (!localDevice->isValid()) {
|
||||
qCWarning(dcBluetooth()) << "Local device" << adapter.toString() << "is not valid";
|
||||
return;
|
||||
}
|
||||
localDevice->requestPairing(device, QBluetoothLocalDevice::Unpaired);
|
||||
}
|
||||
|
||||
BluetoothLowEnergyDevice *BluetoothLowEnergyManagerImplementation::registerDevice(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType)
|
||||
|
|
@ -182,17 +214,17 @@ void BluetoothLowEnergyManagerImplementation::setEnabled(bool enabled)
|
|||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
foreach (QBluetoothHostInfo info, QBluetoothLocalDevice::allDevices()) {
|
||||
QBluetoothLocalDevice localDevice(info.address());
|
||||
if (enabled) {
|
||||
success = enable();
|
||||
localDevice.setHostMode(QBluetoothLocalDevice::HostConnectable);
|
||||
} else {
|
||||
success = disable();
|
||||
localDevice.setHostMode(QBluetoothLocalDevice::HostPoweredOff);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
m_enabled = enabled;
|
||||
emit enabledChanged(m_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothLowEnergyManagerImplementation::onReconnectTimeout()
|
||||
|
|
@ -205,72 +237,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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
namespace nymeaserver {
|
||||
|
||||
class NymeaBluetoothAgent;
|
||||
|
||||
class BluetoothLowEnergyManagerImplementation : public BluetoothLowEnergyManager
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -55,9 +57,11 @@ 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
|
||||
BluetoothPairingJob *pairDevice(const QBluetoothAddress &device, const QBluetoothAddress &adapter) override;
|
||||
void unpairDevice(const QBluetoothAddress &device, const QBluetoothAddress &adapter) override;
|
||||
BluetoothLowEnergyDevice *registerDevice(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType = QLowEnergyController::RandomAddress) override;
|
||||
void unregisterDevice(BluetoothLowEnergyDevice *bluetoothDevice) override;
|
||||
|
||||
|
|
@ -67,27 +71,18 @@ public:
|
|||
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;
|
||||
NymeaBluetoothAgent *m_agent = nullptr;
|
||||
|
||||
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();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
#include "bluetoothpairingjobimplementation.h"
|
||||
|
||||
#include "nymeabluetoothagent.h"
|
||||
|
||||
namespace nymeaserver
|
||||
{
|
||||
|
||||
BluetoothPairingJobImplementation::BluetoothPairingJobImplementation(NymeaBluetoothAgent *agent, const QBluetoothAddress &address, QObject *parent)
|
||||
: BluetoothPairingJob{address, parent},
|
||||
m_agent{agent},
|
||||
m_address{address}
|
||||
{
|
||||
connect(m_agent, &NymeaBluetoothAgent::passKeyRequested, this, [address, this](const QBluetoothAddress &addr){
|
||||
if (address != addr) {
|
||||
// Not for us...
|
||||
return;
|
||||
}
|
||||
emit passKeyRequested();
|
||||
});
|
||||
connect(m_agent, &NymeaBluetoothAgent::displayPinCode, this, [address, this](const QBluetoothAddress &addr, const QString &pinCode){
|
||||
if (address != addr) {
|
||||
// Not for us...
|
||||
return;
|
||||
}
|
||||
emit displayPinCode(pinCode);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
bool BluetoothPairingJobImplementation::isFinished() const
|
||||
{
|
||||
return m_finished;
|
||||
}
|
||||
|
||||
bool BluetoothPairingJobImplementation::success() const
|
||||
{
|
||||
return m_success;
|
||||
}
|
||||
|
||||
void BluetoothPairingJobImplementation::passKeyEntered(const QString passKey)
|
||||
{
|
||||
m_agent->passKeyEntered(m_address, passKey);
|
||||
}
|
||||
|
||||
void BluetoothPairingJobImplementation::finish(bool success)
|
||||
{
|
||||
m_finished = true;
|
||||
m_success = success;
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, Q_ARG(bool, success));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef BLUETOOTHPAIRINGJOBIMPLEMENTATION_H
|
||||
#define BLUETOOTHPAIRINGJOBIMPLEMENTATION_H
|
||||
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergymanager.h"
|
||||
#include <QObject>
|
||||
|
||||
namespace nymeaserver
|
||||
{
|
||||
|
||||
class NymeaBluetoothAgent;
|
||||
class BluetoothLowEnergyManagerImplementation;
|
||||
|
||||
class BluetoothPairingJobImplementation : public BluetoothPairingJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BluetoothPairingJobImplementation(NymeaBluetoothAgent *agent, const QBluetoothAddress &address, QObject *parent = nullptr);
|
||||
|
||||
bool isFinished() const override;
|
||||
bool success() const override;
|
||||
|
||||
void passKeyEntered(const QString passKey) override;
|
||||
|
||||
private:
|
||||
friend BluetoothLowEnergyManagerImplementation;
|
||||
void finish(bool success);
|
||||
|
||||
private:
|
||||
bool m_finished = false;
|
||||
bool m_success = false;
|
||||
|
||||
NymeaBluetoothAgent *m_agent = nullptr;
|
||||
QBluetoothAddress m_address;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif // BLUETOOTHPAIRINGJOBIMPLEMENTATION_H
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
#include "nymeabluetoothagent.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcBluetooth)
|
||||
|
||||
namespace nymeaserver
|
||||
{
|
||||
|
||||
NymeaBluetoothAgentAdapter::NymeaBluetoothAgentAdapter(NymeaBluetoothAgent *agent, QObject *parent):
|
||||
QObject{parent},
|
||||
m_agent{agent}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString NymeaBluetoothAgentAdapter::RequestPinCode(const QDBusObjectPath &device, const QDBusMessage &message)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "RequestPinCode" << device.path() << message.arguments();
|
||||
message.setDelayedReply(true);
|
||||
m_agent->onRequestPassKey(device, message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::DisplayPinCode(const QDBusObjectPath &device, const QString &pinCode)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "DisplayPinCode" << device.path() << pinCode;
|
||||
m_agent->onDisplayPinCode(device, pinCode);
|
||||
}
|
||||
|
||||
quint32 NymeaBluetoothAgentAdapter::RequestPasskey(const QDBusObjectPath &device, const QDBusMessage &message)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "RequestPasskey" << device.path() << message.arguments();
|
||||
message.setDelayedReply(true);
|
||||
m_agent->onRequestPassKey(device, message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::DisplayPasskey(const QDBusObjectPath &device, quint32 passKey, quint16 entered)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "DisplayPasskey" << device.path() << passKey << entered;
|
||||
// Not using "entered" value which would update whenever the user enters a (wrong) pin on the other end...
|
||||
m_agent->onDisplayPinCode(device, QString("%1").arg(passKey, 6, QChar('0')));
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::RequestConfirmation(const QDBusObjectPath &device, quint32 passKey, const QDBusMessage &message)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "RequestConfirmation" << device.path() << passKey << message.arguments();
|
||||
// TODO: Not implemented
|
||||
qCWarning(dcBluetooth()) << "Request confirmation pairing mechanism is not implemented.";
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::RequestAuthorization(const QDBusObjectPath &device, const QDBusMessage &message)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "RequestAuthorization" << device.path() << message.arguments();
|
||||
// TODO: Not implemented
|
||||
qCWarning(dcBluetooth()) << "RequestAuthorization mechanism is not implemented.";
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::AuthorizeService(const QDBusObjectPath &device, const QString &uuid, const QDBusMessage &message)
|
||||
{
|
||||
qCDebug(dcBluetooth) << "AuthorizeService" << device.path() << uuid << message.arguments();
|
||||
// TODO: Not implemented
|
||||
qCWarning(dcBluetooth()) << "AuthorizeService mechanism is not implemented.";
|
||||
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::Cancel()
|
||||
{
|
||||
qCDebug(dcBluetooth()) << "Cancel called on bluetooth agent";
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgentAdapter::Release()
|
||||
{
|
||||
qCDebug(dcBluetooth()) << "Release called on bluetooth agent";
|
||||
}
|
||||
|
||||
NymeaBluetoothAgent::NymeaBluetoothAgent(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
m_adapter = new NymeaBluetoothAgentAdapter(this);
|
||||
bool success = QDBusConnection::systemBus().registerObject("/nymea/bluetoothagent", m_adapter, QDBusConnection::ExportScriptableContents);
|
||||
qCInfo(dcBluetooth) << "Registered Bluetooth pairing agent" << success;
|
||||
|
||||
QDBusMessage message = QDBusMessage::createMethodCall("org.bluez", "/org/bluez", "org.bluez.AgentManager1", "RegisterAgent");
|
||||
message << QVariant::fromValue(QDBusObjectPath("/nymea/bluetoothagent"));
|
||||
message << "KeyboardDisplay";
|
||||
QDBusMessage registerReply = QDBusConnection::systemBus().call(message);
|
||||
if (!registerReply.errorName().isEmpty()) {
|
||||
qCWarning(dcBluetooth()) << "Error registering pairing agent:" << registerReply.errorMessage();
|
||||
} else {
|
||||
qCDebug(dcBluetooth()) << "Pairing agent registered.";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgent::passKeyEntered(const QBluetoothAddress &address, const QString passKey)
|
||||
{
|
||||
if (!m_pendingPairings.contains(address.toString())) {
|
||||
qCWarning(dcBluetooth()) << "No ongoing pairing process for" << address.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCInfo(dcBluetooth()) << "Providing passkey to" << address.toString() << passKey;
|
||||
QDBusMessage message = m_pendingPairings.take(address.toString());
|
||||
message << static_cast<quint32>(passKey.toUInt());
|
||||
QDBusConnection::systemBus().send(message);
|
||||
}
|
||||
|
||||
QBluetoothAddress NymeaBluetoothAgent::deviceForPath(const QDBusObjectPath &path)
|
||||
{
|
||||
// qdbus --system org.bluez /org/bluez/hci0/dev_00_1A_22_0B_12_EB org.freedesktop.DBus.Properties.Get org.bluez.Device1 Address
|
||||
|
||||
QDBusMessage message = QDBusMessage::createMethodCall("org.bluez", path.path(), "org.freedesktop.DBus.Properties", "Get");
|
||||
message << "org.bluez.Device1" << "Address";
|
||||
QDBusMessage reply = QDBusConnection::systemBus().call(message);
|
||||
if (!reply.errorName().isEmpty()) {
|
||||
qCWarning(dcBluetooth()) << "Error reading Address property for" << path.path() << reply.errorMessage();
|
||||
return QBluetoothAddress();
|
||||
}
|
||||
|
||||
if (reply.arguments().count() != 1) {
|
||||
qCWarning(dcBluetooth) << "Read property reply received an unexpected argument count";
|
||||
return QBluetoothAddress();
|
||||
}
|
||||
|
||||
return QBluetoothAddress(reply.arguments().at(0).value<QDBusVariant>().variant().toString());
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgent::onRequestPassKey(const QDBusObjectPath &path, const QDBusMessage &message)
|
||||
{
|
||||
QBluetoothAddress address = deviceForPath(path);
|
||||
qCDebug(dcBluetooth()) << "RequestPassKey" << path.path() << address;
|
||||
m_pendingPairings[address.toString()] = message.createReply();
|
||||
emit passKeyRequested(address);
|
||||
}
|
||||
|
||||
void NymeaBluetoothAgent::onDisplayPinCode(const QDBusObjectPath &path, const QString &pinCode)
|
||||
{
|
||||
QBluetoothAddress address = deviceForPath(path);
|
||||
qCDebug(dcBluetooth()) << "RequestPassKey" << path.path() << address;
|
||||
emit displayPinCode(address, pinCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef NYMEABLUETOOTHAGENT_H
|
||||
#define NYMEABLUETOOTHAGENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDBusObjectPath>
|
||||
#include <QDBusMessage>
|
||||
#include <QBluetoothAddress>
|
||||
|
||||
namespace nymeaserver
|
||||
{
|
||||
|
||||
class NymeaBluetoothAgent;
|
||||
|
||||
class NymeaBluetoothAgentAdapter: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.bluez.Agent1")
|
||||
|
||||
public:
|
||||
explicit NymeaBluetoothAgentAdapter(NymeaBluetoothAgent *agent, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
Q_SCRIPTABLE QString RequestPinCode(const QDBusObjectPath &device, const QDBusMessage &message);
|
||||
Q_SCRIPTABLE void DisplayPinCode(const QDBusObjectPath &device, const QString &pinCode);
|
||||
Q_SCRIPTABLE quint32 RequestPasskey(const QDBusObjectPath &device, const QDBusMessage &message);
|
||||
Q_SCRIPTABLE void DisplayPasskey(const QDBusObjectPath &device, quint32 passKey, quint16 entered);
|
||||
|
||||
Q_SCRIPTABLE void RequestConfirmation(const QDBusObjectPath &device, quint32 passKey, const QDBusMessage &message);
|
||||
Q_SCRIPTABLE void RequestAuthorization(const QDBusObjectPath &device, const QDBusMessage &message);
|
||||
Q_SCRIPTABLE void AuthorizeService(const QDBusObjectPath &device, const QString &uuid, const QDBusMessage &message);
|
||||
|
||||
Q_SCRIPTABLE void Cancel();
|
||||
Q_SCRIPTABLE void Release();
|
||||
|
||||
private:
|
||||
NymeaBluetoothAgent *m_agent = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class NymeaBluetoothAgent : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NymeaBluetoothAgent(QObject *parent = nullptr);
|
||||
|
||||
void passKeyEntered(const QBluetoothAddress &address, const QString passKey);
|
||||
|
||||
signals:
|
||||
void passKeyRequested(const QBluetoothAddress &address);
|
||||
void displayPinCode(const QBluetoothAddress &address, const QString &pinCode);
|
||||
|
||||
private:
|
||||
friend class NymeaBluetoothAgentAdapter;
|
||||
QBluetoothAddress deviceForPath(const QDBusObjectPath &path);
|
||||
void onRequestPassKey(const QDBusObjectPath &path, const QDBusMessage &message);
|
||||
void onDisplayPinCode(const QDBusObjectPath &path, const QString &pinCode);
|
||||
NymeaBluetoothAgentAdapter *m_adapter = nullptr;
|
||||
QHash<QString, QDBusMessage> m_pendingPairings;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NYMEABLUETOOTHAGENT_H
|
||||
|
|
@ -58,6 +58,8 @@ RESOURCES += $$top_srcdir/icons.qrc \
|
|||
$$top_srcdir/data/debug-interface/debug-interface.qrc
|
||||
|
||||
HEADERS += nymeacore.h \
|
||||
hardware/bluetoothlowenergy/bluetoothpairingjobimplementation.h \
|
||||
hardware/bluetoothlowenergy/nymeabluetoothagent.h \
|
||||
hardware/network/macaddressdatabasereplyimpl.h \
|
||||
hardware/serialport/serialportmonitor.h \
|
||||
hardware/zwave/zwavehardwareresourceimplementation.h \
|
||||
|
|
@ -165,6 +167,8 @@ HEADERS += nymeacore.h \
|
|||
|
||||
|
||||
SOURCES += nymeacore.cpp \
|
||||
hardware/bluetoothlowenergy/bluetoothpairingjobimplementation.cpp \
|
||||
hardware/bluetoothlowenergy/nymeabluetoothagent.cpp \
|
||||
hardware/network/macaddressdatabasereplyimpl.cpp \
|
||||
hardware/serialport/serialportmonitor.cpp \
|
||||
hardware/zwave/zwavehardwareresourceimplementation.cpp \
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QBluetoothDeviceInfo>
|
||||
#include <QBluetoothHostInfo>
|
||||
|
||||
#include "libnymea.h"
|
||||
|
||||
|
|
@ -54,7 +55,7 @@ public:
|
|||
|
||||
virtual bool isFinished() const = 0;
|
||||
virtual BluetoothDiscoveryReplyError error() const = 0;
|
||||
virtual QList<QBluetoothDeviceInfo> discoveredDevices() const = 0;
|
||||
virtual QList<QPair<QBluetoothDeviceInfo, QBluetoothHostInfo>> discoveredDevices() const = 0;
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ BluetoothLowEnergyManager::BluetoothLowEnergyManager(QObject *parent) :
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
/*! This method enables / disables this hardwareresource for all plugins. This method is available on the D-Bus. This can be useful if a Bluetooth LE server
|
||||
needs access to the hardware. By disabling the bluetooth support, nymea will not allow to use the hardware until it gets reenabled.
|
||||
*/
|
||||
|
|
@ -73,3 +74,16 @@ void BluetoothLowEnergyManager::EnableBluetooth(bool enabled)
|
|||
{
|
||||
setEnabled(enabled);
|
||||
}
|
||||
|
||||
BluetoothPairingJob::BluetoothPairingJob(const QBluetoothAddress &address, QObject *parent):
|
||||
QObject(parent),
|
||||
m_address(address)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QBluetoothAddress BluetoothPairingJob::address() const
|
||||
{
|
||||
return m_address;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,31 @@
|
|||
|
||||
#include "libnymea.h"
|
||||
|
||||
class NymeaBluetoothAgent;
|
||||
|
||||
class LIBNYMEA_EXPORT BluetoothPairingJob: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BluetoothPairingJob(const QBluetoothAddress &address, QObject *parent = nullptr);
|
||||
virtual ~BluetoothPairingJob() = default;
|
||||
|
||||
QBluetoothAddress address() const;
|
||||
|
||||
virtual bool isFinished() const = 0;
|
||||
virtual bool success() const = 0;
|
||||
|
||||
virtual void passKeyEntered(const QString passKey) = 0;
|
||||
|
||||
signals:
|
||||
void finished(bool success);
|
||||
void passKeyRequested();
|
||||
void displayPinCode(const QString &pinCode);
|
||||
|
||||
private:
|
||||
QBluetoothAddress m_address;
|
||||
};
|
||||
|
||||
class LIBNYMEA_EXPORT BluetoothLowEnergyManager : public HardwareResource
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -53,14 +78,18 @@ public:
|
|||
explicit BluetoothLowEnergyManager(QObject *parent = nullptr);
|
||||
virtual ~BluetoothLowEnergyManager() = default;
|
||||
|
||||
|
||||
virtual BluetoothDiscoveryReply *discoverDevices(int interval = 5000) = 0;
|
||||
|
||||
// Bluetooth device registration methods
|
||||
virtual BluetoothPairingJob *pairDevice(const QBluetoothAddress &device, const QBluetoothAddress &adapter) = 0;
|
||||
virtual void unpairDevice(const QBluetoothAddress &device, const QBluetoothAddress &adapter) = 0;
|
||||
virtual BluetoothLowEnergyDevice *registerDevice(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType = QLowEnergyController::RandomAddress) = 0;
|
||||
virtual void unregisterDevice(BluetoothLowEnergyDevice *bluetoothDevice) = 0;
|
||||
|
||||
public slots:
|
||||
Q_SCRIPTABLE void EnableBluetooth(bool enabled);
|
||||
|
||||
};
|
||||
|
||||
#endif // BLUETOOTHLOWENERGYMANAGER_H
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
|
|||
JSON_PROTOCOL_VERSION_MAJOR=8
|
||||
JSON_PROTOCOL_VERSION_MINOR=1
|
||||
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
|
||||
LIBNYMEA_API_VERSION_MAJOR=7
|
||||
LIBNYMEA_API_VERSION_MINOR=4
|
||||
LIBNYMEA_API_VERSION_MAJOR=8
|
||||
LIBNYMEA_API_VERSION_MINOR=0
|
||||
LIBNYMEA_API_VERSION_PATCH=0
|
||||
LIBNYMEA_API_VERSION="$${LIBNYMEA_API_VERSION_MAJOR}.$${LIBNYMEA_API_VERSION_MINOR}.$${LIBNYMEA_API_VERSION_PATCH}"
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <QLoggingCategory>
|
||||
#include <QObject>
|
||||
|
||||
extern "C" const QString libnymea_api_version() { return QString("7.4.0");}
|
||||
extern "C" const QString libnymea_api_version() { return QString("8.0.0");}
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcMock)
|
||||
Q_LOGGING_CATEGORY(dcMock, "Mock")
|
||||
|
|
|
|||
Loading…
Reference in New Issue