diff --git a/debian/control b/debian/control
index 1726cb72..3cae4a8b 100644
--- a/debian/control
+++ b/debian/control
@@ -27,6 +27,7 @@ Architecture: any
Depends: libqt5network5,
libqt5gui5,
libqt5sql5,
+ libqt5bluetooth5,
libguh1 (= ${binary:Version}),
${shlibs:Depends},
${misc:Depends}
diff --git a/guh.pri b/guh.pri
index 97a8856f..1bafb986 100644
--- a/guh.pri
+++ b/guh.pri
@@ -14,6 +14,12 @@ QT+= network
QMAKE_CXXFLAGS += -Werror
CONFIG += c++11
+# Check for Bluetoot LE support (Qt >= 5.4.0)
+!contains(QT_VERSION, ^5\\.[0-3]\\..*) {
+ QT += bluetooth
+ DEFINES += BLUETOOTH_LE
+}
+
# Enable coverage option
coverage {
QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage -O0
diff --git a/guh.pro b/guh.pro
index 901a037f..5c2f0de6 100644
--- a/guh.pro
+++ b/guh.pro
@@ -12,6 +12,13 @@ SUBDIRS += libguh server plugins
message("Building guh tests disabled")
}
+# Bluetooth LE support
+contains(DEFINES, BLUETOOTH_LE) {
+ message("Bluetooth LE available (Qt $${QT_VERSION}).")
+} else {
+ message("Bluetooth LE not available (Qt $${QT_VERSION}).")
+}
+
server.depends = libguh plugins
plugins.depends = libguh
tests.depends = libguh
diff --git a/libguh/bluetooth/bluetoothscanner.cpp b/libguh/bluetooth/bluetoothscanner.cpp
new file mode 100644
index 00000000..a1ef27d3
--- /dev/null
+++ b/libguh/bluetooth/bluetoothscanner.cpp
@@ -0,0 +1,117 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This file is part of guh. *
+ * *
+ * Guh is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, version 2 of the License. *
+ * *
+ * Guh is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with guh. If not, see . *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "bluetoothscanner.h"
+
+BluetoothScanner::BluetoothScanner(QObject *parent) :
+ QObject(parent)
+{
+ m_timer = new QTimer(this);
+ m_timer->setSingleShot(true);
+ m_timer->setInterval(10000);
+ connect(m_timer, &QTimer::timeout, this, &BluetoothScanner::discoveryTimeout);
+}
+
+bool BluetoothScanner::isAvailable()
+{
+ //Using default Bluetooth adapter
+ QBluetoothLocalDevice localDevice;
+
+ // Check if Bluetooth is available on this device
+ if (!localDevice.isValid()) {
+ qWarning() << "ERROR: no bluetooth device found.";
+ m_available = false;
+ return false;
+ }
+
+ // Turn Bluetooth on
+ localDevice.powerOn();
+
+ // Make it visible to others
+ localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
+
+ // Get connected devices
+ QList remotes = localDevice.allDevices();
+ if (remotes.isEmpty()) {
+ qWarning() << "ERROR: no bluetooth host info found.";
+ m_available = false;
+ return false;
+ }
+
+ QBluetoothHostInfo hostInfo = remotes.first();
+
+ // Create a discovery agent and connect to its signals
+ m_discoveryAgent = new QBluetoothDeviceDiscoveryAgent(hostInfo.address(), this);
+
+ connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothScanner::deviceDiscovered);
+ connect(m_discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onError(QBluetoothDeviceDiscoveryAgent::Error)));
+
+ qDebug() << "--> Bluetooth discovery created successfully.";
+ m_available = true;
+ return true;
+}
+
+bool BluetoothScanner::isRunning()
+{
+ return m_discoveryAgent->isActive();
+}
+
+bool BluetoothScanner::discover(const PluginId &pluginId)
+{
+ if (m_available && !m_discoveryAgent->isActive()) {
+ m_pluginId = pluginId;
+ m_deviceInfos.clear();
+ m_discoveryAgent->start();
+ m_timer->start();
+ qDebug() << "Bluetooth discovery started...";
+ return true;
+ }
+ return false;
+}
+
+void BluetoothScanner::deviceDiscovered(const QBluetoothDeviceInfo &device)
+{
+ // check if this is LE device
+ bool bluetoothLE = false;
+ if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
+ bluetoothLE = true;
+ }
+ qDebug() << "Bluetooth device discovered:" << device.name() << device.address() << "LE:" << bluetoothLE;
+
+ m_deviceInfos.append(device);
+}
+
+void BluetoothScanner::onError(QBluetoothDeviceDiscoveryAgent::Error error)
+{
+ Q_UNUSED(error);
+ m_available = false;
+ if (m_timer->isActive()) {
+ m_timer->stop();
+ }
+ if (isRunning()) {
+ m_discoveryAgent->stop();
+ }
+ qWarning() << "ERROR: Bluetooth discovery:" << m_discoveryAgent->errorString();
+}
+
+void BluetoothScanner::discoveryTimeout()
+{
+ qDebug() << "Bluetooth discovery finished.";
+ m_discoveryAgent->stop();
+ emit bluetoothDiscoveryFinished(m_pluginId, m_deviceInfos);
+}
diff --git a/libguh/bluetooth/bluetoothscanner.h b/libguh/bluetooth/bluetoothscanner.h
new file mode 100644
index 00000000..1f081004
--- /dev/null
+++ b/libguh/bluetooth/bluetoothscanner.h
@@ -0,0 +1,59 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This file is part of guh. *
+ * *
+ * Guh is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, version 2 of the License. *
+ * *
+ * Guh is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with guh. If not, see . *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef BLUETOOTHLE_H
+#define BLUETOOTHLE_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "typeutils.h"
+
+class BluetoothScanner : public QObject
+{
+ Q_OBJECT
+public:
+ explicit BluetoothScanner(QObject *parent = 0);
+ bool isAvailable();
+ bool isRunning();
+ bool discover(const PluginId &pluginId);
+
+private:
+ QBluetoothDeviceDiscoveryAgent *m_discoveryAgent;
+ QList m_deviceInfos;
+ QTimer *m_timer;
+ bool m_available;
+ PluginId m_pluginId;
+
+signals:
+ void bluetoothDiscoveryFinished(const PluginId &pluginId, const QList &deviceInfos);
+
+private slots:
+ void deviceDiscovered(const QBluetoothDeviceInfo &device);
+ void onError(QBluetoothDeviceDiscoveryAgent::Error error);
+ void discoveryTimeout();
+};
+
+#endif // BLUETOOTHLE_H
diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp
index 125aed50..043be2e7 100644
--- a/libguh/devicemanager.cpp
+++ b/libguh/devicemanager.cpp
@@ -213,6 +213,22 @@ DeviceManager::DeviceManager(QObject *parent) :
m_networkManager = new NetworkManager(this);
connect(m_networkManager, &NetworkManager::replyReady, this, &DeviceManager::replyReady);
+
+ // UPnP discovery
+ m_upnpDiscovery = new UpnpDiscovery(this);
+ connect(m_upnpDiscovery, &UpnpDiscovery::discoveryFinished, this, &DeviceManager::upnpDiscoveryFinished);
+ connect(m_upnpDiscovery, &UpnpDiscovery::upnpNotify, this, &DeviceManager::upnpNotifyReceived);
+
+ // Bluetooth LE
+ #ifdef BLUETOOTH_LE
+ m_bluetoothScanner = new BluetoothScanner(this);
+ if (!m_bluetoothScanner->isAvailable()) {
+ delete m_bluetoothScanner;
+ m_bluetoothScanner = 0;
+ } else {
+ connect(m_bluetoothScanner, &BluetoothScanner::bluetoothDiscoveryFinished, this, &DeviceManager::bluetoothDiscoveryFinished);
+ }
+ #endif
}
/*! Destructor of the DeviceManager. Each loaded \l{DevicePlugin} will be deleted. */
@@ -1168,6 +1184,17 @@ void DeviceManager::upnpNotifyReceived(const QByteArray ¬ifyData)
}
}
+#ifdef BLUETOOTH_LE
+void DeviceManager::bluetoothDiscoveryFinished(const PluginId &pluginId, const QList &deviceInfos)
+{
+ foreach (DevicePlugin *devicePlugin, m_devicePlugins) {
+ if (devicePlugin->requiredHardware().testFlag(HardwareResourceBluetoothLE) && devicePlugin->pluginId() == pluginId) {
+ devicePlugin->bluetoothDiscoveryFinished(deviceInfos);
+ }
+ }
+}
+#endif
+
void DeviceManager::timerEvent()
{
foreach (DevicePlugin *plugin, m_pluginTimerUsers) {
diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h
index ec405e3d..3dbe44f1 100644
--- a/libguh/devicemanager.h
+++ b/libguh/devicemanager.h
@@ -32,6 +32,10 @@
#include "network/networkmanager.h"
+#ifdef BLUETOOTH_LE
+#include "bluetooth/bluetoothscanner.h"
+#endif
+
#include
#include
#include
@@ -51,7 +55,9 @@ public:
HardwareResourceRadio433 = 0x01,
HardwareResourceRadio868 = 0x02,
HardwareResourceTimer = 0x04,
- HardwareResourceNetworkManager = 0x08
+ HardwareResourceNetworkManager = 0x08,
+ HardwareResourceUpnpDisovery = 0x16,
+ HardwareResourceBluetoothLE = 0x32
};
Q_DECLARE_FLAGS(HardwareResources, HardwareResource)
@@ -146,6 +152,14 @@ private slots:
void radio433SignalReceived(QList rawData);
void replyReady(const PluginId &pluginId, QNetworkReply *reply);
+
+ void upnpDiscoveryFinished(const QList &deviceDescriptorList, const PluginId &pluginId);
+ void upnpNotifyReceived(const QByteArray ¬ifyData);
+
+ #ifdef BLUETOOTH_LE
+ void bluetoothDiscoveryFinished(const PluginId &pluginId, const QList &deviceInfos);
+ #endif
+
void timerEvent();
private:
@@ -172,6 +186,10 @@ private:
QList m_pluginTimerUsers;
NetworkManager *m_networkManager;
+ #ifdef BLUETOOTH_LE
+ BluetoothScanner *m_bluetoothScanner;
+ #endif
+
QHash > m_pairingsJustAdd;
QHash > m_pairingsDiscovery;
diff --git a/libguh/libguh.pro b/libguh/libguh.pro
index 44a12cc7..46c42db2 100644
--- a/libguh/libguh.pro
+++ b/libguh/libguh.pro
@@ -8,6 +8,11 @@ QT += network
target.path = /usr/lib
INSTALLS += target
+contains(DEFINES, BLUETOOTH_LE) {
+ SOURCES += bluetooth/bluetoothscanner.cpp
+ HEADERS += bluetooth/bluetoothscanner.h
+}
+
SOURCES += plugin/device.cpp \
plugin/deviceclass.cpp \
plugin/deviceplugin.cpp \
diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp
index d5843bf5..dd030733 100644
--- a/libguh/plugin/deviceplugin.cpp
+++ b/libguh/plugin/deviceplugin.cpp
@@ -649,6 +649,30 @@ QNetworkReply *DevicePlugin::networkManagerPut(const QNetworkRequest &request, c
return nullptr;
}
+/*!
+ Starts a SSDP search for a certain \a searchTarget (ST). Certain UPnP devices need a special ST (i.e. "udap:rootservice"
+ for LG Smart Tv's), otherwise they will not respond on the SSDP search. Each HTTP request to this device needs sometimes
+ also a special \a userAgent, which will be written into the HTTP header.
+
+ \sa DevicePlugin::requiredHardware(), DevicePlugin::upnpDiscoveryFinished()
+ */
+void DevicePlugin::upnpDiscover(QString searchTarget, QString userAgent)
+{
+ if(requiredHardware().testFlag(DeviceManager::HardwareResourceUpnpDisovery)){
+ deviceManager()->m_upnpDiscovery->discoverDevices(searchTarget, userAgent, pluginId());
+ }
+}
+
+#ifdef BLUETOOTH_LE
+bool DevicePlugin::discoverBluetooth()
+{
+ if(requiredHardware().testFlag(DeviceManager::HardwareResourceBluetoothLE)){
+ return deviceManager()->m_bluetoothScanner->discover(pluginId());
+ }
+ return false;
+}
+#endif
+
QStringList DevicePlugin::verifyFields(const QStringList &fields, const QJsonObject &value) const
{
QStringList ret;
diff --git a/libguh/plugin/deviceplugin.h b/libguh/plugin/deviceplugin.h
index 2535a16b..624d0e30 100644
--- a/libguh/plugin/deviceplugin.h
+++ b/libguh/plugin/deviceplugin.h
@@ -31,6 +31,10 @@
#include "types/vendor.h"
#include "types/param.h"
+#ifdef BLUETOOTH_LE
+#include
+#endif
+
#include
#include
@@ -71,6 +75,10 @@ public:
virtual void networkManagerReplyReady(QNetworkReply *reply) {Q_UNUSED(reply)}
+ #ifdef BLUETOOTH_LE
+ virtual void bluetoothDiscoveryFinished(const QList &deviceInfos) {Q_UNUSED(deviceInfos)}
+ #endif
+
// Configuration
virtual QList configurationDescription() const;
DeviceManager::DeviceError setConfiguration(const ParamList &configuration);
@@ -101,6 +109,11 @@ protected:
// Radio 433
bool transmitData(int delay, QList rawData);
+ // Bluetooth LE discovery
+ #ifdef BLUETOOTH_LE
+ bool discoverBluetooth();
+ #endif
+
// Network manager
QNetworkReply *networkManagerGet(const QNetworkRequest &request);
QNetworkReply *networkManagerPost(const QNetworkRequest &request, const QByteArray &data);