diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 97d87ecf..9e7b863a 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -146,18 +146,20 @@ QList DeviceManager::supportedDevices(const VendorId &vendorId) con return ret; } -QList DeviceManager::discoveredDevices(const DeviceClassId &deviceClassId) const +DeviceManager::DeviceError DeviceManager::discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap ¶ms) const { - QList ret; DeviceClass deviceClass = findDeviceClass(deviceClassId); if (!deviceClass.isValid()) { - return ret; + return DeviceManager::DeviceErrorDeviceClassNotFound; + } + if (deviceClass.createMethod() != DeviceClass::CreateMethodDiscovery) { + return DeviceManager::DeviceErrorCreationMethodNotSupported; } DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); if (!plugin) { - return ret; + return DeviceManager::DeviceErrorPluginNotFound; } - ret = plugin->discoveredDevices(deviceClassId); + return plugin->discoverDevices(deviceClassId, params); } /*! Add a new configured device for the given \l{DeviceClass} and the given parameters. @@ -175,7 +177,7 @@ DeviceManager::DeviceError DeviceManager::addConfiguredDevice(const DeviceClassI if (deviceClass.createMethod() == DeviceClass::CreateMethodUser) { return addConfiguredDeviceInternal(deviceClassId, params, id); } - return DeviceErrorCreationNotSupported; + return DeviceErrorCreationMethodNotSupported; } DeviceManager::DeviceError DeviceManager::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QVariantMap ¶ms, const DeviceId id) @@ -366,6 +368,7 @@ void DeviceManager::loadPlugins() m_devicePlugins.insert(pluginIface->pluginId(), pluginIface); connect(pluginIface, &DevicePlugin::emitEvent, this, &DeviceManager::emitEvent); + connect(pluginIface, &DevicePlugin::devicesDiscovered, this, &DeviceManager::slotDevicesDiscovered); } } } @@ -430,6 +433,12 @@ void DeviceManager::createNewAutoDevices() storeConfiguredDevices(); } +void DeviceManager::slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors) +{ + m_discoveredDevices[deviceClassId] = deviceDescriptors; + emit devicesDiscovered(deviceClassId, deviceDescriptors); +} + void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value) { Device *device = qobject_cast(sender()); diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index ba0a4d28..ef0970cf 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -21,7 +21,7 @@ #include "plugin/deviceclass.h" #include "plugin/device.h" -#include "plugin/devicedescription.h" +#include "plugin/devicedescriptor.h" #include "types/event.h" #include "types/action.h" @@ -55,7 +55,7 @@ public: DeviceErrorPluginNotFound, DeviceErrorSetupFailed, DeviceErrorDuplicateUuid, - DeviceErrorCreationNotSupported, + DeviceErrorCreationMethodNotSupported, DeviceErrorDeviceParameterError, DeviceErrorActionParameterError }; @@ -68,7 +68,7 @@ public: QList supportedVendors() const; QList supportedDevices(const VendorId &vendorId = VendorId()) const; - QList discoveredDevices(const DeviceClassId &deviceClassId) const; + DeviceError discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap ¶ms) const; QList configuredDevices() const; DeviceError addConfiguredDevice(const DeviceClassId &deviceClassId, const QVariantMap ¶ms, const DeviceId id = DeviceId::createDeviceId()); @@ -82,6 +82,7 @@ signals: void loaded(); void emitEvent(const Event &event); void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value); + void devicesDiscovered(const DeviceClassId &deviceClassId, const QList &devices); public slots: DeviceError executeAction(const Action &action); @@ -91,6 +92,7 @@ private slots: void loadConfiguredDevices(); void storeConfiguredDevices(); void createNewAutoDevices(); + void slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors); // Only connect this to Devices. It will query the sender() void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value); @@ -106,6 +108,7 @@ private: QHash > m_vendorDeviceMap; QHash m_supportedDevices; QList m_configuredDevices; + QHash > m_discoveredDevices; QHash m_devicePlugins; diff --git a/libguh/libguh.pro b/libguh/libguh.pro index 27b51e53..241fb8ab 100644 --- a/libguh/libguh.pro +++ b/libguh/libguh.pro @@ -9,6 +9,7 @@ INSTALLS += target SOURCES += plugin/device.cpp \ plugin/deviceclass.cpp \ plugin/deviceplugin.cpp \ + plugin/devicedescriptor.cpp \ devicemanager.cpp \ hardware/radio433.cpp \ hardware/gpio.cpp \ @@ -19,11 +20,11 @@ SOURCES += plugin/device.cpp \ types/eventtype.cpp \ types/event.cpp \ types/vendor.cpp \ - plugin/devicedescription.cpp HEADERS += plugin/device.h \ plugin/deviceclass.h \ plugin/deviceplugin.h \ + plugin/devicedescriptor.h \ devicemanager.h \ hardware/radio433.h \ hardware/gpio.h \ @@ -35,5 +36,4 @@ HEADERS += plugin/device.h \ types/event.h \ types/vendor.h \ types/typeutils.h \ - plugin/devicedescription.h diff --git a/libguh/plugin/devicedescription.cpp b/libguh/plugin/devicedescription.cpp deleted file mode 100644 index 040cfd33..00000000 --- a/libguh/plugin/devicedescription.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "devicedescription.h" - -DeviceDescription::DeviceDescription() -{ -} diff --git a/libguh/plugin/devicedescription.h b/libguh/plugin/devicedescription.h deleted file mode 100644 index c1348adf..00000000 --- a/libguh/plugin/devicedescription.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DEVICEDESCRIPTION_H -#define DEVICEDESCRIPTION_H - -class DeviceDescription -{ -public: - DeviceDescription(); -}; - -#endif // DEVICEDESCRIPTION_H diff --git a/libguh/plugin/devicedescriptor.cpp b/libguh/plugin/devicedescriptor.cpp new file mode 100644 index 00000000..f3f9780e --- /dev/null +++ b/libguh/plugin/devicedescriptor.cpp @@ -0,0 +1,49 @@ +#include "devicedescriptor.h" + +DeviceDescriptor::DeviceDescriptor(const DeviceDescriptorId &id, const DeviceClassId &deviceClassId, const QString &title, const QString &description) : + m_id(id), + m_deviceClassId(deviceClassId), + m_title(title), + m_description(description) +{ +} + +DeviceDescriptorId DeviceDescriptor::id() const +{ + return m_id; +} + +DeviceClassId DeviceDescriptor::deviceClassId() const +{ + return m_deviceClassId; +} + +QString DeviceDescriptor::title() const +{ + return m_title; +} + +void DeviceDescriptor::setTitle(const QString &title) +{ + m_title = title; +} + +QString DeviceDescriptor::description() const +{ + return m_description; +} + +void DeviceDescriptor::setDescription(const QString &description) +{ + m_description = description; +} + +QVariantMap DeviceDescriptor::params() const +{ + return m_params; +} + +void DeviceDescriptor::setParams(const QVariantMap ¶ms) +{ + m_params = params; +} diff --git a/libguh/plugin/devicedescriptor.h b/libguh/plugin/devicedescriptor.h new file mode 100644 index 00000000..71fb9559 --- /dev/null +++ b/libguh/plugin/devicedescriptor.h @@ -0,0 +1,33 @@ +#ifndef DEVICEDESCRIPTION_H +#define DEVICEDESCRIPTION_H + +#include + +#include + +class DeviceDescriptor +{ +public: + DeviceDescriptor(const DeviceDescriptorId &id, const DeviceClassId &deviceClassId, const QString &title = QString(), const QString &description = QString()); + + DeviceDescriptorId id() const; + DeviceClassId deviceClassId() const; + + QString title() const; + void setTitle(const QString &title); + + QString description() const; + void setDescription(const QString &description); + + QVariantMap params() const; + void setParams(const QVariantMap ¶ms); + +private: + DeviceDescriptorId m_id; + DeviceClassId m_deviceClassId; + QString m_title; + QString m_description; + QVariantMap m_params; +}; + +#endif // DEVICEDESCRIPTION_H diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index 04ecd0b5..39291103 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -126,9 +126,11 @@ bool DevicePlugin::configureAutoDevice(QList loadedDevices, Device *dev return false; } -QList DevicePlugin::discoveredDevices(const DeviceClassId &deviceClassId) const +DeviceManager::DeviceError DevicePlugin::discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap ¶ms) const { - return QList(); + Q_UNUSED(deviceClassId) + Q_UNUSED(params) + return DeviceManager::DeviceErrorCreationMethodNotSupported; } /*! This will be called when a new device is created. The plugin has the chance to do some setup. diff --git a/libguh/plugin/deviceplugin.h b/libguh/plugin/deviceplugin.h index e88be521..38427f3e 100644 --- a/libguh/plugin/deviceplugin.h +++ b/libguh/plugin/deviceplugin.h @@ -49,7 +49,7 @@ public: virtual DeviceManager::HardwareResources requiredHardware() const = 0; virtual bool configureAutoDevice(QList loadedDevices, Device *device) const; - virtual QList discoveredDevices(const DeviceClassId &deviceClassId) const; + virtual DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap ¶ms) const; virtual bool deviceCreated(Device *device); virtual void deviceRemoved(Device *device); @@ -67,6 +67,7 @@ public slots: signals: void emitEvent(const Event &event); + void devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors); protected: DeviceManager *deviceManager() const; diff --git a/libguh/typeutils.h b/libguh/typeutils.h index 59f54752..4b5c6a31 100644 --- a/libguh/typeutils.h +++ b/libguh/typeutils.h @@ -18,11 +18,11 @@ Q_DECLARE_METATYPE(type##Id); DECLARE_TYPE_ID(Vendor) DECLARE_TYPE_ID(DeviceClass) DECLARE_TYPE_ID(Device) +DECLARE_TYPE_ID(DeviceDescriptor) DECLARE_TYPE_ID(EventType) DECLARE_TYPE_ID(StateType) DECLARE_TYPE_ID(ActionType) DECLARE_TYPE_ID(Plugin) - #endif // TYPEUTILS_H diff --git a/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp b/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp index f0abd643..a32e86d3 100644 --- a/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp +++ b/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp @@ -289,8 +289,9 @@ QList DevicePluginOpenweathermap::supportedDevices() const return ret; } -QList DevicePluginOpenweathermap::discoveredDevices(const DeviceClassId &deviceClassId) const +DeviceManager::DeviceError DevicePluginOpenweathermap::discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap ¶ms) const { + qDebug() << "should discover divces for" << deviceClassId << params; // m_openweaher-> } diff --git a/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.h b/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.h index f526e807..9a54cf6c 100644 --- a/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.h +++ b/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.h @@ -38,7 +38,7 @@ public: QList supportedVendors() const override; QList supportedDevices() const override; - QList discoveredDevices(const DeviceClassId &deviceClassId) const; + DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap ¶ms) const override; DeviceManager::HardwareResources requiredHardware() const override; diff --git a/server/jsonrpc/actionhandler.cpp b/server/jsonrpc/actionhandler.cpp index 93234eea..7db2f734 100644 --- a/server/jsonrpc/actionhandler.cpp +++ b/server/jsonrpc/actionhandler.cpp @@ -43,7 +43,7 @@ QString ActionHandler::name() const return "Actions"; } -QVariantMap ActionHandler::ExecuteAction(const QVariantMap ¶ms) +JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms) { DeviceId deviceId(params.value("deviceId").toString()); @@ -81,5 +81,5 @@ QVariantMap ActionHandler::ExecuteAction(const QVariantMap ¶ms) returns.insert("success", false); } - return returns; + return createReply(returns); } diff --git a/server/jsonrpc/actionhandler.h b/server/jsonrpc/actionhandler.h index 074ea9c6..9eed3f07 100644 --- a/server/jsonrpc/actionhandler.h +++ b/server/jsonrpc/actionhandler.h @@ -29,7 +29,7 @@ public: QString name() const; - Q_INVOKABLE QVariantMap ExecuteAction(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* ExecuteAction(const QVariantMap ¶ms); }; #endif // ACTIONHANDLER_H diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index 66e46b7d..8c4d4049 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -85,6 +85,20 @@ DeviceHandler::DeviceHandler(QObject *parent) : returns.insert("devices", devices); setReturns("GetConfiguredDevices", returns); + params.clear(); returns.clear(); + setDescription("GetDiscoveredDevices", "Performs a device discovery and returns the results. This function may take a while to return."); + params.insert("deviceClassId", "uuid"); + QVariantList discoveryParams; + discoveryParams.append(JsonTypes::paramRef()); + params.insert("o:discoveryParams", discoveryParams); + setParams("GetDiscoveredDevices", params); + returns.insert("success", "bool"); + returns.insert("errorMessage", "string"); + QVariantList deviceDescriptors; + deviceDescriptors.append(JsonTypes::deviceDescriptorRef()); + returns.insert("o:deviceDescriptors", deviceDescriptors); + setReturns("GetDiscoveredDevices", returns); + params.clear(); returns.clear(); setDescription("RemoveConfiguredDevice", "Remove a device from the system."); params.insert("deviceId", "uuid"); @@ -146,7 +160,7 @@ QString DeviceHandler::name() const return "Devices"; } -QVariantMap DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const { QVariantMap returns; QVariantList supportedVendors; @@ -154,10 +168,10 @@ QVariantMap DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const supportedVendors.append(JsonTypes::packVendor(vendor)); } returns.insert("vendors", supportedVendors); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const { QVariantMap returns; QVariantList supportedDeviceList; @@ -171,10 +185,37 @@ QVariantMap DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const supportedDeviceList.append(JsonTypes::packDeviceClass(deviceClass)); } returns.insert("deviceClasses", supportedDeviceList); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetPlugins(const QVariantMap ¶ms) const +JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap ¶ms) const +{ + QVariantMap returns; + + DeviceClassId deviceClassId = DeviceClassId(params.value("deviceClassId").toString()); + + DeviceManager::DeviceError status = GuhCore::instance()->deviceManager()->discoverDevices(deviceClassId, params.value("discoveryParams").toMap()); + switch (status) { + case DeviceManager::DeviceErrorNoError: { + JsonReply *reply = createAsyncReply("GetDiscoveredDevices"); + m_discoverRequests.insert(deviceClassId, reply); + return reply; + } + case DeviceManager::DeviceErrorDeviceClassNotFound: + returns.insert("errorMessage", "Cannot discover devices. Unknown DeviceClassId."); + break; + case DeviceManager::DeviceErrorPluginNotFound: + returns.insert("errorMessage", "Cannot discover devices. Plugin for DeviceClass not found."); + break; + default: + returns.insert("errorMessage", QString("Unknown error %1").arg(status)); + } + + returns.insert("success", false); + return createReply(returns); +} + +JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms) const { Q_UNUSED(params) QVariantMap returns; @@ -187,18 +228,23 @@ QVariantMap DeviceHandler::GetPlugins(const QVariantMap ¶ms) const plugins.append(pluginMap); } returns.insert("plugins", plugins); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::SetPluginConfiguration(const QVariantMap ¶ms) +JsonReply* DeviceHandler::SetPluginConfiguration(const QVariantMap ¶ms) { + QVariantMap returns; PluginId pluginId = PluginId(params.value("pluginId").toString()); QVariantMap pluginParams = params.value("pluginParams").toMap(); GuhCore::instance()->deviceManager()->setPluginConfig(pluginId, pluginParams); - return QVariantMap(); + + // TODO: handle return values + returns.insert("errorMessage", QString()); + returns.insert("success", true); + return createReply(returns); } -QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) +JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) { DeviceClassId deviceClass(params.value("deviceClassId").toString()); QVariantMap deviceParams = params.value("deviceParams").toMap(); @@ -223,7 +269,7 @@ QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) returns.insert("errorMessage", "Error creating device. Device setup failed."); returns.insert("success", false); break; - case DeviceManager::DeviceErrorCreationNotSupported: + case DeviceManager::DeviceErrorCreationMethodNotSupported: returns.insert("errorMessage", "Error creating device. This device can't be created this way."); returns.insert("success", false); break; @@ -235,10 +281,10 @@ QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) returns.insert("errorMessage", "Unknown error."); returns.insert("success", false); } - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetConfiguredDevices(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap ¶ms) const { Q_UNUSED(params) QVariantMap returns; @@ -247,28 +293,28 @@ QVariantMap DeviceHandler::GetConfiguredDevices(const QVariantMap ¶ms) const configuredDeviceList.append(JsonTypes::packDevice(device)); } returns.insert("devices", configuredDeviceList); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::RemoveConfiguredDevice(const QVariantMap ¶ms) +JsonReply* DeviceHandler::RemoveConfiguredDevice(const QVariantMap ¶ms) { QVariantMap returns; switch(GuhCore::instance()->deviceManager()->removeConfiguredDevice(DeviceId(params.value("deviceId").toString()))) { case DeviceManager::DeviceErrorNoError: returns.insert("success", true); returns.insert("errorMessage", ""); - return returns; + return createReply(returns); case DeviceManager::DeviceErrorDeviceNotFound: returns.insert("success", false); returns.insert("errorMessage", "No such device."); - return returns; + return createReply(returns); } returns.insert("success", false); returns.insert("errorMessage", "Unknown error."); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetEventTypes(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetEventTypes(const QVariantMap ¶ms) const { QVariantMap returns; @@ -278,10 +324,10 @@ QVariantMap DeviceHandler::GetEventTypes(const QVariantMap ¶ms) const eventList.append(JsonTypes::packEventType(eventType)); } returns.insert("eventTypes", eventList); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetActionTypes(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetActionTypes(const QVariantMap ¶ms) const { QVariantMap returns; @@ -291,10 +337,10 @@ QVariantMap DeviceHandler::GetActionTypes(const QVariantMap ¶ms) const actionList.append(JsonTypes::packActionType(actionType)); } returns.insert("actionTypes", actionList); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetStateTypes(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetStateTypes(const QVariantMap ¶ms) const { QVariantMap returns; @@ -304,10 +350,10 @@ QVariantMap DeviceHandler::GetStateTypes(const QVariantMap ¶ms) const stateList.append(JsonTypes::packStateType(stateType)); } returns.insert("stateTypes", stateList); - return returns; + return createReply(returns); } -QVariantMap DeviceHandler::GetStateValue(const QVariantMap ¶ms) const +JsonReply* DeviceHandler::GetStateValue(const QVariantMap ¶ms) const { QVariantMap returns; @@ -315,19 +361,19 @@ QVariantMap DeviceHandler::GetStateValue(const QVariantMap ¶ms) const if (!device) { returns.insert("success", false); returns.insert("errorMessage", "No such device"); - return returns; + return createReply(returns); } if (!device->hasState(params.value("stateTypeId").toUuid())) { returns.insert("success", false); returns.insert("errorMessage", QString("Device %1 %2 doesn't have such a state.").arg(device->name()).arg(device->id().toString())); - return returns; + return createReply(returns); } QVariant stateValue = device->stateValue(params.value("stateTypeId").toUuid()); returns.insert("success", true); returns.insert("errorMessage", ""); returns.insert("value", stateValue); - return returns; + return createReply(returns); } void DeviceHandler::deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value) @@ -339,3 +385,23 @@ void DeviceHandler::deviceStateChanged(Device *device, const QUuid &stateTypeId, emit StateChanged(params); } + +void DeviceHandler::devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors) +{ + if (!m_discoverRequests.contains(deviceClassId)) { + return; // We didn't start this discovery... Ignore it. + } + + JsonReply *reply = m_discoverRequests.take(deviceClassId); + QVariantList list; + foreach (const DeviceDescriptor &descriptor, deviceDescriptors) { + list.append(JsonTypes::packDeviceDescriptor(descriptor)); + } + QVariantMap returns; + returns.insert("deviceDescriptors", list); + returns.insert("success", true); + returns.insert("errorMessage", ""); + + reply->setData(returns); + reply->finished(); +} diff --git a/server/jsonrpc/devicehandler.h b/server/jsonrpc/devicehandler.h index bebd9640..0dd61ca7 100644 --- a/server/jsonrpc/devicehandler.h +++ b/server/jsonrpc/devicehandler.h @@ -29,33 +29,41 @@ public: QString name() const override; - Q_INVOKABLE QVariantMap GetSupportedVendors(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetSupportedVendors(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap GetSupportedDevices(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetSupportedDevices(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap GetPlugins(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetDiscoveredDevices(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap SetPluginConfiguration(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* GetPlugins(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap AddConfiguredDevice(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* SetPluginConfiguration(const QVariantMap ¶ms); - Q_INVOKABLE QVariantMap GetConfiguredDevices(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* AddConfiguredDevice(const QVariantMap ¶ms); - Q_INVOKABLE QVariantMap RemoveConfiguredDevice(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* GetConfiguredDevices(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap GetEventTypes(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* RemoveConfiguredDevice(const QVariantMap ¶ms); - Q_INVOKABLE QVariantMap GetActionTypes(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetEventTypes(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap GetStateTypes(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetActionTypes(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap GetStateValue(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetStateTypes(const QVariantMap ¶ms) const; + + Q_INVOKABLE JsonReply* GetStateValue(const QVariantMap ¶ms) const; signals: void StateChanged(const QVariantMap ¶ms); private slots: void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value); + + void devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors); + +private: + // A cache for async replies + mutable QHash m_discoverRequests; }; #endif // DEVICEHANDLER_H diff --git a/server/jsonrpc/jsonhandler.cpp b/server/jsonrpc/jsonhandler.cpp index d82ed906..4b4ebd88 100644 --- a/server/jsonrpc/jsonhandler.cpp +++ b/server/jsonrpc/jsonhandler.cpp @@ -119,3 +119,90 @@ void JsonHandler::setReturns(const QString &methodName, const QVariantMap &retur } qWarning() << "Cannot set returns. No such method:" << methodName; } + +JsonReply *JsonHandler::createReply(const QVariantMap &data) const +{ + return JsonReply::createReply(const_cast(this), data); +} + +JsonReply* JsonHandler::createAsyncReply(const QString &method) const +{ + return JsonReply::createAsyncReply(const_cast(this), method); +} + + +JsonReply::JsonReply(Type type, JsonHandler *handler, const QString &method, const QVariantMap &data): + m_handler(handler), + m_method(method), + m_type(type), + m_data(data) +{ + connect(&m_timeout, &QTimer::timeout, this, &JsonReply::timeout); +} + +JsonReply *JsonReply::createReply(JsonHandler *handler, const QVariantMap &data) +{ + return new JsonReply(TypeSync, handler, QString(), data); +} + +JsonReply *JsonReply::createAsyncReply(JsonHandler *handler, const QString &method) +{ + return new JsonReply(TypeAsync, handler, method); +} + +JsonReply::Type JsonReply::type() const +{ + return m_type; +} + +QVariantMap JsonReply::data() const +{ + return m_data; +} + +void JsonReply::setData(const QVariantMap &data) +{ + m_data = data; +} + +JsonHandler *JsonReply::handler() const +{ + return m_handler; +} + +QString JsonReply::method() const +{ + return m_method; +} + +QUuid JsonReply::clientId() const +{ + return m_clientId; +} + +void JsonReply::setClientId(const QUuid &clientId) +{ + m_clientId = clientId; +} + +int JsonReply::commandId() const +{ + return m_commandId; +} + +void JsonReply::setCommandId(int commandId) +{ + m_commandId = commandId; +} + +void JsonReply::startWait() +{ + m_timeout.start(5000); +} + +void JsonReply::timeout() +{ + m_data.insert("success", false); + m_data.insert("errorMessage", "Command timed out."); + finished(); +} diff --git a/server/jsonrpc/jsonhandler.h b/server/jsonrpc/jsonhandler.h index a449cd3b..0e653867 100644 --- a/server/jsonrpc/jsonhandler.h +++ b/server/jsonrpc/jsonhandler.h @@ -24,6 +24,57 @@ #include #include #include +#include + +class JsonHandler; + +class JsonReply: public QObject +{ + Q_OBJECT +public: + enum Type { + TypeSync, + TypeAsync + }; + + static JsonReply* createReply(JsonHandler *handler, const QVariantMap &data); + static JsonReply* createAsyncReply(JsonHandler *handler, const QString &method); + + Type type() const; + QVariantMap data() const; + void setData(const QVariantMap &data); + + JsonHandler *handler() const; + QString method() const; + + QUuid clientId() const; + void setClientId(const QUuid &clientId); + + int commandId() const; + void setCommandId(int commandId); + +public slots: + void startWait(); + +signals: + void finished(); + +private slots: + void timeout(); + +private: + JsonReply(Type type, JsonHandler *handler, const QString &method, const QVariantMap &data = QVariantMap()); + Type m_type; + QVariantMap m_data; + + JsonHandler *m_handler; + QString m_method; + QUuid m_clientId; + int m_commandId; + + QTimer m_timeout; + +}; class JsonHandler : public QObject { @@ -39,11 +90,17 @@ public: QPair validateParams(const QString &methodName, const QVariantMap ¶ms); QPair validateReturns(const QString &methodName, const QVariantMap &returns); +signals: + void asyncReply(int id, const QVariantMap ¶ms); + protected: void setDescription(const QString &methodName, const QString &description); void setParams(const QString &methodName, const QVariantMap ¶ms); void setReturns(const QString &methodName, const QVariantMap &returns); + JsonReply *createReply(const QVariantMap &data) const; + JsonReply *createAsyncReply(const QString &method) const; + private: QHash m_descriptions; QHash m_params; diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index d2d4bf2e..56ef342c 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -90,7 +90,7 @@ QString JsonRPCServer::name() const return QStringLiteral("JSONRPC"); } -QVariantMap JsonRPCServer::Introspect(const QVariantMap ¶ms) const +JsonReply* JsonRPCServer::Introspect(const QVariantMap ¶ms) const { Q_UNUSED(params) @@ -108,19 +108,19 @@ QVariantMap JsonRPCServer::Introspect(const QVariantMap ¶ms) const } data.insert("notifications", signalsMap); - return data; + return createReply(data); } -QVariantMap JsonRPCServer::Version(const QVariantMap ¶ms) const +JsonReply* JsonRPCServer::Version(const QVariantMap ¶ms) const { Q_UNUSED(params) QVariantMap data; data.insert("version", "0.0.0"); - return data; + return createReply(data); } -QVariantMap JsonRPCServer::SetNotificationStatus(const QVariantMap ¶ms) +JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap ¶ms) { QUuid clientId = this->property("clientId").toUuid(); // qDebug() << "got client socket" << clientId; @@ -129,7 +129,7 @@ QVariantMap JsonRPCServer::SetNotificationStatus(const QVariantMap ¶ms) returns.insert("success", "true"); returns.insert("errorMessage", "No error"); returns.insert("enabled", m_clients[clientId]); - return returns; + return createReply(returns); } void JsonRPCServer::setup() @@ -192,10 +192,19 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonDat // Hack: attach clientId to handler to be able to handle the JSONRPC methods. Do not use this outside of jsonrpcserver handler->setProperty("clientId", clientId); - 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).first); - sendResponse(clientId, commandId, returns); + JsonReply *reply; + QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params)); + if (reply->type() == JsonReply::TypeAsync) { + qDebug() << "got an async reply..."; + reply->setClientId(clientId); + reply->setCommandId(commandId); + connect(reply, &JsonReply::finished, this, &JsonRPCServer::asyncReplyFinished); + reply->startWait(); + } else { + Q_ASSERT((targetNamespace == "JSONRPC" && method == "Introspect") || handler->validateReturns(method, reply->data()).first); + sendResponse(clientId, commandId, reply->data()); + reply->deleteLater(); + } } void JsonRPCServer::sendNotification(const QVariantMap ¶ms) @@ -212,9 +221,24 @@ void JsonRPCServer::sendNotification(const QVariantMap ¶ms) m_tcpServer->sendData(m_clients.keys(true), jsonDoc.toJson()); } +void JsonRPCServer::sendAsyncReply(int id, const QVariantMap ¶ms) +{ + qDebug() << "should send async reply"; +} + +void JsonRPCServer::asyncReplyFinished() +{ + JsonReply *reply = qobject_cast(sender()); + qDebug() << "got async reply:" << reply->method() << reply->data(); + Q_ASSERT(reply->handler()->validateReturns(reply->method(), reply->data()).first); + sendResponse(reply->clientId(), reply->commandId(), reply->data()); + reply->deleteLater(); +} + void JsonRPCServer::registerHandler(JsonHandler *handler) { m_handlers.insert(handler->name(), handler); + connect(handler, &JsonHandler::asyncReply, this, &JsonRPCServer::sendAsyncReply); for (int i = 0; i < handler->metaObject()->methodCount(); ++i) { QMetaMethod method = handler->metaObject()->method(i); if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegExp("^[A-Z]"))) { diff --git a/server/jsonrpc/jsonrpcserver.h b/server/jsonrpc/jsonrpcserver.h index d87375b5..0643f052 100644 --- a/server/jsonrpc/jsonrpcserver.h +++ b/server/jsonrpc/jsonrpcserver.h @@ -44,9 +44,9 @@ public: // JsonHandler API implementation QString name() const; - Q_INVOKABLE QVariantMap Introspect(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap Version(const QVariantMap ¶ms) const; - Q_INVOKABLE QVariantMap SetNotificationStatus(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* Introspect(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* Version(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* SetNotificationStatus(const QVariantMap ¶ms); signals: void commandReceived(const QString &targetNamespace, const QString &command, const QVariantMap ¶ms); @@ -60,6 +60,9 @@ private slots: void processData(const QUuid &clientId, const QByteArray &jsonData); void sendNotification(const QVariantMap ¶ms); + void sendAsyncReply(int id, const QVariantMap ¶ms); + + void asyncReplyFinished(); private: void registerHandler(JsonHandler *handler); diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index e6a7ccda..aa59671f 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -44,6 +44,7 @@ QVariantMap JsonTypes::s_plugin; QVariantMap JsonTypes::s_vendor; QVariantMap JsonTypes::s_deviceClass; QVariantMap JsonTypes::s_device; +QVariantMap JsonTypes::s_deviceDescriptor; QVariantMap JsonTypes::s_rule; void JsonTypes::init() @@ -120,6 +121,11 @@ void JsonTypes::init() s_device.insert("name", "string"); s_device.insert("params", QVariantList() << paramRef()); + // DeviceDescription + s_deviceDescriptor.insert("id", "uuid"); + s_deviceDescriptor.insert("title", "string"); + s_deviceDescriptor.insert("description", "string"); + s_rule.insert("id", "uuid"); s_rule.insert("ruleType", ruleTypesRef()); s_rule.insert("events", QVariantList() << eventRef()); @@ -151,6 +157,7 @@ QVariantMap JsonTypes::allTypes() allTypes.insert("State", stateDescription()); allTypes.insert("Event", eventDescription()); allTypes.insert("Device", deviceDescription()); + allTypes.insert("DeviceDescriptor", deviceDescriptorDescription()); allTypes.insert("Action", actionDescription()); allTypes.insert("RuleType", ruleTypes()); allTypes.insert("Rule", ruleDescription()); @@ -252,6 +259,15 @@ QVariantMap JsonTypes::packDevice(Device *device) return variant; } +QVariantMap JsonTypes::packDeviceDescriptor(const DeviceDescriptor &descriptor) +{ + QVariantMap variant; + variant.insert("id", descriptor.id()); + variant.insert("title", descriptor.title()); + variant.insert("description", descriptor.description()); + return variant; +} + QVariantMap JsonTypes::packRule(const Rule &rule) { QVariantMap ruleMap; diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h index 6acea032..5a701147 100644 --- a/server/jsonrpc/jsontypes.h +++ b/server/jsonrpc/jsontypes.h @@ -20,6 +20,7 @@ #define JSONTYPES_H #include "plugin/deviceclass.h" +#include "plugin/devicedescriptor.h" #include "rule.h" #include "types/event.h" @@ -77,6 +78,7 @@ public: DECLARE_OBJECT(vendor, "Vendor") DECLARE_OBJECT(deviceClass, "DeviceClass") DECLARE_OBJECT(device, "Device") + DECLARE_OBJECT(deviceDescriptor, "DeviceDescriptor") DECLARE_OBJECT(rule, "Rule") static QVariantMap packEventType(const EventType &eventType); @@ -88,6 +90,7 @@ public: static QVariantMap packDeviceClass(const DeviceClass &deviceClass); static QVariantMap packPlugin(DevicePlugin *plugin); static QVariantMap packDevice(Device *device); + static QVariantMap packDeviceDescriptor(const DeviceDescriptor &descriptor); static QVariantMap packRule(const Rule &rule); static QPair validateMap(const QVariantMap &templateMap, const QVariantMap &map); diff --git a/server/jsonrpc/ruleshandler.cpp b/server/jsonrpc/ruleshandler.cpp index 6752f86e..5a9964c6 100644 --- a/server/jsonrpc/ruleshandler.cpp +++ b/server/jsonrpc/ruleshandler.cpp @@ -60,7 +60,7 @@ QString RulesHandler::name() const return "Rules"; } -QVariantMap RulesHandler::GetRules(const QVariantMap ¶ms) +JsonReply* RulesHandler::GetRules(const QVariantMap ¶ms) { Q_UNUSED(params) @@ -73,10 +73,10 @@ QVariantMap RulesHandler::GetRules(const QVariantMap ¶ms) QVariantMap returns; returns.insert("rules", rulesList); - return returns; + return createReply(returns); } -QVariantMap RulesHandler::AddRule(const QVariantMap ¶ms) +JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) { QVariantMap eventMap = params.value("event").toMap(); @@ -99,7 +99,7 @@ QVariantMap RulesHandler::AddRule(const QVariantMap ¶ms) if (actions.count() == 0) { returns.insert("success", false); returns.insert("errorMessage", "Missing parameter: \"actions\"."); - return returns; + return createReply(returns); } switch(GuhCore::instance()->ruleEngine()->addRule(event, actions)) { @@ -119,10 +119,10 @@ QVariantMap RulesHandler::AddRule(const QVariantMap ¶ms) returns.insert("success", false); returns.insert("errorMessage", "Unknown error"); } - return returns; + return createReply(returns); } -QVariantMap RulesHandler::RemoveRule(const QVariantMap ¶ms) +JsonReply* RulesHandler::RemoveRule(const QVariantMap ¶ms) { QVariantMap returns; QUuid ruleId = params.value("ruleId").toUuid(); @@ -138,5 +138,5 @@ QVariantMap RulesHandler::RemoveRule(const QVariantMap ¶ms) returns.insert("success", false); returns.insert("errorMessage", "Unknown error"); } - return returns; + return createReply(returns); } diff --git a/server/jsonrpc/ruleshandler.h b/server/jsonrpc/ruleshandler.h index 9d287dc6..be34d4ff 100644 --- a/server/jsonrpc/ruleshandler.h +++ b/server/jsonrpc/ruleshandler.h @@ -29,10 +29,10 @@ public: QString name() const override; - Q_INVOKABLE QVariantMap GetRules(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* GetRules(const QVariantMap ¶ms); - Q_INVOKABLE QVariantMap AddRule(const QVariantMap ¶ms); - Q_INVOKABLE QVariantMap RemoveRule(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* AddRule(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* RemoveRule(const QVariantMap ¶ms); }; diff --git a/tests/scripts/getdiscovereddevices.sh b/tests/scripts/getdiscovereddevices.sh new file mode 100755 index 00000000..b0fe207a --- /dev/null +++ b/tests/scripts/getdiscovereddevices.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z $2 ]; then + echo "usage $0 host deviceclassid [paramname paramvalue]" +elif [ -z $3 ]; then + (echo '{"id":1, "method":"Devices.GetDiscoveredDevices", "params":{"deviceClassId":"'$2'"}}'; sleep 6) | nc $1 1234 +elif [ -z $4 ]; then + echo "usage $0 host deviceclassid [paramname paramvalue]" +else + (echo '{"id":1, "method":"Devices.GetDiscoveredDevices", "params":{"deviceClassId":"'$2'", "discoveryParams": {"'$3'":"'$4'"}}}'; sleep 6) | nc $1 1234 +fi