diff --git a/libnymea-core/devices/devicemanagerimplementation.cpp b/libnymea-core/devices/devicemanagerimplementation.cpp index 863bc7d8..85b2dff9 100644 --- a/libnymea-core/devices/devicemanagerimplementation.cpp +++ b/libnymea-core/devices/devicemanagerimplementation.cpp @@ -712,9 +712,6 @@ Device::DeviceError DeviceManagerImplementation::removeConfiguredDevice(const De Device::BrowseResult DeviceManagerImplementation::browseDevice(const DeviceId &deviceId, const QString &itemId, const QLocale &locale) { - Q_UNUSED(deviceId) - Q_UNUSED(itemId) - Device::BrowseResult result = createBrowseResult(); Device *device = m_configuredDevices.value(deviceId); @@ -734,6 +731,35 @@ Device::BrowseResult DeviceManagerImplementation::browseDevice(const DeviceId &d return result; } +Device::BrowserItemResult DeviceManagerImplementation::browserItemDetails(const DeviceId &deviceId, const QString &itemId, const QLocale &locale) +{ + Device::BrowserItemResult result = createBrowserItemResult(); + + 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()->browserItem(device, result, itemId, locale); + if (result.status == Device::DeviceErrorAsync) { + // Error or Async + return result; + } + if (result.status != Device::DeviceErrorNoError) { + qCWarning(dcDeviceManager()) << "Browse device failed:" << result.status; + return result; + } + return result; +} + Device::DeviceError DeviceManagerImplementation::executeBrowserItem(const BrowserAction &browserAction) { Device *device = m_configuredDevices.value(browserAction.deviceId()); @@ -1024,6 +1050,7 @@ void DeviceManagerImplementation::loadPlugin(DevicePlugin *pluginIface, const Pl connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManagerImplementation::onAutoDevicesAppeared); connect(pluginIface, &DevicePlugin::autoDeviceDisappeared, this, &DeviceManagerImplementation::onAutoDeviceDisappeared); connect(pluginIface, &DevicePlugin::browseRequestFinished, this, &DeviceManagerImplementation::browseRequestFinished); + connect(pluginIface, &DevicePlugin::browserItemRequestFinished, this, &DeviceManagerImplementation::browserItemRequestFinished); connect(pluginIface, &DevicePlugin::browserItemExecutionFinished, this, &DeviceManagerImplementation::browserItemExecutionFinished); } diff --git a/libnymea-core/devices/devicemanagerimplementation.h b/libnymea-core/devices/devicemanagerimplementation.h index 0362567e..75141c5d 100644 --- a/libnymea-core/devices/devicemanagerimplementation.h +++ b/libnymea-core/devices/devicemanagerimplementation.h @@ -100,6 +100,7 @@ public: Device::DeviceError executeAction(const Action &action) override; Device::BrowseResult browseDevice(const DeviceId &deviceId, const QString &itemId, const QLocale &locale) override; + Device::BrowserItemResult browserItemDetails(const DeviceId &deviceId, const QString &itemId, const QLocale &locale) override; Device::DeviceError executeBrowserItem(const BrowserAction &browserAction) override; Device::DeviceError executeBrowserItemAction(const BrowserItemAction &browserItemAction) override; diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 9a13983c..a5fc70db 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -292,6 +292,15 @@ DeviceHandler::DeviceHandler(QObject *parent) : returns.insert("items", QVariantList() << JsonTypes::browserItemRef()); setReturns("BrowseDevice", returns); + params.clear(); returns.clear(); + setDescription("GetBrowserItem", "Get a single item from the browser. This won't give any more info on an item than a regular browseDevice call, but it allows to fetch details of an item if only the ID is known."); + params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:itemId", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("GetBrowserItem", params); + returns.insert("deviceError", JsonTypes::deviceErrorRef()); + returns.insert("o:item", JsonTypes::browserItemRef()); + setReturns("GetBrowserItem", returns); + // Notifications params.clear(); returns.clear(); setDescription("StateChanged", "Emitted whenever a State of a device changes."); @@ -339,6 +348,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : 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); + connect(NymeaCore::instance()->deviceManager(), &DeviceManager::browserItemRequestFinished, this, &DeviceHandler::browserItemRequestFinished); } /*! Returns the name of the \l{DeviceHandler}. In this case \b Devices.*/ @@ -685,9 +695,9 @@ JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms) const if (result.status == Device::DeviceErrorAsync ) { JsonReply *reply = createAsyncReply("BrowseDevice"); - m_asyncBrowseRequests.insert(result.id, reply); + m_asyncBrowseRequests.insert(result.id(), reply); connect(reply, &JsonReply::finished, this, [this, result](){ - m_asyncBrowseRequests.remove(result.id); + m_asyncBrowseRequests.remove(result.id()); }); return reply; } @@ -697,6 +707,30 @@ JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms) const return createReply(returns); } +JsonReply *DeviceHandler::GetBrowserItem(const QVariantMap ¶ms) const +{ + QVariantMap returns; + DeviceId deviceId = DeviceId(params.value("deviceId").toString()); + QString itemId = params.value("itemId").toString(); + + Device::BrowserItemResult result = NymeaCore::instance()->deviceManager()->browserItemDetails(deviceId, itemId, params.value("locale").toLocale()); + + if (result.status == Device::DeviceErrorAsync ) { + JsonReply *reply = createAsyncReply("GetBrowserItem"); + m_asyncBrowseRequests.insert(result.id(), reply); + connect(reply, &JsonReply::finished, this, [this, result](){ + m_asyncBrowseRequests.remove(result.id()); + }); + return reply; + } + + returns.insert("deviceError", JsonTypes::deviceErrorToString(result.status)); + if (result.status == Device::DeviceErrorNoError) { + returns.insert("item", JsonTypes::packBrowserItem(result.item)); + } + return createReply(returns); +} + void DeviceHandler::pluginConfigChanged(const PluginId &id, const ParamList &config) { QVariantMap params; @@ -824,14 +858,31 @@ void DeviceHandler::pairingFinished(const PairingTransactionId &pairingTransacti void DeviceHandler::browseRequestFinished(const Device::BrowseResult &result) { - if (!m_asyncBrowseRequests.contains(result.id)) { + if (!m_asyncBrowseRequests.contains(result.id())) { qCWarning(dcJsonRpc()) << "No pending JsonRpc reply. Did it time out?"; return; } - JsonReply *reply = m_asyncBrowseRequests.take(result.id); + + JsonReply *reply = m_asyncBrowseRequests.take(result.id()); QVariantMap params; - params.insert("deviceError", JsonTypes::deviceErrorToString(result.status)); params.insert("items", JsonTypes::packBrowserItems(result.items)); + params.insert("deviceError", JsonTypes::deviceErrorToString(result.status)); + reply->setData(params); + reply->finished(); +} + +void DeviceHandler::browserItemRequestFinished(const Device::BrowserItemResult &result) +{ + if (m_asyncBrowseDetailsRequests.contains(result.id())) { + qCWarning(dcJsonRpc()) << "No pending JsonRpc reply. Did it time out?"; + return; + } + JsonReply *reply = m_asyncBrowseDetailsRequests.take(result.id()); + QVariantMap params; + if (result.status == Device::DeviceErrorNoError) { + params.insert("item", JsonTypes::packBrowserItem(result.item)); + } + params.insert("deviceError", JsonTypes::deviceErrorToString(result.status)); reply->setData(params); reply->finished(); } diff --git a/libnymea-core/jsonrpc/devicehandler.h b/libnymea-core/jsonrpc/devicehandler.h index 7ae2352a..a2a26fbf 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 *GetBrowserItem(const QVariantMap ¶ms) const; signals: void PluginConfigurationChanged(const QVariantMap ¶ms); @@ -90,6 +91,8 @@ private slots: void browseRequestFinished(const Device::BrowseResult &result); + void browserItemRequestFinished(const Device::BrowserItemResult &result); + private: // A cache for async replies mutable QHash m_discoverRequests; @@ -97,6 +100,7 @@ private: mutable QHash m_asynDeviceEditAdditions; mutable QHash m_asyncPairingRequests; mutable QHash m_asyncBrowseRequests; + mutable QHash m_asyncBrowseDetailsRequests; }; } diff --git a/libnymea-core/jsonrpc/jsontypes.h b/libnymea-core/jsonrpc/jsontypes.h index 7f7e7f20..bc5f9878 100644 --- a/libnymea-core/jsonrpc/jsontypes.h +++ b/libnymea-core/jsonrpc/jsontypes.h @@ -27,6 +27,7 @@ #include "devices/devicemanager.h" #include "ruleengine/rule.h" #include "ruleengine/ruleengine.h" +#include "ruleengine/ruleactionparam.h" #include "nymeaconfiguration.h" #include "usermanager/usermanager.h" @@ -36,7 +37,6 @@ #include "types/actiontype.h" #include "types/paramtype.h" #include "types/paramdescriptor.h" -#include "types/ruleactionparam.h" #include "types/mediabrowseritem.h" #include "logging/logging.h" diff --git a/libnymea-core/ruleengine/rule.h b/libnymea-core/ruleengine/rule.h index c1828383..dacf9559 100644 --- a/libnymea-core/ruleengine/rule.h +++ b/libnymea-core/ruleengine/rule.h @@ -23,10 +23,10 @@ #define RULE_H #include "types/state.h" -#include "types/ruleaction.h" #include "types/eventdescriptor.h" -#include "devices/stateevaluator.h" #include "time/timedescriptor.h" +#include "ruleaction.h" +#include "stateevaluator.h" #include diff --git a/libnymea-core/ruleengine/ruleengine.cpp b/libnymea-core/ruleengine/ruleengine.cpp index 35f0e93d..916b7fc6 100644 --- a/libnymea-core/ruleengine/ruleengine.cpp +++ b/libnymea-core/ruleengine/ruleengine.cpp @@ -1254,6 +1254,69 @@ void RuleEngine::saveRuleActions(NymeaSettings *settings, const QList RuleEngine::loadRuleActions(NymeaSettings *settings) +{ + QList actions; + foreach (const QString &actionNumber, settings->childGroups()) { + settings->beginGroup(actionNumber); + + RuleActionParamList params; + foreach (QString paramTypeIdString, settings->childGroups()) { + if (paramTypeIdString.startsWith("RuleActionParam-")) { + settings->beginGroup(paramTypeIdString); + QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); + EventTypeId eventTypeId = EventTypeId(settings->value("eventTypeId", EventTypeId()).toString()); + ParamTypeId eventParamTypeId = ParamTypeId(settings->value("eventParamTypeId", ParamTypeId()).toString()); + DeviceId stateDeviceId = DeviceId(settings->value("stateDeviceId", DeviceId()).toString()); + StateTypeId stateTypeId = StateTypeId(settings->value("stateTypeId", StateTypeId()).toString()); + QVariant value = settings->value("value"); + if (settings->contains("valueType")) { + QVariant::Type valueType = static_cast(settings->value("valueType").toInt()); + // Note: only warn, and continue with the QVariant guessed type + if (valueType == QVariant::Invalid) { + qCWarning(dcRuleEngine()) << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; + } else if (!value.canConvert(static_cast(valueType))) { + qCWarning(dcRuleEngine()) << "Error loading rule action. Could not convert the rule action param value" << value << "to the stored type" << valueType; + } else { + value.convert(static_cast(valueType)); + } + } + + RuleActionParam param; + if (!ParamTypeId(strippedParamTypeIdString).isNull()) { + // By ParamTypeId + param = RuleActionParam(ParamTypeId(strippedParamTypeIdString), value); + } else { + // By param name + param = RuleActionParam(strippedParamTypeIdString, value); + } + param.setEventTypeId(eventTypeId); + param.setEventParamTypeId(eventParamTypeId); + param.setStateDeviceId(stateDeviceId); + param.setStateTypeId(stateTypeId); + params.append(param); + settings->endGroup(); + } + } + + if (settings->contains("actionTypeId") && settings->contains("deviceId")) { + RuleAction action = RuleAction(ActionTypeId(settings->value("actionTypeId").toString()), DeviceId(settings->value("deviceId").toString())); + action.setRuleActionParams(params); + actions.append(action); + } else if (settings->contains("deviceId") && settings->contains("browserItemId")) { + RuleAction action = RuleAction(DeviceId(settings->value("deviceId").toString()), settings->value("browserItemId").toString()); + actions.append(action); + } else if (settings->contains("interface") && settings->contains("interfaceAction")){ + RuleAction action = RuleAction(settings->value("interface").toString(), settings->value("interfaceAction").toString()); + action.setRuleActionParams(params); + actions.append(action); + } + + settings->endGroup(); + } + return actions; +} + void RuleEngine::init() { NymeaSettings settings(NymeaSettings::SettingsRoleRules); @@ -1413,109 +1476,13 @@ void RuleEngine::init() // Load actions QList actions; settings.beginGroup("ruleActions"); - foreach (const QString &actionNumber, settings.childGroups()) { - settings.beginGroup(actionNumber); - - RuleActionParamList params; - foreach (QString paramTypeIdString, settings.childGroups()) { - if (paramTypeIdString.startsWith("RuleActionParam-")) { - settings.beginGroup(paramTypeIdString); - QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); - EventTypeId eventTypeId = EventTypeId(settings.value("eventTypeId", EventTypeId()).toString()); - ParamTypeId eventParamTypeId = ParamTypeId(settings.value("eventParamTypeId", ParamTypeId()).toString()); - DeviceId stateDeviceId = DeviceId(settings.value("stateDeviceId", DeviceId()).toString()); - StateTypeId stateTypeId = StateTypeId(settings.value("stateTypeId", StateTypeId()).toString()); - QVariant value = settings.value("value"); - if (settings.contains("valueType")) { - QVariant::Type valueType = static_cast(settings.value("valueType").toInt()); - // Note: only warn, and continue with the QVariant guessed type - if (valueType == QVariant::Invalid) { - qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; - } else if (!value.canConvert(static_cast(valueType))) { - qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType; - } else { - value.convert(static_cast(valueType)); - } - } - - RuleActionParam param; - if (!ParamTypeId(strippedParamTypeIdString).isNull()) { - // By ParamTypeId - param = RuleActionParam(ParamTypeId(strippedParamTypeIdString), value); - } else { - // By param name - param = RuleActionParam(strippedParamTypeIdString, value); - } - param.setEventTypeId(eventTypeId); - param.setEventParamTypeId(eventParamTypeId); - param.setStateDeviceId(stateDeviceId); - param.setStateTypeId(stateTypeId); - params.append(param); - settings.endGroup(); - } - } - - if (settings.contains("actionTypeId") && settings.contains("deviceId")) { - RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); - action.setRuleActionParams(params); - actions.append(action); - } else if (settings.contains("interface") && settings.contains("interfaceAction")){ - RuleAction action = RuleAction(settings.value("interface").toString(), settings.value("interfaceAction").toString()); - action.setRuleActionParams(params); - actions.append(action); - } - - settings.endGroup(); - } + actions = loadRuleActions(&settings); settings.endGroup(); // Load exit actions QList exitActions; settings.beginGroup("ruleExitActions"); - foreach (const QString &actionNumber, settings.childGroups()) { - settings.beginGroup(actionNumber); - - RuleActionParamList params; - foreach (QString paramTypeIdString, settings.childGroups()) { - if (paramTypeIdString.startsWith("RuleActionParam-")) { - settings.beginGroup(paramTypeIdString); - QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); - QVariant value = settings.value("value"); - if (settings.contains("valueType")) { - QVariant::Type valueType = static_cast(settings.value("valueType").toInt()); - // Note: only warn, and continue with the QVariant guessed type - if (valueType == QVariant::Invalid) { - qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; - } else if (!value.canConvert(static_cast(valueType))) { - qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType; - } else { - value.convert(static_cast(valueType)); - } - } - - if (!ParamTypeId(strippedParamTypeIdString).isNull()) { - RuleActionParam param(ParamTypeId(strippedParamTypeIdString), value); - params.append(param); - } else { - RuleActionParam param(strippedParamTypeIdString, value); - params.append(param); - } - settings.endGroup(); - } - } - - if (settings.contains("actionTypeId") && settings.contains("deviceId")) { - RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); - action.setRuleActionParams(params); - exitActions.append(action); - } else if (settings.contains("interface") && settings.contains("interfaceAction")) { - RuleAction action = RuleAction(settings.value("interface").toString(),settings.value("interfaceAction").toString()); - action.setRuleActionParams(params); - exitActions.append(action); - } - - settings.endGroup(); - } + exitActions = loadRuleActions(&settings); settings.endGroup(); Rule rule; diff --git a/libnymea-core/ruleengine/ruleengine.h b/libnymea-core/ruleengine/ruleengine.h index e2039fae..cd18e8f4 100644 --- a/libnymea-core/ruleengine/ruleengine.h +++ b/libnymea-core/ruleengine/ruleengine.h @@ -23,9 +23,9 @@ #define RULEENGINE_H #include "rule.h" +#include "stateevaluator.h" #include "types/event.h" #include "types/deviceclass.h" -#include "devices/stateevaluator.h" #include #include @@ -114,6 +114,7 @@ private: void appendRule(const Rule &rule); void saveRule(const Rule &rule); void saveRuleActions(NymeaSettings *settings, const QList &ruleActions); + QList loadRuleActions(NymeaSettings *settings); private: QList m_ruleIds; // Keeping a list of RuleIds to keep sorting order... diff --git a/libnymea/devices/device.h b/libnymea/devices/device.h index f9e5c5e0..89e54fa8 100644 --- a/libnymea/devices/device.h +++ b/libnymea/devices/device.h @@ -85,11 +85,23 @@ public: class BrowseResult { public: - QUuid id; Device::DeviceError status = Device::DeviceErrorNoError; BrowserItems items; + QUuid id() const { return m_id; } private: - BrowseResult(): id(QUuid::createUuid()) {} + QUuid m_id; + BrowseResult(): m_id(QUuid::createUuid()) {} + friend class DeviceManager; + }; + + class BrowserItemResult { + public: + Device::DeviceError status = Device::DeviceErrorNoError; + BrowserItem item; + QUuid id() const { return m_id; } + private: + QUuid m_id; + BrowserItemResult(): m_id(QUuid::createUuid()) {} friend class DeviceManager; }; diff --git a/libnymea/devices/devicemanager.cpp b/libnymea/devices/devicemanager.cpp index e9f0f5c0..9ccb6dc3 100644 --- a/libnymea/devices/devicemanager.cpp +++ b/libnymea/devices/devicemanager.cpp @@ -42,11 +42,10 @@ DeviceManager::DeviceManager(QObject *parent) : QObject(parent) Device::BrowseResult DeviceManager::createBrowseResult() { - Device::BrowseResult result = Device::BrowseResult(); - return result; + return Device::BrowseResult(); } -QUuid DeviceManager::browseResultId(const Device::BrowseResult &result) +Device::BrowserItemResult DeviceManager::createBrowserItemResult() { - return result.id; + return Device::BrowserItemResult(); } diff --git a/libnymea/devices/devicemanager.h b/libnymea/devices/devicemanager.h index 4b9d4464..6892340d 100644 --- a/libnymea/devices/devicemanager.h +++ b/libnymea/devices/devicemanager.h @@ -75,6 +75,7 @@ public: virtual Device::DeviceError executeAction(const Action &action) = 0; virtual Device::BrowseResult browseDevice(const DeviceId &deviceId, const QString &itemId, const QLocale &locale) = 0; + virtual Device::BrowserItemResult browserItemDetails(const DeviceId &deviceId, const QString &itemId, const QLocale &locale) = 0; virtual Device::DeviceError executeBrowserItem(const BrowserAction &browserAction) = 0; virtual Device::DeviceError executeBrowserItemAction(const BrowserItemAction &browserItemAction) = 0; @@ -95,12 +96,15 @@ signals: 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); + void browserItemRequestFinished(const Device::BrowserItemResult &result); void browserItemExecutionFinished(const ActionId &actionId, Device::DeviceError status); void browserItemActionExecutionFinished(const ActionId &actionId, Device::DeviceError status); protected: + // BrowseResult/BrowserItemResult have private Ctors to make sure a plugin dev can not mess up the request ids. + // DeviceManager is a friend, but the implementation is not, expose this to the DeviceManager implementation. Device::BrowseResult createBrowseResult(); - QUuid browseResultId(const Device::BrowseResult &result); + Device::BrowserItemResult createBrowserItemResult(); }; #endif // DEVICEMANAGER_H diff --git a/libnymea/devices/deviceplugin.cpp b/libnymea/devices/deviceplugin.cpp index a4607b6f..5acecb7d 100644 --- a/libnymea/devices/deviceplugin.cpp +++ b/libnymea/devices/deviceplugin.cpp @@ -248,7 +248,7 @@ Device::DeviceError DevicePlugin::executeAction(Device *device, const Action &ac return Device::DeviceErrorNoError; } -/*! Implement this if your devices support browsing (besides settings browsable to true in the metadata). +/*! Implement this if your devices support browsing (set "browsable" to true in the metadata). * When the system calls this method, fill the \a result object's items list with entries from the browser. * If \a itemId is empty it means that the root node of the file system should be returned. Each item in * the result set shall be uniquely identifiable using its \l{BrowserItem::id}{id} property. @@ -270,6 +270,23 @@ Device::BrowseResult DevicePlugin::browseDevice(Device *device, Device::BrowseRe return result; } +/*! Implement this if your devices support browsing (set "browsable" to true in the metadata). + * When the system calls this method, fetch the item details required to create a BrowserItem + * for the item with the given \a id and append that one item to the \a result. + * When done, set the \l{BrowserResult::status}{result's status} field approprietly. Set the result's + * status to Device::DeviceErrorAsync if this operation requires async behavior and emit + * \l{browserItemRequestFinished} when done. + */ +Device::BrowserItemResult DevicePlugin::browserItem(Device *device, Device::BrowserItemResult result, const QString &itemId, const QLocale &locale) +{ + Q_UNUSED(device) + Q_UNUSED(itemId) + Q_UNUSED(locale) + + result.status = Device::DeviceErrorUnsupportedFeature; + return result; +} + /*! Implement this if your devices support browsing and execute the itemId defined in \a browserAction. * Return Device::DeviceErrorAsync if this operation requires async behavior and emit * \l{browserItemExecutionFinished} when done. diff --git a/libnymea/devices/deviceplugin.h b/libnymea/devices/deviceplugin.h index 2b425313..89e74682 100644 --- a/libnymea/devices/deviceplugin.h +++ b/libnymea/devices/deviceplugin.h @@ -80,7 +80,8 @@ public: virtual Device::DeviceError executeAction(Device *device, const Action &action); - virtual Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId, const QLocale &locale); + virtual Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &itemId, const QLocale &locale); + virtual Device::BrowserItemResult browserItem(Device *device, Device::BrowserItemResult result, const QString &itemId, const QLocale &locale); virtual Device::DeviceError executeBrowserItem(Device *device, const BrowserAction &browserAction); virtual Device::DeviceError executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction); @@ -103,6 +104,7 @@ signals: void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList &deviceDescriptors); void autoDeviceDisappeared(const DeviceId &deviceId); void browseRequestFinished(const Device::BrowseResult &result); + void browserItemRequestFinished(const Device::BrowserItemResult &result); void browserItemExecutionFinished(const ActionId &actionid, Device::DeviceError status); void browserItemActionExecutionFinished(const ActionId &actionid, Device::DeviceError status); diff --git a/plugins/mock/devicepluginmock.cpp b/plugins/mock/devicepluginmock.cpp index efe64e7a..28f6b6a0 100644 --- a/plugins/mock/devicepluginmock.cpp +++ b/plugins/mock/devicepluginmock.cpp @@ -272,6 +272,20 @@ Device::BrowseResult DevicePluginMock::browseDevice(Device *device, Device::Brow return result; } +Device::BrowserItemResult DevicePluginMock::browserItem(Device *device, Device::BrowserItemResult result, const QString &itemId, const QLocale &locale) +{ + Q_UNUSED(device) + Q_UNUSED(locale) + VirtualFsNode *node = m_virtualFs->findNode(itemId); + if (!node) { + result.status = Device::DeviceErrorItemNotFound; + return result; + } + result.item = node->item; + result.status = Device::DeviceErrorNoError; + return result; +} + Device::DeviceError DevicePluginMock::executeAction(Device *device, const Action &action) { if (!myDevices().contains(device)) @@ -404,7 +418,7 @@ Device::DeviceError DevicePluginMock::executeAction(Device *device, const Action Device::DeviceError DevicePluginMock::executeBrowserItem(Device *device, const BrowserAction &browserAction) { - qCDebug(dcMockDevice()) << "ExecuteBrowserItem called"; + qCDebug(dcMockDevice()) << "ExecuteBrowserItem called" << browserAction.itemId(); bool broken = device->paramValue(mockDeviceBrokenParamTypeId).toBool(); bool async = device->paramValue(mockDeviceAsyncParamTypeId).toBool(); @@ -657,31 +671,31 @@ void DevicePluginMock::generateBrowseItems() { m_virtualFs = new VirtualFsNode(BrowserItem()); - BrowserItem item = BrowserItem(QUuid::createUuid().toString(), "Item 0", true); + BrowserItem item = BrowserItem("001", "Item 0", true); item.setDescription("I'm a folder"); item.setIcon(BrowserItem::BrowserIconFolder); m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem(QUuid::createUuid().toString(), "Item 1", false, true); + item = BrowserItem("002", "Item 1", false, true); item.setDescription("I'm executable"); item.setIcon(BrowserItem::BrowserIconApplication); item.setActionTypeIds({mockAddToFavoritesBrowserItemActionTypeId}); m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem(QUuid::createUuid().toString(), "Item 2", false, true); + item = BrowserItem("003", "Item 2", false, true); item.setDescription("I'm a file"); item.setIcon(BrowserItem::BrowserIconFile); item.setActionTypeIds({mockAddToFavoritesBrowserItemActionTypeId}); m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem(QUuid::createUuid().toString(), "Item 3", false, true); + item = BrowserItem("004", "Item 3", false, true); item.setDescription("I have a nice thumbnail"); item.setIcon(BrowserItem::BrowserIconFile); item.setThumbnail("https://github.com/guh/nymea/raw/master/icons/nymea-logo-256x256.png"); item.setActionTypeIds({mockAddToFavoritesBrowserItemActionTypeId}); m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem(QUuid::createUuid().toString(), "Item 4", false, false); + item = BrowserItem("005", "Item 4", false, false); item.setDescription("I'm disabled"); item.setDisabled(true); item.setIcon(BrowserItem::BrowserIconFile); diff --git a/plugins/mock/devicepluginmock.h b/plugins/mock/devicepluginmock.h index 76cbd647..d5ec48e2 100644 --- a/plugins/mock/devicepluginmock.h +++ b/plugins/mock/devicepluginmock.h @@ -53,6 +53,7 @@ public: Device::DeviceError displayPin(const PairingTransactionId &pairingTransactionId, const DeviceDescriptor &deviceDescriptor) override; Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &itemId, const QLocale &locale) override; + Device::BrowserItemResult browserItem(Device *device, Device::BrowserItemResult result, const QString &itemId, const QLocale &locale) override; public slots: Device::DeviceError executeAction(Device *device, const Action &action) override; diff --git a/tests/auto/api.json b/tests/auto/api.json index 7f378ef9..a2af70d6 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -318,6 +318,17 @@ ] } }, + "Devices.GetBrowserItem": { + "description": "Get a single item from the browser. This won't give any more info on an item than a regular browseDevice call, but it allows to fetch details of an item if only the ID is known.", + "params": { + "deviceId": "Uuid", + "o:itemId": "String" + }, + "returns": { + "deviceError": "$ref:DeviceError", + "o:item": "$ref:BrowserItem" + } + }, "Devices.GetConfiguredDevices": { "description": "Returns a list of configured devices, optionally filtered by deviceId.", "params": { @@ -1731,6 +1742,7 @@ }, "RuleAction": { "o:actionTypeId": "Uuid", + "o:browserItemId": "String", "o:deviceId": "Uuid", "o:interface": "String", "o:interfaceAction": "String",