From 30f5c44e084c9bab6973602bca4c9445d5d87111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Sat, 18 Oct 2014 10:47:10 +0200 Subject: [PATCH] added upnp discovery as hardware resource --- libguh/devicemanager.cpp | 12 + libguh/devicemanager.h | 9 +- libguh/hardware/upnpdiscovery/upnpdevice.cpp | 108 +++++++++ libguh/hardware/upnpdiscovery/upnpdevice.h | 70 ++++++ .../upnpdiscovery/upnpdevicedescriptor.cpp | 183 +++++++++++++++ .../upnpdiscovery/upnpdevicedescriptor.h | 96 ++++++++ .../hardware/upnpdiscovery/upnpdiscovery.cpp | 221 ++++++++++++++++++ libguh/hardware/upnpdiscovery/upnpdiscovery.h | 71 ++++++ libguh/libguh.pro | 7 +- libguh/plugin/deviceplugin.cpp | 28 ++- libguh/plugin/deviceplugin.h | 4 +- plugins/plugins.pri | 4 +- 12 files changed, 801 insertions(+), 12 deletions(-) create mode 100644 libguh/hardware/upnpdiscovery/upnpdevice.cpp create mode 100644 libguh/hardware/upnpdiscovery/upnpdevice.h create mode 100644 libguh/hardware/upnpdiscovery/upnpdevicedescriptor.cpp create mode 100644 libguh/hardware/upnpdiscovery/upnpdevicedescriptor.h create mode 100644 libguh/hardware/upnpdiscovery/upnpdiscovery.cpp create mode 100644 libguh/hardware/upnpdiscovery/upnpdiscovery.h diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 14a3815a..395c554f 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -181,6 +181,9 @@ DeviceManager::DeviceManager(QObject *parent) : connect(m_radio433, &Radio433::dataReceived, this, &DeviceManager::radio433SignalReceived); m_radio433->enable(); // TODO: error handling if no Radio433 detected (GPIO or network), disable radio433 plugins or something... + + m_upnpDiscovery = new UpnpDiscovery(this); + connect(m_upnpDiscovery, &UpnpDiscovery::discoveryFinished, this, &DeviceManager::upnpDiscoveryFinished); } /*! Destructor of the DeviceManager. Each loaded \l{DevicePlugin} will be deleted. */ @@ -929,6 +932,15 @@ void DeviceManager::radio433SignalReceived(QList rawData) } } +void DeviceManager::upnpDiscoveryFinished(const QList &deviceDescriptorList, const PluginId &pluginId) +{ + foreach (DevicePlugin *devicePlugin, m_devicePlugins) { + if (devicePlugin->requiredHardware().testFlag(HardwareResourceUpnpDisovery) && devicePlugin->pluginId() == pluginId) { + devicePlugin->upnpDiscoveryFinished(deviceDescriptorList); + } + } +} + void DeviceManager::timerEvent() { foreach (Device *device, m_configuredDevices) { diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index cfeac9a9..35ae2d31 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -27,12 +27,16 @@ #include "types/action.h" #include "types/vendor.h" +#include "hardware/upnpdiscovery/upnpdiscovery.h" +#include "hardware/upnpdiscovery/upnpdevicedescriptor.h" + #include #include class Device; class DevicePlugin; class Radio433; +class UpnpDiscovery; class DeviceManager : public QObject { @@ -43,7 +47,8 @@ public: HardwareResourceNone = 0x00, HardwareResourceRadio433 = 0x01, HardwareResourceRadio868 = 0x02, - HardwareResourceTimer = 0x04 + HardwareResourceTimer = 0x04, + HardwareResourceUpnpDisovery = 0x08 }; Q_DECLARE_FLAGS(HardwareResources, HardwareResource) @@ -126,6 +131,7 @@ private slots: void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value); void radio433SignalReceived(QList rawData); + void upnpDiscoveryFinished(const QList &deviceDescriptorList, const PluginId &pluginId); void timerEvent(); private: @@ -152,6 +158,7 @@ private: Radio433* m_radio433; QTimer m_pluginTimer; QList m_pluginTimerUsers; + UpnpDiscovery* m_upnpDiscovery; QHash > m_pairingsJustAdd; QHash > m_pairingsDiscovery; diff --git a/libguh/hardware/upnpdiscovery/upnpdevice.cpp b/libguh/hardware/upnpdiscovery/upnpdevice.cpp new file mode 100644 index 00000000..79d09714 --- /dev/null +++ b/libguh/hardware/upnpdiscovery/upnpdevice.cpp @@ -0,0 +1,108 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "upnpdevice.h" + +UpnpDevice::UpnpDevice(QObject *parent, UpnpDeviceDescriptor upnpDeviceDescriptor) : + QObject(parent) +{ + m_location = upnpDeviceDescriptor.location(); + m_hostAddress = upnpDeviceDescriptor.hostAddress(); + m_port = upnpDeviceDescriptor.port(); + m_deviceType = upnpDeviceDescriptor.deviceType(); + m_friendlyName = upnpDeviceDescriptor.friendlyName(); + m_manufacturer = upnpDeviceDescriptor.manufacturer(); + m_manufacturerURL = upnpDeviceDescriptor.manufacturerURL(); + m_modelDescription = upnpDeviceDescriptor.modelDescription(); + m_modelName = upnpDeviceDescriptor.modelName(); + m_modelNumber = upnpDeviceDescriptor.modelNumber(); + m_modelURL = upnpDeviceDescriptor.modelURL(); + m_serialNumber = upnpDeviceDescriptor.serialNumber(); + m_uuid = upnpDeviceDescriptor.uuid(); + m_upc = upnpDeviceDescriptor.upc(); +} + +QUrl UpnpDevice::location() +{ + return m_location; +} + +QHostAddress UpnpDevice::hostAddress() const +{ + return m_hostAddress; +} + +int UpnpDevice::port() const +{ + return m_port; +} + +QString UpnpDevice::deviceType() const +{ + return m_deviceType; +} + +QString UpnpDevice::friendlyName() const +{ + return m_friendlyName; +} + +QString UpnpDevice::manufacturer() const +{ + return m_manufacturer; +} + +QUrl UpnpDevice::manufacturerURL() const +{ + return m_manufacturerURL; +} + +QString UpnpDevice::modelDescription() const +{ + return m_modelDescription; +} + +QString UpnpDevice::modelName() const +{ + return m_modelName; +} + +QString UpnpDevice::modelNumber() const +{ + return m_modelNumber; +} + +QUrl UpnpDevice::modelURL() const +{ + return m_modelURL; +} + +QString UpnpDevice::serialNumber() const +{ + return m_serialNumber; +} + +QString UpnpDevice::uuid() const +{ + return m_uuid; +} + +QString UpnpDevice::upc() const +{ + return m_upc; +} diff --git a/libguh/hardware/upnpdiscovery/upnpdevice.h b/libguh/hardware/upnpdiscovery/upnpdevice.h new file mode 100644 index 00000000..d86dfec3 --- /dev/null +++ b/libguh/hardware/upnpdiscovery/upnpdevice.h @@ -0,0 +1,70 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 UPNPDEVICE_H +#define UPNPDEVICE_H + +#include + +#include "upnpdevicedescriptor.h" + +class UpnpDevice : public QObject +{ + Q_OBJECT +public: + explicit UpnpDevice(QObject *parent = 0, UpnpDeviceDescriptor upnpDeviceDescriptor = UpnpDeviceDescriptor()); + + QUrl location(); + QHostAddress hostAddress() const; + int port() const; + QString deviceType() const; + QString friendlyName() const; + QString manufacturer() const; + QUrl manufacturerURL() const; + QString modelDescription() const; + QString modelName() const; + QString modelNumber() const; + QUrl modelURL() const; + QString serialNumber() const; + QString uuid() const; + QString upc() const; + + +private: + QUrl m_location; + QHostAddress m_hostAddress; + int m_port; + QString m_deviceType; + QString m_friendlyName; + QString m_manufacturer; + QUrl m_manufacturerURL; + QString m_modelDescription; + QString m_modelName; + QString m_modelNumber; + QUrl m_modelURL; + QString m_serialNumber; + QString m_uuid; + QString m_upc; + +signals: + +public slots: + +}; + +#endif // UPNPDEVICE_H diff --git a/libguh/hardware/upnpdiscovery/upnpdevicedescriptor.cpp b/libguh/hardware/upnpdiscovery/upnpdevicedescriptor.cpp new file mode 100644 index 00000000..74f2187a --- /dev/null +++ b/libguh/hardware/upnpdiscovery/upnpdevicedescriptor.cpp @@ -0,0 +1,183 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "upnpdevicedescriptor.h" + +UpnpDeviceDescriptor::UpnpDeviceDescriptor() +{ +} + +void UpnpDeviceDescriptor::setLocation(const QUrl &location) +{ + m_location = location; +} + +QUrl UpnpDeviceDescriptor::location() const +{ + return m_location; +} + +void UpnpDeviceDescriptor::setHostAddress(const QHostAddress &hostAddress) +{ + m_hostAddress = hostAddress; +} + +QHostAddress UpnpDeviceDescriptor::hostAddress() const +{ + return m_hostAddress; +} + +void UpnpDeviceDescriptor::setPort(const int &port) +{ + m_port = port; +} + +int UpnpDeviceDescriptor::port() const +{ + return m_port; +} + +void UpnpDeviceDescriptor::setDeviceType(const QString &deviceType) +{ + m_deviceType = deviceType; +} + +QString UpnpDeviceDescriptor::deviceType() const +{ + return m_deviceType; +} + +void UpnpDeviceDescriptor::setFriendlyName(const QString &friendlyName) +{ + m_friendlyName = friendlyName; +} + +QString UpnpDeviceDescriptor::friendlyName() const +{ + return m_friendlyName; +} + +void UpnpDeviceDescriptor::setManufacturer(const QString &manufacturer) +{ + m_manufacturer = manufacturer; +} + +QString UpnpDeviceDescriptor::manufacturer() const +{ + return m_manufacturer; +} + +void UpnpDeviceDescriptor::setManufacturerURL(const QUrl &manufacturerURL) +{ + m_manufacturerURL = manufacturerURL; +} + +QUrl UpnpDeviceDescriptor::manufacturerURL() const +{ + return m_manufacturerURL; +} + +void UpnpDeviceDescriptor::setModelDescription(const QString &modelDescription) +{ + m_modelDescription = modelDescription; +} + +QString UpnpDeviceDescriptor::modelDescription() const +{ + return m_modelDescription; +} + +void UpnpDeviceDescriptor::setModelName(const QString &modelName) +{ + m_modelName = modelName; +} + +QString UpnpDeviceDescriptor::modelName() const +{ + return m_modelName; +} + +void UpnpDeviceDescriptor::setModelNumber(const QString &modelNumber) +{ + m_modelNumber = modelNumber; +} + +QString UpnpDeviceDescriptor::modelNumber() const +{ + return m_modelNumber; +} + +void UpnpDeviceDescriptor::setModelURL(const QUrl &modelURL) +{ + m_modelURL = modelURL; +} + +QUrl UpnpDeviceDescriptor::modelURL() const +{ + return m_modelURL; +} + +void UpnpDeviceDescriptor::setSerialNumber(const QString &serialNumber) +{ + m_serialNumber = serialNumber; +} + +QString UpnpDeviceDescriptor::serialNumber() const +{ + return m_serialNumber; +} + +void UpnpDeviceDescriptor::setUuid(const QString &uuid) +{ + m_uuid = uuid; +} + +QString UpnpDeviceDescriptor::uuid() const +{ + return m_uuid; +} + +void UpnpDeviceDescriptor::setUpc(const QString &upc) +{ + m_upc = upc; +} + +QString UpnpDeviceDescriptor::upc() const +{ + return m_upc; +} + +QDebug operator<<(QDebug debug, const UpnpDeviceDescriptor &upnpDeviceDescriptor) +{ + debug << "----------------------------------------------\n"; + debug << "UPnP device on " << upnpDeviceDescriptor.hostAddress().toString() << upnpDeviceDescriptor.port() << "\n"; + debug << "location | " << upnpDeviceDescriptor.location() << "\n"; + debug << "friendly name | " << upnpDeviceDescriptor.friendlyName() << "\n"; + debug << "manufacturer | " << upnpDeviceDescriptor.manufacturer() << "\n"; + debug << "manufacturer URL | " << upnpDeviceDescriptor.manufacturerURL().toString() << "\n"; + debug << "device type | " << upnpDeviceDescriptor.deviceType() << "\n"; + debug << "model name | " << upnpDeviceDescriptor.modelName() << "\n"; + debug << "model number | " << upnpDeviceDescriptor.modelNumber() << "\n"; + debug << "model description | " << upnpDeviceDescriptor.modelDescription() << "\n"; + debug << "model URL | " << upnpDeviceDescriptor.modelURL().toString() << "\n"; + debug << "serial number | " << upnpDeviceDescriptor.serialNumber() << "\n"; + debug << "UUID | " << upnpDeviceDescriptor.uuid() << "\n"; + debug << "UPC | " << upnpDeviceDescriptor.upc() << "\n\n"; + + return debug; +} diff --git a/libguh/hardware/upnpdiscovery/upnpdevicedescriptor.h b/libguh/hardware/upnpdiscovery/upnpdevicedescriptor.h new file mode 100644 index 00000000..fb3dd967 --- /dev/null +++ b/libguh/hardware/upnpdiscovery/upnpdevicedescriptor.h @@ -0,0 +1,96 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 UPNPDEVICEDESCRIPTOR_H +#define UPNPDEVICEDESCRIPTOR_H + +#include +#include +#include + +// reference: http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf + +class UpnpDeviceDescriptor +{ +public: + explicit UpnpDeviceDescriptor(); + + void setLocation(const QUrl &location); + QUrl location() const; + + void setHostAddress(const QHostAddress &hostAddress); + QHostAddress hostAddress() const; + + void setPort(const int &port); + int port() const; + + void setDeviceType(const QString &deviceType); + QString deviceType() const; + + void setFriendlyName(const QString &friendlyName); + QString friendlyName() const; + + void setManufacturer(const QString &manufacturer); + QString manufacturer() const; + + void setManufacturerURL(const QUrl &manufacturerURL); + QUrl manufacturerURL() const; + + void setModelDescription(const QString &modelDescription); + QString modelDescription() const; + + void setModelName(const QString &modelName); + QString modelName() const; + + void setModelNumber(const QString &modelNumber); + QString modelNumber() const; + + void setModelURL(const QUrl &modelURL); + QUrl modelURL() const; + + void setSerialNumber(const QString &serialNumber); + QString serialNumber() const; + + void setUuid(const QString &uuid); + QString uuid() const; + + void setUpc(const QString &upc); + QString upc() const; + + +private: + QUrl m_location; + QHostAddress m_hostAddress; + int m_port; + QString m_deviceType; + QString m_friendlyName; + QString m_manufacturer; + QUrl m_manufacturerURL; + QString m_modelDescription; + QString m_modelName; + QString m_modelNumber; + QUrl m_modelURL; + QString m_serialNumber; + QString m_uuid; + QString m_upc; +}; + +Q_DECLARE_METATYPE(UpnpDeviceDescriptor) +QDebug operator<< (QDebug debug, const UpnpDeviceDescriptor &upnpDeviceDescriptor); + +#endif // UPNPDEVICEDESCRIPTOR_H diff --git a/libguh/hardware/upnpdiscovery/upnpdiscovery.cpp b/libguh/hardware/upnpdiscovery/upnpdiscovery.cpp new file mode 100644 index 00000000..33c44486 --- /dev/null +++ b/libguh/hardware/upnpdiscovery/upnpdiscovery.cpp @@ -0,0 +1,221 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "upnpdiscovery.h" + +UpnpDiscovery::UpnpDiscovery(QObject *parent) : + QUdpSocket(parent) +{ + // bind udp socket and join multicast group + m_port = 1900; + m_host = QHostAddress("239.255.255.250"); + + setSocketOption(QAbstractSocket::MulticastTtlOption,QVariant(1)); + setSocketOption(QAbstractSocket::MulticastLoopbackOption,QVariant(1)); + + if(!bind(QHostAddress::AnyIPv4,m_port,QUdpSocket::ShareAddress)){ + qWarning() << "ERROR: UPnP discovery could not bind to port " << m_port; + return; + } + + if(!joinMulticastGroup(m_host)){ + qWarning() << "ERROR: UPnP discovery could not join multicast group " << m_host; + return; + } + + m_deviceList.clear(); + m_informationRequestList.clear(); + + // network access manager for requesting device information + m_networkAccessManager = new QNetworkAccessManager(this); + connect(m_networkAccessManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*))); + + // discovery refresh timer + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + connect(m_timer, &QTimer::timeout, this, &UpnpDiscovery::discoverTimeout); + + connect(this,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error(QAbstractSocket::SocketError))); + connect(this, &UpnpDiscovery::readyRead, this, &UpnpDiscovery::readData); + + qDebug() << "--> Successfully created UPnPDiscovery."; +} + +bool UpnpDiscovery::discoverDevices(const QString &searchTarget, const QString &userAgent, const PluginId &pluginId) +{ + // clear the list... + m_deviceList.clear(); + foreach (QNetworkReply* reply, m_informationRequestList.keys()) { + m_informationRequestList.remove(reply); + reply->deleteLater(); + } + + m_searchTarget = searchTarget; + m_userAgent = userAgent; + m_pluginId = pluginId; + + QByteArray ssdpSearchMessage = QByteArray("M-SEARCH * HTTP/1.1\r\n" + "HOST:239.255.255.250:1900\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:2\r\n" + "ST: " + m_searchTarget.toUtf8() + "\r\n" + "USR-AGENT: " + m_userAgent.toUtf8() + "\r\n\r\n"); + + writeDatagram(ssdpSearchMessage,m_host,m_port); + + m_timer->start(3000); + return true; +} + +void UpnpDiscovery::requestDeviceInformation(const UpnpDeviceDescriptor &upnpDeviceDescriptor) +{ + QNetworkRequest deviceRequest; + deviceRequest.setUrl(upnpDeviceDescriptor.location()); + deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml")); + deviceRequest.setHeader(QNetworkRequest::UserAgentHeader,QVariant(m_userAgent)); + + QNetworkReply *replay; + replay = m_networkAccessManager->get(deviceRequest); + m_informationRequestList.insert(replay, upnpDeviceDescriptor); +} + +void UpnpDiscovery::error(QAbstractSocket::SocketError error) +{ + qWarning() << errorString() << error; +} + +void UpnpDiscovery::readData() +{ + QByteArray data; + QHostAddress hostAddress; + QUrl location; + + // read the answere from the multicast + while (hasPendingDatagrams()) { + data.resize(pendingDatagramSize()); + readDatagram(data.data(), data.size(), &hostAddress); + } +// qDebug() << "-----------------------"; +// qDebug() << data; + + // if the data contains the HTTP OK header... + if(data.contains("HTTP/1.1 200 OK")){ + const QStringList lines = QString(data).split("\r\n"); + foreach( const QString& line, lines){ + int separatorIndex = line.indexOf(':'); + QString key = line.left(separatorIndex).toUpper(); + QString value = line.mid(separatorIndex+1).trimmed(); + + // get location + if(key.contains("LOCATION")){ + location = QUrl(value); + } + } + + UpnpDeviceDescriptor upnpDeviceDescriptor; + upnpDeviceDescriptor.setLocation(location); + upnpDeviceDescriptor.setHostAddress(hostAddress); + upnpDeviceDescriptor.setPort(location.port()); + + requestDeviceInformation(upnpDeviceDescriptor); + } +} + +void UpnpDiscovery::replyFinished(QNetworkReply *reply) +{ + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + switch (status) { + case(200):{ + QByteArray data = reply->readAll(); + UpnpDeviceDescriptor upnpDeviceDescriptor = m_informationRequestList.take(reply); + + // parse XML data + QXmlStreamReader xml(data); + while(!xml.atEnd() && !xml.hasError()){ + xml.readNext(); + if(xml.isStartDocument()){ + continue; + } + if(xml.isStartElement()){ + if(xml.name().toString() == "device"){ + while(!xml.atEnd()){ + if(xml.name() == "deviceType" && xml.isStartElement()){ + upnpDeviceDescriptor.setDeviceType(xml.readElementText()); + } + if(xml.name() == "friendlyName" && xml.isStartElement()){ + upnpDeviceDescriptor.setFriendlyName(xml.readElementText()); + } + if(xml.name() == "manufacturer" && xml.isStartElement()){ + upnpDeviceDescriptor.setManufacturer(xml.readElementText()); + } + if(xml.name() == "manufacturerURL" && xml.isStartElement()){ + upnpDeviceDescriptor.setManufacturerURL(QUrl(xml.readElementText())); + } + if(xml.name() == "modelDescription" && xml.isStartElement()){ + upnpDeviceDescriptor.setModelDescription(xml.readElementText()); + } + if(xml.name() == "modelName" && xml.isStartElement()){ + upnpDeviceDescriptor.setModelName(xml.readElementText()); + } + if(xml.name() == "modelNumber" && xml.isStartElement()){ + upnpDeviceDescriptor.setModelNumber(xml.readElementText()); + } + if(xml.name() == "modelURL" && xml.isStartElement()){ + upnpDeviceDescriptor.setModelURL(QUrl(xml.readElementText())); + } + if(xml.name() == "serialNumber" && xml.isStartElement()){ + upnpDeviceDescriptor.setSerialNumber(xml.readElementText()); + } + if(xml.name() == "UDN" && xml.isStartElement()){ + upnpDeviceDescriptor.setUuid(xml.readElementText()); + } + if(xml.name() == "UPC" && xml.isStartElement()){ + upnpDeviceDescriptor.setUpc(xml.readElementText()); + } + xml.readNext(); + } + xml.readNext(); + } + } + } + + // check if we allready have the device in the list + bool isAlreadyInList = false; + foreach (UpnpDeviceDescriptor deviceDescriptor, m_deviceList) { + if(deviceDescriptor.uuid() == upnpDeviceDescriptor.uuid()){ + isAlreadyInList = true; + } + } + if(!isAlreadyInList){ + m_deviceList.append(upnpDeviceDescriptor); + } + break; + } + default: + qWarning() << "HTTP request error " << status; + m_informationRequestList.remove(reply); + } + + reply->deleteLater(); +} + +void UpnpDiscovery::discoverTimeout() +{ + emit discoveryFinished(m_deviceList, m_pluginId); +} diff --git a/libguh/hardware/upnpdiscovery/upnpdiscovery.h b/libguh/hardware/upnpdiscovery/upnpdiscovery.h new file mode 100644 index 00000000..bcc66d42 --- /dev/null +++ b/libguh/hardware/upnpdiscovery/upnpdiscovery.h @@ -0,0 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 UPNPDISCOVERY_H +#define UPNPDISCOVERY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "upnpdevicedescriptor.h" +#include "devicemanager.h" + +// reference: http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf + +class UpnpDiscovery : public QUdpSocket +{ + Q_OBJECT +public: + explicit UpnpDiscovery(QObject *parent = 0); + bool discoverDevices(const QString &searchTarget = "ssdp:all", const QString &userAgent = "", const PluginId &pluginId = PluginId()); + +private: + QHostAddress m_host; + qint16 m_port; + QTimer *m_timer; + PluginId m_pluginId; + QString m_searchTarget; + QString m_userAgent; + + QNetworkAccessManager *m_networkAccessManager; + QHash m_informationRequestList; + QList m_deviceList; + + void requestDeviceInformation(const UpnpDeviceDescriptor &upnpDeviceDescriptor); + +signals: + void discoveryFinished(const QList &deviceDescriptorList, const PluginId & pluginId); + +private slots: + void error(QAbstractSocket::SocketError error); + void readData(); + void replyFinished(QNetworkReply *reply); + void discoverTimeout(); + +public slots: +}; + +#endif // UPNPDISCOVERY_H diff --git a/libguh/libguh.pro b/libguh/libguh.pro index ebbe96b6..dd19db15 100644 --- a/libguh/libguh.pro +++ b/libguh/libguh.pro @@ -31,6 +31,9 @@ SOURCES += plugin/device.cpp \ types/param.cpp \ types/paramdescriptor.cpp \ types/statedescriptor.cpp \ + hardware/upnpdiscovery/upnpdiscovery.cpp \ + hardware/upnpdiscovery/upnpdevice.cpp \ + hardware/upnpdiscovery/upnpdevicedescriptor.cpp HEADERS += plugin/device.h \ plugin/deviceclass.h \ @@ -57,4 +60,6 @@ HEADERS += plugin/device.h \ types/paramdescriptor.h \ types/statedescriptor.h \ typeutils.h \ - + hardware/upnpdiscovery/upnpdiscovery.h \ + hardware/upnpdiscovery/upnpdevice.h \ + hardware/upnpdiscovery/upnpdevicedescriptor.h diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index 3c6d7240..3f9cd950 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -47,7 +47,15 @@ */ /*! - \fn DeviceManager::DeviceError DevicePlugin::executeAction(Device *device, const Action &action) + \fn void DevicePlugin::upnpDiscoveryFinished(QList deviceList) + If the plugin has requested the upnp \a deviceList using \l{DevicePlugin::upnpDiscover(QString searchTarget)}, + this slot will be called after 3 seconds (search timeout). The list will contain all devices available on in + the network, which responded to the given search target string + \sa DevicePlugin::upnpDiscover() + */ + +/*! + \fn void DevicePlugin::executeAction(Device *device, const Action &action) This will be called to actually execute actions on the hardware. The \{Device} and the \{Action} are contained in the \a device and \a action parameters. Return the appropriate \l{DeviceManager::DeviceError}{DeviceError}. @@ -117,6 +125,7 @@ #include "devicemanager.h" #include "hardware/radio433/radio433.h" +#include "hardware/upnpdiscovery/upnpdiscovery.h" #include #include @@ -507,13 +516,16 @@ bool DevicePlugin::transmitData(int delay, QList rawData, int repetitions) return false; } -QStringList DevicePlugin::verifyFields(const QStringList &fields, const QJsonObject &value) const +/*! + 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) { - QStringList ret; - foreach (const QString &field, fields) { - if (!value.contains(field)) { - ret << field; - } + if(requiredHardware() == DeviceManager::HardwareResourceUpnpDisovery){ + deviceManager()->m_upnpDiscovery->discoverDevices(searchTarget, userAgent, pluginId()); } - return ret; } diff --git a/libguh/plugin/deviceplugin.h b/libguh/plugin/deviceplugin.h index b05b5e3c..3cb82552 100644 --- a/libguh/plugin/deviceplugin.h +++ b/libguh/plugin/deviceplugin.h @@ -61,6 +61,7 @@ public: // Hardware input virtual void radioData(const QList &rawData) {Q_UNUSED(rawData)} virtual void guhTimer() {} + virtual void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) {Q_UNUSED(upnpDeviceDescriptorList)} // Configuration virtual QList configurationDescription() const; @@ -89,7 +90,8 @@ protected: QList myDevices() const; Device* findDeviceByParams(const ParamList ¶ms) const; - bool transmitData(int delay, QList rawData, int repetitions = 10); + bool transmitData(int delay, QList rawData); + void upnpDiscover(QString searchTarget = "ssdp:all", QString userAgent = QString()); private: void initPlugin(const QJsonObject &metaData, DeviceManager *deviceManager); diff --git a/plugins/plugins.pri b/plugins/plugins.pri index cd59f1a9..1263541a 100644 --- a/plugins/plugins.pri +++ b/plugins/plugins.pri @@ -3,7 +3,9 @@ include(../guh.pri) TEMPLATE = lib CONFIG += plugin -INCLUDEPATH += $$top_srcdir/libguh +QT += network + +INCLUDEPATH += ../../../libguh LIBS += -L../../../libguh -lguh infofile.output = plugininfo.h