diff --git a/libnymea-core/devices/devicemanagerimplementation.cpp b/libnymea-core/devices/devicemanagerimplementation.cpp index 63901ab8..1f9a26bc 100644 --- a/libnymea-core/devices/devicemanagerimplementation.cpp +++ b/libnymea-core/devices/devicemanagerimplementation.cpp @@ -710,12 +710,40 @@ Device::DeviceError DeviceManagerImplementation::removeConfiguredDevice(const De return Device::DeviceErrorNoError; } -BrowserItems DeviceManagerImplementation::browseDevice(const DeviceId &deviceId, const QString &browser, const BrowserItemId &node) +Device::BrowseResult DeviceManagerImplementation::browseDevice(const DeviceId &deviceId, const QString &nodeId) { Q_UNUSED(deviceId) - Q_UNUSED(browser) - Q_UNUSED(node) - return BrowserItems(); + Q_UNUSED(nodeId) + + Device::BrowseResult result = createBrowseResult(); + + Device *device = m_configuredDevices.value(deviceId); + if (!device) { + qCWarning(dcDeviceManager()) << "Cannot browse device. No such device:" << deviceId.toString(); + result.status = Device::DeviceErrorDeviceNotFound; + return result; + } + + if (!device->deviceClass().browsable()) { + qCWarning(dcDeviceManager()) << "Cannot browse device. DeviceClass" << device->deviceClass().name() << "is not browsable."; + result.status = Device::DeviceErrorUnsupportedFeature; + return result; + } + + result = device->plugin()->browseDevice(device, result, nodeId); + return result; +} + +Device::DeviceError DeviceManagerImplementation::executeBrowserItem(const DeviceId &deviceId, const QString &nodeId) +{ + Device *device = m_configuredDevices.value(deviceId); + if (!device) { + return Device::DeviceErrorDeviceNotFound; + } + if (!device->deviceClass().browsable()) { + return Device::DeviceErrorUnsupportedFeature; + } + return device->plugin()->executeBrowserItem(device, nodeId); } QString DeviceManagerImplementation::translate(const PluginId &pluginId, const QString &string, const QLocale &locale) @@ -980,6 +1008,7 @@ void DeviceManagerImplementation::loadPlugin(DevicePlugin *pluginIface, const Pl connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManagerImplementation::slotPairingFinished); connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManagerImplementation::onAutoDevicesAppeared); connect(pluginIface, &DevicePlugin::autoDeviceDisappeared, this, &DeviceManagerImplementation::onAutoDeviceDisappeared); + connect(pluginIface, &DevicePlugin::browseRequestFinished, this, &DeviceManagerImplementation::browseRequestFinished); } diff --git a/libnymea-core/devices/devicemanagerimplementation.h b/libnymea-core/devices/devicemanagerimplementation.h index 66e2c7f6..86df086e 100644 --- a/libnymea-core/devices/devicemanagerimplementation.h +++ b/libnymea-core/devices/devicemanagerimplementation.h @@ -97,7 +97,8 @@ public: Device::DeviceError removeConfiguredDevice(const DeviceId &deviceId) override; - BrowserItems browseDevice(const DeviceId &deviceId, const QString &browser, const BrowserItemId &node = BrowserItemId()) override; + Device::BrowseResult browseDevice(const DeviceId &deviceId, const QString &nodeId = QString()) override; + Device::DeviceError executeBrowserItem(const DeviceId &deviceId, const QString &nodeId) override; QString translate(const PluginId &pluginId, const QString &string, const QLocale &locale) override; diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 85f57abe..5f2deaf4 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -283,6 +283,23 @@ DeviceHandler::DeviceHandler(QObject *parent) : returns.insert("o:values", states); setReturns("GetStateValues", returns); + params.clear(); returns.clear(); + setDescription("BrowseDevice", "Browse a device. If a DeviceClass indicates a device is browsable, this method will return the BrowserItems. If no parameter besides the deviceId is used, the root node of this device will be returned. Any returned item which is browsable can be passed as node. Results will be children of the given node."); + params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:nodeId", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("BrowseDevice", params); + returns.insert("deviceError", JsonTypes::deviceErrorRef()); + returns.insert("items", QVariantList() << JsonTypes::browserItemRef()); + setReturns("BrowseDevice", returns); + + params.clear(); returns.clear(); + setDescription("ExecuteBrowserItem", "Execute the item identified by nodeId on the given device."); + params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:nodeId", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("ExecuteBrowserItem", params); + returns.insert("deviceError", JsonTypes::deviceErrorRef()); + setReturns("ExecuteBrowserItem", returns); + // Notifications params.clear(); returns.clear(); setDescription("StateChanged", "Emitted whenever a State of a device changes."); @@ -329,6 +346,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : connect(NymeaCore::instance(), &NymeaCore::deviceSetupFinished, this, &DeviceHandler::deviceSetupFinished); connect(NymeaCore::instance(), &NymeaCore::deviceReconfigurationFinished, this, &DeviceHandler::deviceReconfigurationFinished); connect(NymeaCore::instance(), &NymeaCore::pairingFinished, this, &DeviceHandler::pairingFinished); + connect(NymeaCore::instance()->deviceManager(), &DeviceManager::browseRequestFinished, this, &DeviceHandler::browseRequestFinished); } /*! Returns the name of the \l{DeviceHandler}. In this case \b Devices.*/ @@ -669,14 +687,31 @@ JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms) const { QVariantMap returns; DeviceId deviceId = DeviceId(params.value("deviceId").toString()); - QString browser = params.value("browser").toString(); + QString nodeId = params.value("nodeId").toString(); - Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(deviceId); - if (!device) { - returns.insert("deviceError", JsonTypes::deviceErrorToString(Device::DeviceErrorDeviceNotFound)); - return createReply(returns); + Device::BrowseResult result = NymeaCore::instance()->deviceManager()->browseDevice(deviceId, nodeId); + + if (result.status == Device::DeviceErrorAsync ) { + JsonReply *reply = createAsyncReply("BrowseDevice"); + m_asyncBrowseRequests.insert(result.id, reply); + connect(reply, &JsonReply::finished, this, [this, result](){ + m_asyncBrowseRequests.remove(result.id); + }); + return reply; } - NymeaCore::instance()->deviceManager()->browseDevice(device->id(), browser); + + returns.insert("deviceError", JsonTypes::deviceErrorToString(result.status)); + returns.insert("items", JsonTypes::packBrowserItems(result.items)); + return createReply(returns); +} + +JsonReply *DeviceHandler::ExecuteBrowserItem(const QVariantMap ¶ms) +{ + DeviceId deviceId = DeviceId(params.value("deviceId").toString()); + QString nodeId = params.value("nodeId").toString(); + Device::DeviceError status = NymeaCore::instance()->deviceManager()->executeBrowserItem(deviceId, nodeId); + QVariantMap returns; + returns.insert("deviceError", JsonTypes::deviceErrorToString(status)); return createReply(returns); } @@ -805,4 +840,18 @@ void DeviceHandler::pairingFinished(const PairingTransactionId &pairingTransacti m_asynDeviceAdditions.insert(deviceId, reply); } +void DeviceHandler::browseRequestFinished(const Device::BrowseResult &result) +{ + if (!m_asyncBrowseRequests.contains(result.id)) { + qCWarning(dcJsonRpc()) << "No pending JsonRpc reply. Did it time out?"; + return; + } + JsonReply *reply = m_asyncBrowseRequests.take(result.id); + QVariantMap params; + params.insert("deviceError", JsonTypes::deviceErrorToString(result.status)); + params.insert("items", JsonTypes::packBrowserItems(result.items)); + reply->setData(params); + reply->finished(); +} + } diff --git a/libnymea-core/jsonrpc/devicehandler.h b/libnymea-core/jsonrpc/devicehandler.h index d0994ef3..217de3ec 100644 --- a/libnymea-core/jsonrpc/devicehandler.h +++ b/libnymea-core/jsonrpc/devicehandler.h @@ -58,6 +58,7 @@ public: Q_INVOKABLE JsonReply *GetStateValues(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *BrowseDevice(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *ExecuteBrowserItem(const QVariantMap ¶ms); signals: void PluginConfigurationChanged(const QVariantMap ¶ms); @@ -88,12 +89,15 @@ private slots: void pairingFinished(const PairingTransactionId &pairingTransactionId, Device::DeviceError status, const DeviceId &deviceId); + void browseRequestFinished(const Device::BrowseResult &result); + private: // A cache for async replies mutable QHash m_discoverRequests; mutable QHash m_asynDeviceAdditions; mutable QHash m_asynDeviceEditAdditions; mutable QHash m_asyncPairingRequests; + mutable QHash m_asyncBrowseRequests; }; } diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 089175de..e2cd7d37 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -127,6 +127,7 @@ QVariantMap JsonTypes::s_tag; QVariantMap JsonTypes::s_mqttPolicy; QVariantMap JsonTypes::s_package; QVariantMap JsonTypes::s_repository; +QVariantMap JsonTypes::s_browserItem; void JsonTypes::init() { @@ -273,6 +274,7 @@ void JsonTypes::init() s_deviceClass.insert("name", basicTypeToString(String)); s_deviceClass.insert("displayName", basicTypeToString(String)); s_deviceClass.insert("interfaces", QVariantList() << basicTypeToString(String)); + s_deviceClass.insert("browsable", basicTypeToString(Bool)); s_deviceClass.insert("setupMethod", setupMethodRef()); s_deviceClass.insert("createMethods", QVariantList() << createMethodRef()); s_deviceClass.insert("stateTypes", QVariantList() << stateTypeRef()); @@ -403,6 +405,7 @@ void JsonTypes::init() s_tag.insert("tagId", basicTypeToString(QVariant::String)); s_tag.insert("o:value", basicTypeToString(QVariant::String)); + // Package s_package.insert("id", basicTypeToString(QVariant::String)); s_package.insert("displayName", basicTypeToString(QVariant::String)); s_package.insert("summary", basicTypeToString(QVariant::String)); @@ -413,10 +416,19 @@ void JsonTypes::init() s_package.insert("rollbackAvailable", basicTypeToString(QVariant::Bool)); s_package.insert("canRemove", basicTypeToString(QVariant::Bool)); + // Repository s_repository.insert("id", basicTypeToString(QVariant::String)); s_repository.insert("displayName", basicTypeToString(QVariant::String)); s_repository.insert("enabled", basicTypeToString(QVariant::Bool)); + // BrowserItem + s_browserItem.insert("id", basicTypeToString(QVariant::String)); + s_browserItem.insert("displayName", basicTypeToString(QVariant::String)); + s_browserItem.insert("description", basicTypeToString(QVariant::String)); + s_browserItem.insert("thumbnail", basicTypeToString(QVariant::String)); + s_browserItem.insert("executable", basicTypeToString(QVariant::Bool)); + s_browserItem.insert("browsable", basicTypeToString(QVariant::Bool)); + s_initialized = true; } @@ -706,6 +718,18 @@ QVariantMap JsonTypes::packParam(const Param ¶m) return variantMap; } +QVariantMap JsonTypes::packBrowserItem(const BrowserItem &item) +{ + QVariantMap ret; + ret.insert("id", item.id()); + ret.insert("displayName", item.displayName()); + ret.insert("description", item.description()); + ret.insert("thumbnail", item.thumbnail()); + ret.insert("executable", item.executable()); + ret.insert("browsable", item.browsable()); + return ret; +} + QVariantList JsonTypes::packParams(const ParamList ¶mList) { QVariantList ret; @@ -790,6 +814,7 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo variant.insert("vendorId", deviceClass.vendorId().toString()); variant.insert("pluginId", deviceClass.pluginId().toString()); variant.insert("interfaces", deviceClass.interfaces()); + variant.insert("browsable", deviceClass.browsable()); QVariantList stateTypes; foreach (const StateType &stateType, deviceClass.stateTypes()) @@ -1186,6 +1211,15 @@ QVariantList JsonTypes::packDeviceDescriptors(const QList devi return deviceDescriptorList; } +QVariantList JsonTypes::packBrowserItems(const BrowserItems &items) +{ + QVariantList ret; + foreach (const BrowserItem &item, items) { + ret.append(packBrowserItem(item)); + } + return ret; +} + /*! Returns a variant map with the current basic configuration of the server. */ QVariantMap JsonTypes::packBasicConfiguration() { @@ -2080,6 +2114,12 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, qCWarning(dcJsonRpc) << "Repository not matching"; return result; } + } else if (refName == browserItemRef()) { + QPair result = validateMap(browserItemDescription(), variant.toMap()); + if (!result.first) { + qCWarning(dcJsonRpc) << "BrowserItem not matching"; + return result; + } } else if (refName == basicTypeRef()) { QPair result = validateBasicType(variant); if (!result.first) { diff --git a/libnymea-core/jsonrpc/jsontypes.h b/libnymea-core/jsonrpc/jsontypes.h index cacb1636..33ac9e2d 100644 --- a/libnymea-core/jsonrpc/jsontypes.h +++ b/libnymea-core/jsonrpc/jsontypes.h @@ -180,6 +180,7 @@ public: DECLARE_OBJECT(mqttPolicy, "MqttPolicy") DECLARE_OBJECT(package, "Package") DECLARE_OBJECT(repository, "Repository") + DECLARE_OBJECT(browserItem, "BrowserItem") // pack types static QVariantMap packEventType(const EventType &eventType, const PluginId &pluginId, const QLocale &locale); @@ -194,7 +195,7 @@ public: static QVariantMap packStateDescriptor(const StateDescriptor &stateDescriptor); static QVariantMap packStateEvaluator(const StateEvaluator &stateEvaluator); static QVariantMap packParam(const Param ¶m); - static QVariantList packParams(const ParamList ¶mList); + static QVariantMap packBrowserItem(const BrowserItem &item); static QVariantMap packParamType(const ParamType ¶mType, const PluginId &pluginId, const QLocale &locale); static QVariantMap packParamDescriptor(const ParamDescriptor ¶mDescriptor); static QVariantMap packVendor(const Vendor &vendor, const QLocale &locale); @@ -214,6 +215,8 @@ public: static QVariantMap packWiredNetworkDevice(WiredNetworkDevice *networkDevice); static QVariantMap packWirelessNetworkDevice(WirelessNetworkDevice *networkDevice); + static QVariantList packParams(const ParamList ¶mList); + static QVariantList packBrowserItems(const BrowserItems &items); static QVariantList packRules(const QList rules); static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods); static QVariantList packSupportedVendors(const QLocale &locale); diff --git a/libnymea/devices/device.cpp b/libnymea/devices/device.cpp index 1de3160e..a0b24ada 100644 --- a/libnymea/devices/device.cpp +++ b/libnymea/devices/device.cpp @@ -154,6 +154,18 @@ PluginId Device::pluginId() const return m_plugin->pluginId(); } +/*! Returns the \l{DeviceClass} of this device. */ +DeviceClass Device::deviceClass() const +{ + return m_deviceClass; +} + +/*! Returns the the \l{DevicePlugin} this Device is managed by. */ +DevicePlugin *Device::plugin() const +{ + return m_plugin; +} + /*! Returns the name of this Device. This is visible to the user. */ QString Device::name() const { diff --git a/libnymea/devices/device.h b/libnymea/devices/device.h index 9f91d3a8..d304fdbe 100644 --- a/libnymea/devices/device.h +++ b/libnymea/devices/device.h @@ -30,6 +30,7 @@ #include "types/deviceclass.h" #include "types/state.h" #include "types/param.h" +#include "types/browseritem.h" #include #include @@ -69,7 +70,8 @@ public: DeviceErrorDeviceInRule, DeviceErrorDeviceIsChild, DeviceErrorPairingTransactionIdNotFound, - DeviceErrorParameterNotWritable + DeviceErrorParameterNotWritable, + DeviceErrorUnsupportedFeature, }; Q_ENUM(DeviceError) @@ -80,12 +82,22 @@ public: }; Q_ENUM(DeviceSetupStatus) + class BrowseResult { + public: + QUuid id; + Device::DeviceError status = Device::DeviceErrorNoError; + BrowserItems items; + private: + BrowseResult(): id(QUuid::createUuid()) {} + friend class DeviceManager; + }; + DeviceId id() const; DeviceClassId deviceClassId() const; PluginId pluginId() const; DeviceClass deviceClass() const; - DevicePlugin* plugin(); + DevicePlugin* plugin() const; QString name() const; void setName(const QString &name); diff --git a/libnymea/devices/devicemanager.cpp b/libnymea/devices/devicemanager.cpp index a2bb77e4..e9f0f5c0 100644 --- a/libnymea/devices/devicemanager.cpp +++ b/libnymea/devices/devicemanager.cpp @@ -39,3 +39,14 @@ DeviceManager::DeviceManager(QObject *parent) : QObject(parent) { } + +Device::BrowseResult DeviceManager::createBrowseResult() +{ + Device::BrowseResult result = Device::BrowseResult(); + return result; +} + +QUuid DeviceManager::browseResultId(const Device::BrowseResult &result) +{ + return result.id; +} diff --git a/libnymea/devices/devicemanager.h b/libnymea/devices/devicemanager.h index aee6693a..ef77f1cd 100644 --- a/libnymea/devices/devicemanager.h +++ b/libnymea/devices/devicemanager.h @@ -70,7 +70,8 @@ public: virtual Device::DeviceError removeConfiguredDevice(const DeviceId &deviceId) = 0; - virtual BrowserItems browseDevice(const DeviceId &deviceId, const QString &browser, const BrowserItemId &node = BrowserItemId()) = 0; + virtual Device::BrowseResult browseDevice(const DeviceId &deviceId, const QString &nodeId = QString()) = 0; + virtual Device::DeviceError executeBrowserItem(const DeviceId &deviceId, const QString &nodeId) = 0; virtual QString translate(const PluginId &pluginId, const QString &string, const QLocale &locale) = 0; @@ -88,7 +89,11 @@ signals: void deviceReconfigurationFinished(Device *device, Device::DeviceError status); void pairingFinished(const PairingTransactionId &pairingTransactionId, Device::DeviceError status, const DeviceId &deviceId = DeviceId()); void actionExecutionFinished(const ActionId &actionId, Device::DeviceError status); + void browseRequestFinished(const Device::BrowseResult &result); +protected: + Device::BrowseResult createBrowseResult(); + QUuid browseResultId(const Device::BrowseResult &result); }; #endif // DEVICEMANAGER_H diff --git a/libnymea/devices/deviceplugin.cpp b/libnymea/devices/deviceplugin.cpp index 46fc71e9..5d826aa7 100644 --- a/libnymea/devices/deviceplugin.cpp +++ b/libnymea/devices/deviceplugin.cpp @@ -248,6 +248,22 @@ Device::DeviceError DevicePlugin::executeAction(Device *device, const Action &ac return Device::DeviceErrorNoError; } +Device::BrowseResult DevicePlugin::browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId) +{ + Q_UNUSED(device) + Q_UNUSED(nodeId) + + result.status = Device::DeviceErrorUnsupportedFeature; + return result; +} + +Device::DeviceError DevicePlugin::executeBrowserItem(Device *device, const QString &nodeId) +{ + Q_UNUSED(device) + Q_UNUSED(nodeId) + return Device::DeviceErrorUnsupportedFeature; +} + /*! Returns the configuration description of this DevicePlugin as a list of \l{ParamType}{ParamTypes}. */ ParamTypes DevicePlugin::configurationDescription() const { diff --git a/libnymea/devices/deviceplugin.h b/libnymea/devices/deviceplugin.h index 037a5018..29e9e0f6 100644 --- a/libnymea/devices/deviceplugin.h +++ b/libnymea/devices/deviceplugin.h @@ -78,6 +78,9 @@ public: virtual Device::DeviceError executeAction(Device *device, const Action &action); + virtual Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId = QString()); + virtual Device::DeviceError executeBrowserItem(Device *device, const QString &nodeId); + // Configuration ParamTypes configurationDescription() const; Device::DeviceError setConfiguration(const ParamList &configuration); @@ -96,6 +99,7 @@ signals: void configValueChanged(const ParamTypeId ¶mTypeId, const QVariant &value); void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList &deviceDescriptors); void autoDeviceDisappeared(const DeviceId &deviceId); + void browseRequestFinished(const Device::BrowseResult &result); protected: Devices myDevices() const; diff --git a/libnymea/devices/pluginmetadata.cpp b/libnymea/devices/pluginmetadata.cpp index eb7ccceb..5d11cfd2 100644 --- a/libnymea/devices/pluginmetadata.cpp +++ b/libnymea/devices/pluginmetadata.cpp @@ -162,7 +162,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) QJsonObject deviceClassObject = deviceClassJson.toObject(); /*! Returns a list of all valid JSON properties a DeviceClass JSON definition can have. */ QStringList deviceClassProperties = QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod" - << "interfaces" << "pairingInfo" << "discoveryParamTypes" << "discoveryParamTypes" + << "interfaces" << "browsable" << "pairingInfo" << "discoveryParamTypes" << "discoveryParamTypes" << "paramTypes" << "settingsTypes" << "stateTypes" << "actionTypes" << "eventTypes"; QStringList mandatoryDeviceClassProperties = QStringList() << "id" << "name" << "displayName"; @@ -192,6 +192,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) DeviceClass deviceClass(pluginId(), vendorId, deviceClassId); deviceClass.setName(deviceClassName); deviceClass.setDisplayName(deviceClassObject.value("displayName").toString()); + deviceClass.setBrowsable(deviceClassObject.value("browsable").toBool()); // Read create methods DeviceClass::CreateMethods createMethods; diff --git a/libnymea/types/browseritem.cpp b/libnymea/types/browseritem.cpp index c95cdf36..7aecb491 100644 --- a/libnymea/types/browseritem.cpp +++ b/libnymea/types/browseritem.cpp @@ -1,6 +1,70 @@ #include "browseritem.h" -BrowserItem::BrowserItem() + +BrowserItem::BrowserItem(const QString &id, const QString &displayName, bool browsable): + m_id(id), + m_displayName(displayName), + m_browsable(browsable) +{ + +} + +QString BrowserItem::id() const +{ + return m_id; +} + +QString BrowserItem::displayName() const +{ + return m_displayName; +} + +QString BrowserItem::description() const +{ + return m_description; +} + +void BrowserItem::setDescription(const QString &description) +{ + m_description = description; +} + +bool BrowserItem::executable() const +{ + return m_executable; +} + +void BrowserItem::setExecutable(bool executable) +{ + m_executable = executable; +} + +bool BrowserItem::browsable() const +{ + return m_browsable; +} + +void BrowserItem::setBrowsable(bool browsable) +{ + m_browsable = browsable; +} + +QString BrowserItem::thumbnail() const +{ + return m_thumbnail; +} + +void BrowserItem::setThumbnail(const QString &thumbnail) +{ + m_thumbnail = thumbnail; +} + +BrowserItems::BrowserItems() +{ + +} + +BrowserItems::BrowserItems(const QList &other): QList(other) { } diff --git a/libnymea/types/browseritem.h b/libnymea/types/browseritem.h index ad5e301c..43dbafdc 100644 --- a/libnymea/types/browseritem.h +++ b/libnymea/types/browseritem.h @@ -9,12 +9,41 @@ class LIBNYMEA_EXPORT BrowserItem { public: - BrowserItem(); + BrowserItem(const QString &id = QString(), const QString &displayName = QString(), bool browsable = false); + + QString id() const; + void setId(const QString &id); + + QString displayName() const; + void setDisplayName(const QString &displayName); + + QString description() const; + void setDescription(const QString &description); + + bool executable() const; + void setExecutable(bool executable); + + bool browsable() const; + void setBrowsable(bool browsable); + + QString thumbnail() const; + void setThumbnail(const QString &thumbnail); + +private: + QString m_id; + QString m_displayName; + QString m_description; + bool m_executable = false; + bool m_browsable = false; + QString m_thumbnail; }; class LIBNYMEA_EXPORT BrowserItems: public QList { +public: + BrowserItems(); + BrowserItems(const QList &other); }; diff --git a/libnymea/types/deviceclass.cpp b/libnymea/types/deviceclass.cpp index c7641d9d..08dbb71f 100644 --- a/libnymea/types/deviceclass.cpp +++ b/libnymea/types/deviceclass.cpp @@ -308,6 +308,16 @@ void DeviceClass::setInterfaces(const QStringList &interfaces) m_interfaces = interfaces; } +bool DeviceClass::browsable() const +{ + return m_browsable; +} + +void DeviceClass::setBrowsable(bool browsable) +{ + m_browsable = browsable; +} + /*! Compare this \a deviceClass to another. This is effectively the same as calling a.id() == b.id(). Returns true if the ids match.*/ bool DeviceClass::operator==(const DeviceClass &deviceClass) const { diff --git a/libnymea/types/deviceclass.h b/libnymea/types/deviceclass.h index cd5bf07d..111888ea 100644 --- a/libnymea/types/deviceclass.h +++ b/libnymea/types/deviceclass.h @@ -105,6 +105,9 @@ public: QStringList interfaces() const; void setInterfaces(const QStringList &interfaces); + bool browsable() const; + void setBrowsable(bool browsable); + bool operator==(const DeviceClass &device) const; private: @@ -113,6 +116,7 @@ private: PluginId m_pluginId; QString m_name; QString m_displayName; + bool m_browsable = false; StateTypes m_stateTypes; EventTypes m_eventTypes; ActionTypes m_actionTypes; diff --git a/libnymea/typeutils.h b/libnymea/typeutils.h index 8aced417..2066d7a2 100644 --- a/libnymea/typeutils.h +++ b/libnymea/typeutils.h @@ -57,11 +57,10 @@ DECLARE_TYPE_ID(ActionType) DECLARE_TYPE_ID(Action) DECLARE_TYPE_ID(Plugin) DECLARE_TYPE_ID(Rule) +DECLARE_TYPE_ID(Browser) DECLARE_TYPE_ID(PairingTransaction) -DECLARE_TYPE_ID(BrowserItem) - class LIBNYMEA_EXPORT Types { Q_GADGET @@ -153,6 +152,10 @@ public: }; Q_ENUM(StateOperator) + enum BrowserType { + BrowserTypeGeneric, + }; + Q_ENUM(BrowserType) }; Q_DECLARE_METATYPE(Types::InputType) diff --git a/plugins/mock/devicepluginmock.cpp b/plugins/mock/devicepluginmock.cpp index 2c4f94e4..4bd5e398 100644 --- a/plugins/mock/devicepluginmock.cpp +++ b/plugins/mock/devicepluginmock.cpp @@ -239,6 +239,30 @@ Device::DeviceError DevicePluginMock::displayPin(const PairingTransactionId &pai return Device::DeviceErrorNoError; } +Device::BrowseResult DevicePluginMock::browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId) +{ + qCDebug(dcMockDevice()) << "Browse device called" << device; + if (device->deviceClassId() == mockDeviceClassId) { + if (device->paramValue(mockDeviceAsyncParamTypeId).toBool()) { + result.status = Device::DeviceErrorAsync; + QTimer::singleShot(1000, device, [this, device, result, nodeId]() mutable { + if (device->paramValue(mockDeviceBrokenParamTypeId).toBool()) { + result.status = Device::DeviceErrorHardwareFailure; + } else { + result = generateBrowseItems(nodeId, result); + } + emit browseRequestFinished(result); + }); + } + else if (device->paramValue(mockDeviceBrokenParamTypeId).toBool()) { + result.status = Device::DeviceErrorHardwareFailure; + } else { + result = generateBrowseItems(nodeId, result); + } + } + return result; +} + Device::DeviceError DevicePluginMock::executeAction(Device *device, const Action &action) { if (!myDevices().contains(device)) @@ -566,3 +590,38 @@ void DevicePluginMock::onPluginConfigChanged() { } + +Device::BrowseResult DevicePluginMock::generateBrowseItems(const QString &nodeId, Device::BrowseResult result) +{ + result.status = Device::DeviceErrorNoError; + + if (nodeId.isEmpty()) { + result.items.append(BrowserItem("0", "Item 0", true)); + result.items.append(BrowserItem("1", "Item 1")); + result.items.append(BrowserItem("2", "Item 2", true)); + result.items.append(BrowserItem("3", "Item 3")); + result.items.append(BrowserItem("4", "Item 4")); + } + else if (nodeId == "0") { + result.items.append(BrowserItem("5", "Item 5")); + result.items.append(BrowserItem("6", "Item 6")); + result.items.append(BrowserItem("7", "Item 7")); + result.items.append(BrowserItem("8", "Item 8")); + result.items.append(BrowserItem("9", "Item 9")); + } + else if (nodeId == "2") { + result.items.append(BrowserItem("10", "Item 10", true)); + result.items.append(BrowserItem("11", "Item 11")); + result.items.append(BrowserItem("12", "Item 12")); + result.items.append(BrowserItem("13", "Item 13")); + result.items.append(BrowserItem("14", "Item 14")); + } + else if (nodeId == "10") { + result.items.append(BrowserItem("15", "Item 15")); + result.items.append(BrowserItem("16", "Item 16")); + } else { + result.status = Device::DeviceErrorInvalidParameter; + } + + return result; +} diff --git a/plugins/mock/devicepluginmock.h b/plugins/mock/devicepluginmock.h index 49beefa8..2081fa24 100644 --- a/plugins/mock/devicepluginmock.h +++ b/plugins/mock/devicepluginmock.h @@ -52,6 +52,8 @@ public: Device::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret) override; Device::DeviceError displayPin(const PairingTransactionId &pairingTransactionId, const DeviceDescriptor &deviceDescriptor) override; + Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId = QString()) override; + public slots: Device::DeviceError executeAction(Device *device, const Action &action) override; @@ -72,6 +74,9 @@ private slots: void onChildDeviceDiscovered(const DeviceId &parentId); void onPluginConfigChanged(); +private: + Device::BrowseResult generateBrowseItems(const QString &nodeId, Device::BrowseResult result); + private: QHash m_daemons; QList m_asyncSetupDevices; diff --git a/plugins/mock/devicepluginmock.json b/plugins/mock/devicepluginmock.json index 0ea2282b..168f14ce 100644 --- a/plugins/mock/devicepluginmock.json +++ b/plugins/mock/devicepluginmock.json @@ -32,6 +32,7 @@ "displayName": "Mock Device", "interfaces": ["system", "light", "battery"], "createMethods": ["user", "discovery"], + "browsable": true, "discoveryParamTypes": [ { "id": "d222adb4-2f9c-4c3f-8655-76400d0fb6ce",