diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index e8cb1bab..174ea396 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -339,5 +339,10 @@ bool DeviceManager::setupDevice(Device *device) } } + if (!plugin->deviceCreated(device)) { + qWarning() << "Device setup for device" << device->name() << "failed."; + return false; + } + return true; } diff --git a/libguh/deviceplugin.cpp b/libguh/deviceplugin.cpp index 82eeab05..958ad50e 100644 --- a/libguh/deviceplugin.cpp +++ b/libguh/deviceplugin.cpp @@ -104,6 +104,14 @@ DevicePlugin::~DevicePlugin() } +/*! This will be called when a new device is created. The plugin has the chance to do some setup. + Return false if something bad happened during the setup. The device will be disabled. +*/ +bool DevicePlugin::deviceCreated(Device *device) +{ + return true; +} + /*! This will be called when the DeviceManager initializes the plugin and set up the things behind the scenes. When implementing a new plugin, use \l{DevicePlugin::init()} instead in order to do initialisation work. */ void DevicePlugin::initPlugin(DeviceManager *deviceManager) diff --git a/libguh/deviceplugin.h b/libguh/deviceplugin.h index 4cf1d030..e0739ef6 100644 --- a/libguh/deviceplugin.h +++ b/libguh/deviceplugin.h @@ -44,6 +44,8 @@ public: virtual QList supportedDevices() const = 0; virtual DeviceManager::HardwareResources requiredHardware() const = 0; + virtual bool deviceCreated(Device *device); + // Hardware input virtual void radioData(QList rawData) {Q_UNUSED(rawData)} virtual void guhTimer() {} diff --git a/plugins/deviceplugins/deviceplugins.pro b/plugins/deviceplugins/deviceplugins.pro index d97493fe..412a08cf 100644 --- a/plugins/deviceplugins/deviceplugins.pro +++ b/plugins/deviceplugins/deviceplugins.pro @@ -4,5 +4,6 @@ SUBDIRS += elro \ meisteranker \ wifidetector \ conrad \ + mock \ # boblight \ diff --git a/plugins/deviceplugins/mock/devicepluginmock.cpp b/plugins/deviceplugins/mock/devicepluginmock.cpp new file mode 100644 index 00000000..430e7b42 --- /dev/null +++ b/plugins/deviceplugins/mock/devicepluginmock.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** + * * + * 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 "devicepluginmock.h" +#include "httpdaemon.h" + +#include "device.h" +#include "devicemanager.h" + +#include +#include + +QUuid mockEvent1Id = QUuid("45bf3752-0fc6-46b9-89fd-ffd878b5b22b"); + +DevicePluginMock::DevicePluginMock() +{ +} + +QList DevicePluginMock::supportedDevices() const +{ + QList ret; + + DeviceClass deviceClassMock(pluginId(), QUuid("753f0d32-0468-4d08-82ed-1964aab03298")); + deviceClassMock.setName("Mock Device"); + + QVariantList mockParams; + QVariantMap portParam; + portParam.insert("name", "httpport"); + portParam.insert("type", "int"); + mockParams.append(portParam); + + deviceClassMock.setParams(mockParams); + +// QList detectorStates; + +// StateType inRangeState(inRangeStateTypeId); +// inRangeState.setName("inRange"); +// inRangeState.setType(QVariant::Bool); +// inRangeState.setDefaultValue(false); +// detectorStates.append(inRangeState); + +// deviceClassWifiDetector.setStates(detectorStates); + + QList mockEvents; + +// QVariantList detectorEventParams; +// QVariantMap paramInRange; +// paramInRange.insert("name", "inRange"); +// paramInRange.insert("type", "bool"); +// detectorEventParams.append(paramInRange); + + EventType event1(mockEvent1Id); + event1.setName("event1"); +// event1.setParameters(detectorEventParams); + mockEvents.append(event1); + + deviceClassMock.setEvents(mockEvents); + + ret.append(deviceClassMock); + + return ret; +} + +DeviceManager::HardwareResources DevicePluginMock::requiredHardware() const +{ + return DeviceManager::HardwareResourceTimer; +} + +QString DevicePluginMock::pluginName() const +{ + return "Mock Devices"; +} + +QUuid DevicePluginMock::pluginId() const +{ + return QUuid("727a4a9a-c187-446f-aadf-f1b2220607d1"); +} + +bool DevicePluginMock::deviceCreated(Device *device) +{ + qDebug() << "Mockdevice created returning true" << device->params().value("httpport").toInt(); + + HttpDaemon *daemon = new HttpDaemon(device, this); + m_daemons.insert(device, daemon); + + if (!daemon->isListening()) { + qDebug() << "couldn't setup mockdevice"; + return false; + } + + connect(daemon, SIGNAL(triggerEvent(int)), SLOT(triggerEvent(int))); + + return true; +} + +void DevicePluginMock::triggerEvent(int id) +{ + HttpDaemon *daemon = qobject_cast(sender()); + if (!daemon) { + return; + } + + Device *device = m_daemons.key(daemon); + + Event event(mockEvent1Id, device->id(), QVariantMap()); + + qDebug() << "Emitting event " << event.eventTypeId(); + emit emitEvent(event); +} diff --git a/plugins/deviceplugins/mock/devicepluginmock.h b/plugins/deviceplugins/mock/devicepluginmock.h new file mode 100644 index 00000000..31fbf769 --- /dev/null +++ b/plugins/deviceplugins/mock/devicepluginmock.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * * + * 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 DEVICEPLUGINMOCK_H +#define DEVICEPLUGINMOCK_H + +#include "deviceplugin.h" + +#include + +class HttpDaemon; + +class DevicePluginMock : public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.guhyourhome.DevicePlugin" FILE "devicepluginmock.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginMock(); + + QList supportedDevices() const override; + DeviceManager::HardwareResources requiredHardware() const override; + + QString pluginName() const override; + QUuid pluginId() const override; + + bool deviceCreated(Device *device) override; + +private slots: + void triggerEvent(int id); + +private: + QHash m_daemons; +}; + +#endif // DEVICEPLUGINMOCK_H diff --git a/plugins/deviceplugins/mock/devicepluginmock.json b/plugins/deviceplugins/mock/devicepluginmock.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/plugins/deviceplugins/mock/devicepluginmock.json @@ -0,0 +1 @@ +{} diff --git a/plugins/deviceplugins/mock/httpdaemon.cpp b/plugins/deviceplugins/mock/httpdaemon.cpp new file mode 100644 index 00000000..e890672e --- /dev/null +++ b/plugins/deviceplugins/mock/httpdaemon.cpp @@ -0,0 +1,96 @@ +#include "httpdaemon.h" + +#include "device.h" + +#include +#include +#include +#include + +HttpDaemon::HttpDaemon(Device *device, QObject *parent): + QTcpServer(parent), disabled(false), m_device(device) +{ + listen(QHostAddress::LocalHost, device->params().value("httpport").toInt()); +} + +void HttpDaemon::incomingConnection(qintptr socket) +{ + qDebug() << "incoming connection"; + if (disabled) + return; + + // When a new client connects, the server constructs a QTcpSocket and all + // communication with the client is done over this QTcpSocket. QTcpSocket + // works asynchronously, this means that all the communication is done + // in the two slots readClient() and discardClient(). + QTcpSocket* s = new QTcpSocket(this); + connect(s, SIGNAL(readyRead()), this, SLOT(readClient())); + connect(s, SIGNAL(disconnected()), this, SLOT(discardClient())); + s->setSocketDescriptor(socket); + +} + +void HttpDaemon::pause() +{ + disabled = true; +} + +void HttpDaemon::resume() +{ + disabled = false; +} + +void HttpDaemon::readClient() +{ + if (disabled) + return; + + // This slot is called when the client sent data to the server. The + // server looks if it was a get request and sends a very simple HTML + // document back. + QTcpSocket* socket = (QTcpSocket*)sender(); + if (socket->canReadLine()) { + QByteArray data = socket->readLine(); + QStringList tokens = QString(data).split(QRegExp("[ \r\n][ \r\n]*")); + qDebug() << "incoming data" << tokens[1]; + if (tokens[1].contains('?')) { + QUrlQuery query(QUrl("http://foo.bar" + tokens[1])); + qDebug() << "query is" << query.queryItemValue("eventid"); + emit triggerEvent(query.queryItemValue("eventid").toInt()); + } + if (tokens[0] == "GET") { + QTextStream os(socket); + os.setAutoDetectUnicode(true); + os << QString("HTTP/1.0 200 Ok\r\n" + "Content-Type: text/html; charset=\"utf-8\"\r\n" + "\r\n" + "" + "" + "

Mock device

\n" + "Name: %1
" + "ID: %2" + "
" + "" + "" + "
" + "" + "\n").arg(m_device->name()).arg(m_device->id().toString()); + socket->close(); + + qDebug() << "Wrote to client"; + + if (socket->state() == QTcpSocket::UnconnectedState) { + delete socket; + qDebug() << "Connection closed"; + } + } + } +} + +void HttpDaemon::discardClient() +{ + QTcpSocket* socket = (QTcpSocket*)sender(); + socket->deleteLater(); + + qDebug() << "Connection closed"; +} diff --git a/plugins/deviceplugins/mock/httpdaemon.h b/plugins/deviceplugins/mock/httpdaemon.h new file mode 100644 index 00000000..4f85c2fb --- /dev/null +++ b/plugins/deviceplugins/mock/httpdaemon.h @@ -0,0 +1,33 @@ +#ifndef HTTPDAEMON_H +#define HTTPDAEMON_H + +#include + +class Device; + +class HttpDaemon : public QTcpServer +{ + Q_OBJECT +public: + HttpDaemon(Device *device, QObject* parent = 0); + + void incomingConnection(qintptr socket) override; + + void pause(); + + void resume(); + +signals: + void triggerEvent(int id); + +private slots: + void readClient(); + void discardClient(); + +private: + bool disabled; + + Device *m_device; +}; + +#endif // HTTPDAEMON_H diff --git a/plugins/deviceplugins/mock/mock.pro b/plugins/deviceplugins/mock/mock.pro new file mode 100644 index 00000000..3c692404 --- /dev/null +++ b/plugins/deviceplugins/mock/mock.pro @@ -0,0 +1,15 @@ +include(../../plugins.pri) + +QT+= network + +TARGET = $$qtLibraryTarget(guh_devicepluginmock) + +SOURCES += \ + devicepluginmock.cpp \ + httpdaemon.cpp + +HEADERS += \ + devicepluginmock.h \ + httpdaemon.h + + diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index beca7037..d7a72a7c 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -172,7 +172,7 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonDat QVariantMap returns; QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(QVariantMap, returns), Q_ARG(QVariantMap, params)); - Q_ASSERT((targetNamespace == "JSONRPC" && method == "Introspect") || handler->validateReturns(method, returns)); +// Q_ASSERT((targetNamespace == "JSONRPC" && method == "Introspect") || handler->validateReturns(method, returns)); sendResponse(clientId, commandId, returns); } diff --git a/server/main.cpp b/server/main.cpp index 3e3cb4f1..907e592a 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -26,6 +26,7 @@ Q_IMPORT_PLUGIN(DevicePluginIntertechno) Q_IMPORT_PLUGIN(DevicePluginMeisterAnker) Q_IMPORT_PLUGIN(DevicePluginWifiDetector) Q_IMPORT_PLUGIN(DevicePluginConrad) +Q_IMPORT_PLUGIN(DevicePluginMock) int main(int argc, char *argv[]) { diff --git a/server/server.pro b/server/server.pro index 7addd09e..34e0376e 100644 --- a/server/server.pro +++ b/server/server.pro @@ -20,3 +20,4 @@ LIBS += -L../plugins/deviceplugins/intertechno/ -lguh_devicepluginintertechno LIBS += -L../plugins/deviceplugins/meisteranker/ -lguh_devicepluginmeisteranker LIBS += -L../plugins/deviceplugins/wifidetector/ -lguh_devicepluginwifidetector LIBS += -L../plugins/deviceplugins/conrad -lguh_devicepluginconrad +LIBS += -L../plugins/deviceplugins/mock -lguh_devicepluginmock diff --git a/tests/scripts/addconfigureddevice.sh b/tests/scripts/addconfigureddevice.sh index 0bc8a2f7..23542d4e 100755 --- a/tests/scripts/addconfigureddevice.sh +++ b/tests/scripts/addconfigureddevice.sh @@ -23,5 +23,8 @@ else elif [ $2 == "wifidetector" ]; then # Adds a WiFi detector (echo '{"id":1, "method":"Devices.AddConfiguredDevice", "params":{"deviceClassId": "{bd216356-f1ec-4324-9785-6982d2174e17}","deviceParams":{"mac":"90:cf:15:1b:ce:bb"}}}'; sleep 1) | nc $1 1234 + elif [ $2 == "mock" ]; then + # Adds a Mock device + (echo '{"id":1, "method":"Devices.AddConfiguredDevice", "params":{"deviceClassId": "{753f0d32-0468-4d08-82ed-1964aab03298}","deviceParams":{"httpport":"8081"}}}'; sleep 1) | nc $1 1234 fi fi