diff --git a/libnymea-core/devices/devicemanagerimplementation.cpp b/libnymea-core/devices/devicemanagerimplementation.cpp index b45ae186..f5f8aa9a 100644 --- a/libnymea-core/devices/devicemanagerimplementation.cpp +++ b/libnymea-core/devices/devicemanagerimplementation.cpp @@ -734,7 +734,19 @@ Device::BrowseResult DeviceManagerImplementation::browseDevice(const DeviceId &d return result; } -Device::DeviceError DeviceManagerImplementation::executeBrowserItem(const BrowserItemAction &browserItemAction) +Device::DeviceError DeviceManagerImplementation::executeBrowserItem(const BrowserAction &browserAction) +{ + Device *device = m_configuredDevices.value(browserAction.deviceId()); + if (!device) { + return Device::DeviceErrorDeviceNotFound; + } + if (!device->deviceClass().browsable()) { + return Device::DeviceErrorUnsupportedFeature; + } + return device->plugin()->executeBrowserItem(device, browserAction); +} + +Device::DeviceError DeviceManagerImplementation::executeBrowserItemAction(const BrowserItemAction &browserItemAction) { Device *device = m_configuredDevices.value(browserItemAction.deviceId()); if (!device) { @@ -743,7 +755,9 @@ Device::DeviceError DeviceManagerImplementation::executeBrowserItem(const Browse if (!device->deviceClass().browsable()) { return Device::DeviceErrorUnsupportedFeature; } - return device->plugin()->executeBrowserItem(device, browserItemAction); + // TODO: check browserItemAction.params with deviceClass + + return device->plugin()->executeBrowserItemAction(device, browserItemAction); } QString DeviceManagerImplementation::translate(const PluginId &pluginId, const QString &string, const QLocale &locale) @@ -879,6 +893,7 @@ void DeviceManagerImplementation::loadPlugins() loader.setFileName(fi.absoluteFilePath()); loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); + qCDebug(dcDeviceManager()) << "Loading plugin from:" << fi.absoluteFilePath(); if (!loader.load()) { qCWarning(dcDeviceManager) << "Could not load plugin data of" << entry << "\n" << loader.errorString(); continue; diff --git a/libnymea-core/devices/devicemanagerimplementation.h b/libnymea-core/devices/devicemanagerimplementation.h index 99437dfe..24766d7e 100644 --- a/libnymea-core/devices/devicemanagerimplementation.h +++ b/libnymea-core/devices/devicemanagerimplementation.h @@ -100,7 +100,8 @@ public: Device::DeviceError executeAction(const Action &action) override; Device::BrowseResult browseDevice(const DeviceId &deviceId, const QString &itemId = QString()) override; - Device::DeviceError executeBrowserItem(const BrowserItemAction &browserItemAction) override; + Device::DeviceError executeBrowserItem(const BrowserAction &browserAction) override; + Device::DeviceError executeBrowserItemAction(const BrowserItemAction &browserItemAction) override; QString translate(const PluginId &pluginId, const QString &string, const QLocale &locale) override; diff --git a/libnymea-core/jsonrpc/actionhandler.cpp b/libnymea-core/jsonrpc/actionhandler.cpp index bb096dae..8ff4c73c 100644 --- a/libnymea-core/jsonrpc/actionhandler.cpp +++ b/libnymea-core/jsonrpc/actionhandler.cpp @@ -65,11 +65,21 @@ ActionHandler::ActionHandler(QObject *parent) : params.clear(); returns.clear(); setDescription("ExecuteBrowserItem", "Execute the item identified by itemId on the given device."); params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); - params.insert("o:itemId", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("itemId", JsonTypes::basicTypeToString(JsonTypes::String)); setParams("ExecuteBrowserItem", params); returns.insert("deviceError", JsonTypes::deviceErrorRef()); setReturns("ExecuteBrowserItem", returns); + params.clear(); returns.clear(); + setDescription("ExecuteBrowserItemAction", "Execute the action for the browser item identified by actionTypeId and the itemId on the given device."); + params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("itemId", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("actionTypeId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:params", QVariantList() << JsonTypes::paramRef()); + setParams("ExecuteBrowserItemAction", params); + returns.insert("deviceError", JsonTypes::deviceErrorRef()); + setReturns("ExecuteBrowserItemAction", returns); + connect(NymeaCore::instance(), &NymeaCore::actionExecuted, this, &ActionHandler::actionExecuted); connect(NymeaCore::instance(), &NymeaCore::browserItemExecuted, this, &ActionHandler::browserItemExecuted); } @@ -132,7 +142,7 @@ JsonReply *ActionHandler::ExecuteBrowserItem(const QVariantMap ¶ms) { DeviceId deviceId = DeviceId(params.value("deviceId").toString()); QString itemId = params.value("itemId").toString(); - BrowserItemAction action(deviceId, itemId); + BrowserAction action(deviceId, itemId); Device::DeviceError status = NymeaCore::instance()->executeBrowserItem(action); if (status == Device::DeviceErrorAsync) { JsonReply *reply = createAsyncReply("ExecuteBrowserItem"); @@ -144,6 +154,25 @@ JsonReply *ActionHandler::ExecuteBrowserItem(const QVariantMap ¶ms) return createReply(statusToReply(status)); } +JsonReply *ActionHandler::ExecuteBrowserItemAction(const QVariantMap ¶ms) +{ + DeviceId deviceId = DeviceId(params.value("deviceId").toString()); + QString itemId = params.value("itemId").toString(); + ActionTypeId actionTypeId = ActionTypeId(params.value("actionTypeId").toString()); + ParamList paramList = JsonTypes::unpackParams(params.value("params").toList()); + BrowserItemAction browserItemAction(deviceId, itemId, actionTypeId, paramList); + Device::DeviceError status = NymeaCore::instance()->executeBrowserItemAction(browserItemAction); + if (status == Device::DeviceErrorAsync) { + JsonReply *reply = createAsyncReply("ExecuteBrowserItemAction"); + ActionId id = browserItemAction.id(); + connect(reply, &JsonReply::finished, [this, id](){ m_asyncActionExecutions.remove(id); }); + m_asyncActionExecutions.insert(id, reply); + return reply; + } + return createReply(statusToReply(status)); + +} + void ActionHandler::browserItemExecuted(const ActionId &id, Device::DeviceError status) { if (!m_asyncActionExecutions.contains(id)) { @@ -155,5 +184,16 @@ void ActionHandler::browserItemExecuted(const ActionId &id, Device::DeviceError reply->finished(); } +void ActionHandler::browserItemActionExecuted(const ActionId &id, Device::DeviceError status) +{ + if (!m_asyncActionExecutions.contains(id)) { + return; // Not the action we are waiting for. + } + + JsonReply *reply = m_asyncActionExecutions.take(id); + reply->setData(statusToReply(status)); + reply->finished(); +} + } diff --git a/libnymea-core/jsonrpc/actionhandler.h b/libnymea-core/jsonrpc/actionhandler.h index 346ed920..06cdf23f 100644 --- a/libnymea-core/jsonrpc/actionhandler.h +++ b/libnymea-core/jsonrpc/actionhandler.h @@ -39,10 +39,12 @@ public: Q_INVOKABLE JsonReply *GetActionType(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *ExecuteBrowserItem(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply *ExecuteBrowserItemAction(const QVariantMap ¶ms); private slots: void actionExecuted(const ActionId &id, Device::DeviceError status); void browserItemExecuted(const ActionId &id, Device::DeviceError status); + void browserItemActionExecuted(const ActionId &id, Device::DeviceError status); private: QHash m_asyncActionExecutions; diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index d7666ed9..b682567f 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -286,6 +286,7 @@ void JsonTypes::init() s_deviceClass.insert("stateTypes", QVariantList() << stateTypeRef()); s_deviceClass.insert("eventTypes", QVariantList() << eventTypeRef()); s_deviceClass.insert("actionTypes", QVariantList() << actionTypeRef()); + s_deviceClass.insert("browserItemActionTypes", QVariantList() << actionTypeRef()); s_deviceClass.insert("paramTypes", QVariantList() << paramTypeRef()); s_deviceClass.insert("settingsTypes", QVariantList() << paramTypeRef()); s_deviceClass.insert("discoveryParamTypes", QVariantList() << paramTypeRef()); @@ -436,6 +437,8 @@ void JsonTypes::init() s_browserItem.insert("thumbnail", basicTypeToString(QVariant::String)); s_browserItem.insert("executable", basicTypeToString(QVariant::Bool)); s_browserItem.insert("browsable", basicTypeToString(QVariant::Bool)); + s_browserItem.insert("disabled", basicTypeToString(QVariant::Bool)); + s_browserItem.insert("actionTypeIds", QVariantList() << basicTypeToString(QVariant::Uuid)); s_browserItem.insert("o:mediaIcon", mediaBrowserIconRef()); s_initialized = true; @@ -743,6 +746,12 @@ QVariantMap JsonTypes::packBrowserItem(const BrowserItem &item) ret.insert("thumbnail", item.thumbnail()); ret.insert("executable", item.executable()); ret.insert("browsable", item.browsable()); + ret.insert("disabled", item.disabled()); + QVariantList actionTypeIds; + foreach (const ActionTypeId &id, item.actionTypeIds()) { + actionTypeIds.append(id.toString()); + } + ret.insert("actionTypeIds", actionTypeIds); return ret; } @@ -844,6 +853,10 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo foreach (const ActionType &actionType, deviceClass.actionTypes()) actionTypes.append(packActionType(actionType, deviceClass.pluginId(), locale)); + QVariantList browserItemActionTypes; + foreach (const ActionType &actionType, deviceClass.browserItemActionTypes()) + browserItemActionTypes.append(packActionType(actionType, deviceClass.pluginId(), locale)); + QVariantList paramTypes; foreach (const ParamType ¶mType, deviceClass.paramTypes()) paramTypes.append(packParamType(paramType, deviceClass.pluginId(), locale)); @@ -862,6 +875,7 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo variant.insert("stateTypes", stateTypes); variant.insert("eventTypes", eventTypes); variant.insert("actionTypes", actionTypes); + variant.insert("browserItemActionTypes", browserItemActionTypes); variant.insert("createMethods", packCreateMethods(deviceClass.createMethods())); variant.insert("setupMethod", s_setupMethod.at(deviceClass.setupMethod())); return variant; diff --git a/libnymea-core/logging/logengine.cpp b/libnymea-core/logging/logengine.cpp index c85186a8..4faa9689 100644 --- a/libnymea-core/logging/logengine.cpp +++ b/libnymea-core/logging/logengine.cpp @@ -314,10 +314,19 @@ void LogEngine::logAction(const Action &action, Logging::LoggingLevel level, int appendLogEntry(entry); } -void LogEngine::logBrowserAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level, int errorCode) +void LogEngine::logBrowserAction(const BrowserAction &browserAction, Logging::LoggingLevel level, int errorCode) +{ + LogEntry entry(level, Logging::LoggingSourceBrowserActions, errorCode); + entry.setDeviceId(browserAction.deviceId()); + entry.setValue(browserAction.itemId()); + appendLogEntry(entry); +} + +void LogEngine::logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level, int errorCode) { LogEntry entry(level, Logging::LoggingSourceBrowserActions, errorCode); entry.setDeviceId(browserItemAction.deviceId()); + entry.setTypeId(browserItemAction.actionTypeId()); entry.setValue(browserItemAction.itemId()); appendLogEntry(entry); } diff --git a/libnymea-core/logging/logengine.h b/libnymea-core/logging/logengine.h index d115b404..461bd98b 100644 --- a/libnymea-core/logging/logengine.h +++ b/libnymea-core/logging/logengine.h @@ -27,6 +27,7 @@ #include "types/event.h" #include "types/action.h" #include "types/browseritemaction.h" +#include "types/browseraction.h" #include "ruleengine/rule.h" #include @@ -50,7 +51,8 @@ public: void logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level = Logging::LoggingLevelInfo); void logEvent(const Event &event); void logAction(const Action &action, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); - void logBrowserAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); + void logBrowserAction(const BrowserAction &browserAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); + void logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); void logRuleTriggered(const Rule &rule); void logRuleActiveChanged(const Rule &rule); void logRuleEnabledChanged(const Rule &rule, const bool &enabled); diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index cb8fbfb4..9cfa886f 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -448,15 +448,28 @@ Device::DeviceError NymeaCore::executeAction(const Action &action) return ret; } -Device::DeviceError NymeaCore::executeBrowserItem(const BrowserItemAction &browserItemAction) +Device::DeviceError NymeaCore::executeBrowserItem(const BrowserAction &browserAction) { - Device::DeviceError ret = m_deviceManager->executeBrowserItem(browserItemAction); + Device::DeviceError ret = m_deviceManager->executeBrowserItem(browserAction); if (ret == Device::DeviceErrorNoError) { - m_logger->logBrowserAction(browserItemAction); + m_logger->logBrowserAction(browserAction); + } else if (ret == Device::DeviceErrorAsync) { + m_pendingBrowserActions.insert(browserAction.id(), browserAction); + } else { + m_logger->logBrowserAction(browserAction, Logging::LoggingLevelAlert, ret); + } + return ret; +} + +Device::DeviceError NymeaCore::executeBrowserItemAction(const BrowserItemAction &browserItemAction) +{ + Device::DeviceError ret = m_deviceManager->executeBrowserItemAction(browserItemAction); + if (ret == Device::DeviceErrorNoError) { + m_logger->logBrowserItemAction(browserItemAction); } else if (ret == Device::DeviceErrorAsync) { m_pendingBrowserItemActions.insert(browserItemAction.id(), browserItemAction); } else { - m_logger->logBrowserAction(browserItemAction, Logging::LoggingLevelAlert, ret); + m_logger->logBrowserItemAction(browserItemAction, Logging::LoggingLevelAlert, ret); } return ret; } @@ -814,8 +827,15 @@ void NymeaCore::actionExecutionFinished(const ActionId &id, Device::DeviceError void NymeaCore::browserItemExecutionFinished(const ActionId &id, Device::DeviceError status) { emit browserItemExecuted(id, status); - Action action = m_pendingActions.take(id); - m_logger->logAction(action, status == Device::DeviceErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, status); + BrowserAction action = m_pendingBrowserActions.take(id); + m_logger->logBrowserAction(action, status == Device::DeviceErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, status); +} + +void NymeaCore::browserItemActionExecutionFinished(const ActionId &id, Device::DeviceError status) +{ + emit browserItemActionExecuted(id, status); + BrowserItemAction action = m_pendingBrowserItemActions.take(id); + m_logger->logBrowserItemAction(action, status == Device::DeviceErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, status); } void NymeaCore::onDeviceDisappeared(const DeviceId &deviceId) diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h index e3d1e888..8b10dee2 100644 --- a/libnymea-core/nymeacore.h +++ b/libnymea-core/nymeacore.h @@ -72,7 +72,8 @@ public: Device::DeviceError removeConfiguredDevice(const DeviceId &deviceId, const RuleEngine::RemovePolicy &removePolicy); Device::DeviceError executeAction(const Action &action); - Device::DeviceError executeBrowserItem(const BrowserItemAction &browserItemAction); + Device::DeviceError executeBrowserItem(const BrowserAction &browserAction); + Device::DeviceError executeBrowserItemAction(const BrowserItemAction &browserItemAction); void executeRuleActions(const QList ruleActions); @@ -108,6 +109,7 @@ signals: void deviceSettingChanged(const DeviceId deviceId, const ParamTypeId &settingParamTypeId, const QVariant &value); void actionExecuted(const ActionId &id, Device::DeviceError status); void browserItemExecuted(const ActionId &id, Device::DeviceError status); + void browserItemActionExecuted(const ActionId &id, Device::DeviceError status); void devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors); void deviceSetupFinished(Device *device, Device::DeviceError status); @@ -141,6 +143,7 @@ private: System *m_system; QHash m_pendingActions; + QHash m_pendingBrowserActions; QHash m_pendingBrowserItemActions; QList m_executingRules; @@ -149,6 +152,7 @@ private slots: void onDateTimeChanged(const QDateTime &dateTime); void actionExecutionFinished(const ActionId &id, Device::DeviceError status); void browserItemExecutionFinished(const ActionId &id, Device::DeviceError status); + void browserItemActionExecutionFinished(const ActionId &id, Device::DeviceError status); void onDeviceDisappeared(const DeviceId &deviceId); void deviceManagerLoaded(); diff --git a/libnymea/devices/device.h b/libnymea/devices/device.h index d304fdbe..c34ad219 100644 --- a/libnymea/devices/device.h +++ b/libnymea/devices/device.h @@ -71,6 +71,7 @@ public: DeviceErrorDeviceIsChild, DeviceErrorPairingTransactionIdNotFound, DeviceErrorParameterNotWritable, + DeviceErrorItemNotFound, DeviceErrorUnsupportedFeature, }; Q_ENUM(DeviceError) diff --git a/libnymea/devices/devicemanager.h b/libnymea/devices/devicemanager.h index d1fe3290..b1ac9308 100644 --- a/libnymea/devices/devicemanager.h +++ b/libnymea/devices/devicemanager.h @@ -30,6 +30,7 @@ #include "types/interface.h" #include "types/vendor.h" #include "types/browseritem.h" +#include "types/browseraction.h" #include "types/browseritemaction.h" class DeviceManager : public QObject @@ -74,7 +75,8 @@ public: virtual Device::DeviceError executeAction(const Action &action) = 0; virtual Device::BrowseResult browseDevice(const DeviceId &deviceId, const QString &itemId = QString()) = 0; - virtual Device::DeviceError executeBrowserItem(const BrowserItemAction &browserItemAction) = 0; + virtual Device::DeviceError executeBrowserItem(const BrowserAction &browserAction) = 0; + virtual Device::DeviceError executeBrowserItemAction(const BrowserItemAction &browserItemAction) = 0; virtual QString translate(const PluginId &pluginId, const QString &string, const QLocale &locale) = 0; @@ -94,6 +96,7 @@ signals: void actionExecutionFinished(const ActionId &actionId, Device::DeviceError status); void browseRequestFinished(const Device::BrowseResult &result); void browserItemExecutionFinished(const ActionId &actionId, Device::DeviceError status); + void browserItemActionExecutionFinished(const ActionId &actionId, Device::DeviceError status); protected: Device::BrowseResult createBrowseResult(); diff --git a/libnymea/devices/deviceplugin.cpp b/libnymea/devices/deviceplugin.cpp index 05e30e13..7b45dd1a 100644 --- a/libnymea/devices/deviceplugin.cpp +++ b/libnymea/devices/deviceplugin.cpp @@ -248,16 +248,43 @@ Device::DeviceError DevicePlugin::executeAction(Device *device, const Action &ac return Device::DeviceErrorNoError; } -Device::BrowseResult DevicePlugin::browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId) +/*! Implement this if your devices support browsing. 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. + * The system might call this method again, with an \a itemId returned in a previous query, provided + * that item's \l{BrowserItem::browsable} property is true. In this case all children of the given + * item shall be returned. + * 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{browseRequestFinished} when done. + */ +Device::BrowseResult DevicePlugin::browseDevice(Device *device, Device::BrowseResult result, const QString &itemId) { Q_UNUSED(device) - Q_UNUSED(nodeId) + Q_UNUSED(itemId) result.status = Device::DeviceErrorUnsupportedFeature; return result; } -Device::DeviceError DevicePlugin::executeBrowserItem(Device *device, const BrowserItemAction &browserItemAction) +/*! 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. + */ +Device::DeviceError DevicePlugin::executeBrowserItem(Device *device, const BrowserAction &browserAction) +{ + Q_UNUSED(device) + Q_UNUSED(browserAction) + return Device::DeviceErrorUnsupportedFeature; +} + +/*! Implement this if your devices support browsing and execute the item's action for the itemId defined + * in \a browserItemAction. + * Return Device::DeviceErrorAsync if this operation requires async behavior and emit + * \l{browserItemActionExecutionFinished} when done. + */ +Device::DeviceError DevicePlugin::executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction) { Q_UNUSED(device) Q_UNUSED(browserItemAction) diff --git a/libnymea/devices/deviceplugin.h b/libnymea/devices/deviceplugin.h index 3d28293d..818d249c 100644 --- a/libnymea/devices/deviceplugin.h +++ b/libnymea/devices/deviceplugin.h @@ -37,6 +37,7 @@ #include "types/vendor.h" #include "types/param.h" #include "types/interface.h" +#include "types/browseraction.h" #include "types/browseritemaction.h" #include "hardwaremanager.h" @@ -80,7 +81,8 @@ 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 BrowserItemAction &browserItemAction); + virtual Device::DeviceError executeBrowserItem(Device *device, const BrowserAction &browserAction); + virtual Device::DeviceError executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction); // Configuration ParamTypes configurationDescription() const; @@ -102,6 +104,7 @@ signals: void autoDeviceDisappeared(const DeviceId &deviceId); void browseRequestFinished(const Device::BrowseResult &result); void browserItemExecutionFinished(const ActionId &actionid, Device::DeviceError status); + void browserItemActionExecutionFinished(const ActionId &actionid, Device::DeviceError status); protected: Devices myDevices() const; diff --git a/libnymea/devices/pluginmetadata.cpp b/libnymea/devices/pluginmetadata.cpp index 5d11cfd2..4b673636 100644 --- a/libnymea/devices/pluginmetadata.cpp +++ b/libnymea/devices/pluginmetadata.cpp @@ -163,7 +163,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) /*! Returns a list of all valid JSON properties a DeviceClass JSON definition can have. */ QStringList deviceClassProperties = QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod" << "interfaces" << "browsable" << "pairingInfo" << "discoveryParamTypes" << "discoveryParamTypes" - << "paramTypes" << "settingsTypes" << "stateTypes" << "actionTypes" << "eventTypes"; + << "paramTypes" << "settingsTypes" << "stateTypes" << "actionTypes" << "eventTypes" << "browserItemActionTypes"; QStringList mandatoryDeviceClassProperties = QStringList() << "id" << "name" << "displayName"; QPair verificationResult = verifyFields(deviceClassProperties, mandatoryDeviceClassProperties, deviceClassObject); @@ -261,9 +261,10 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) // Read pairing info deviceClass.setPairingInfo(deviceClassObject.value("pairingInfo").toString()); - QList actionTypes; - QList stateTypes; - QList eventTypes; + ActionTypes actionTypes; + StateTypes stateTypes; + EventTypes eventTypes; + ActionTypes browserItemActionTypes; // Read StateTypes int index = 0; @@ -458,6 +459,48 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) } deviceClass.setEventTypes(eventTypes); + // BrowserItemActionTypes + index = 0; + foreach (const QJsonValue &browserItemActionTypesJson, deviceClassObject.value("browserItemActionTypes").toArray()) { + QJsonObject at = browserItemActionTypesJson.toObject(); + QPair verificationResult = verifyFields(ActionType::typeProperties(), ActionType::mandatoryTypeProperties(), at); + + // Check mandatory fields + if (!verificationResult.first.isEmpty()) { + qCWarning(dcPluginMetadata()) << "Device class" << deviceClass.name() << " has missing fields" << verificationResult.first.join(", ") << "in browser item action type:" << endl << at; + hasError = true; + continue; + } + + // Check if there are any unknown fields + if (!verificationResult.second.isEmpty()) { + qCWarning(dcPluginMetadata()) << pluginName() << "Device class" << deviceClass.name() << "has unknown fields:" << verificationResult.second.join(", ") << "in browser item action type:" << endl << at; + hasError = true; + } + + ActionTypeId actionTypeId = ActionTypeId(at.value("id").toString()); + QString actionTypeName = at.value("name").toString(); + if (!verifyDuplicateUuid(actionTypeId)) { + qCWarning(dcPluginMetadata()) << "Browser Action Type" << actionTypeName << "has duplicate UUID:" << actionTypeId.toString(); + hasError = true; + } + ActionType actionType(actionTypeId); + actionType.setName(actionTypeName); + actionType.setDisplayName(at.value("displayName").toString()); + actionType.setIndex(index++); + + QPair > paramVerification = parseParamTypes(at.value("paramTypes").toArray()); + if (!paramVerification.first) { + hasError = true; + break; + } else { + actionType.setParamTypes(paramVerification.second); + } + + browserItemActionTypes.append(actionType); + } + deviceClass.setBrowserItemActionTypes(browserItemActionTypes); + // Read interfaces QStringList interfaces; foreach (const QJsonValue &value, deviceClassObject.value("interfaces").toArray()) { @@ -617,6 +660,7 @@ QPair PluginMetadata::verifyFields(const QStringList & QPair PluginMetadata::parseParamTypes(const QJsonArray &array) { + bool hasErrors = false; int index = 0; QList paramTypes; foreach (const QJsonValue ¶mTypesJson, array) { @@ -627,13 +671,14 @@ QPair PluginMetadata::parseParamTypes(const QJsonArray &array) // Check mandatory fields if (!verificationResult.first.isEmpty()) { qCWarning(dcPluginMetadata()) << pluginName() << "Error parsing ParamType: missing fields:" << verificationResult.first.join(", ") << endl << pt; - return QPair >(false, QList()); + hasErrors = true; + continue; } // Check if there are any unknown fields if (!verificationResult.second.isEmpty()) { qCWarning(dcPluginMetadata()) << pluginName() << "Error parsing ParamType: unknown fields:" << verificationResult.second.join(", ") << endl << pt; - return QPair >(false, QList()); + hasErrors = true; } // Check type @@ -642,14 +687,14 @@ QPair PluginMetadata::parseParamTypes(const QJsonArray &array) qCWarning(dcPluginMetadata()) << pluginName() << QString("Invalid type %1 for param %2 in json file.") .arg(pt.value("type").toString()) .arg(pt.value("name").toString()).toLatin1().data(); - return QPair >(false, QList()); + hasErrors = true; } ParamTypeId paramTypeId = ParamTypeId(pt.value("id").toString()); QString paramName = pt.value("name").toString(); if (!verifyDuplicateUuid(paramTypeId)) { qCWarning(dcPluginMetadata()) << "Param" << paramName << "has duplicate UUID:" << paramTypeId.toString(); - return QPair >(false, QList()); + hasErrors = true; } ParamType paramType(paramTypeId, paramName, t, pt.value("defaultValue").toVariant()); paramType.setDisplayName(pt.value("displayName").toString()); @@ -666,7 +711,7 @@ QPair PluginMetadata::parseParamTypes(const QJsonArray &array) QPair inputTypeVerification = loadAndVerifyInputType(pt.value("inputType").toString()); if (!inputTypeVerification.first) { qCWarning(dcPluginMetadata()) << pluginName() << QString("Invalid inputType for paramType") << pt; - return QPair >(false, QList()); + hasErrors = true; } else { paramType.setInputType(inputTypeVerification.second); } @@ -677,7 +722,7 @@ QPair PluginMetadata::parseParamTypes(const QJsonArray &array) QPair unitVerification = loadAndVerifyUnit(pt.value("unit").toString()); if (!unitVerification.first) { qCWarning(dcPluginMetadata()) << pluginName() << QString("Invalid unit type for paramType") << pt; - return QPair >(false, QList()); + hasErrors = true; } else { paramType.setUnit(unitVerification.second); } @@ -693,7 +738,7 @@ QPair PluginMetadata::parseParamTypes(const QJsonArray &array) paramTypes.append(paramType); } - return QPair >(true, paramTypes); + return QPair >(!hasErrors, paramTypes); } QPair PluginMetadata::loadAndVerifyInputType(const QString &inputType) diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index 21f46c81..bee2ccdc 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -16,7 +16,8 @@ HEADERS += \ platform/package.h \ platform/repository.h \ types/browseritem.h \ - types/browseritemaction.h \ + types/browseritemaction.h \ + types/browseraction.h \ types/mediabrowseritem.h \ typeutils.h \ loggingcategories.h \ @@ -114,7 +115,8 @@ SOURCES += \ coap/corelink.cpp \ coap/coapobserveresource.cpp \ types/browseritem.cpp \ - types/browseritemaction.cpp \ + types/browseritemaction.cpp \ + types/browseraction.cpp \ types/mediabrowseritem.cpp \ types/deviceclass.cpp \ types/action.cpp \ diff --git a/libnymea/types/actiontype.cpp b/libnymea/types/actiontype.cpp index 9f7dc1c3..959183bd 100644 --- a/libnymea/types/actiontype.cpp +++ b/libnymea/types/actiontype.cpp @@ -142,3 +142,9 @@ ActionType ActionTypes::findById(const ActionTypeId &id) } return ActionType(ActionTypeId()); } + +QDebug operator<<(QDebug dbg, const ActionType &actionType) +{ + dbg.nospace().noquote() << "ActionType: " << actionType.name() << actionType.displayName() << actionType.id(); + return dbg; +} diff --git a/libnymea/types/actiontype.h b/libnymea/types/actiontype.h index 09853a36..d809ab4e 100644 --- a/libnymea/types/actiontype.h +++ b/libnymea/types/actiontype.h @@ -60,6 +60,8 @@ private: ParamTypes m_paramTypes; }; +QDebug operator<<(QDebug dbg, const ActionType &actionType); + class ActionTypes: public QList { public: diff --git a/libnymea/types/browseraction.cpp b/libnymea/types/browseraction.cpp new file mode 100644 index 00000000..9d0a2841 --- /dev/null +++ b/libnymea/types/browseraction.cpp @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "browseraction.h" + +BrowserAction::BrowserAction(const DeviceId &deviceId, const QString &itemId): + m_id(ActionId::createActionId()), + m_deviceId(deviceId), + m_itemId(itemId) +{ + +} + +BrowserAction::BrowserAction(const BrowserAction &other): + m_id(other.id()), + m_deviceId(other.deviceId()), + m_itemId(other.itemId()) +{ + +} + +ActionId BrowserAction::id() const +{ + return m_id; +} + +bool BrowserAction::isValid() const +{ + return !m_id.isNull() && !m_deviceId.isNull() && !m_itemId.isNull(); +} + +DeviceId BrowserAction::deviceId() const +{ + return m_deviceId; +} + +QString BrowserAction::itemId() const +{ + return m_itemId; +} + +void BrowserAction::operator=(const BrowserAction &other) +{ + m_id = other.id(); + m_deviceId = other.deviceId(); + m_itemId = other.itemId(); +} diff --git a/libnymea/types/browseraction.h b/libnymea/types/browseraction.h new file mode 100644 index 00000000..781f4372 --- /dev/null +++ b/libnymea/types/browseraction.h @@ -0,0 +1,48 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BROWSERACTION_H +#define BROWSERACTION_H + +#include "typeutils.h" + +class BrowserAction +{ +public: + explicit BrowserAction(const DeviceId &deviceId = DeviceId(), const QString &itemId = QString()); + BrowserAction(const BrowserAction &other); + + ActionId id() const; + + bool isValid() const; + + DeviceId deviceId() const; + QString itemId() const; + + void operator=(const BrowserAction &other); +private: + ActionId m_id; + DeviceId m_deviceId; + QString m_itemId; +}; + +#endif // BROWSERACTION_H diff --git a/libnymea/types/browseritem.cpp b/libnymea/types/browseritem.cpp index da914a22..3aa5354f 100644 --- a/libnymea/types/browseritem.cpp +++ b/libnymea/types/browseritem.cpp @@ -77,6 +77,16 @@ void BrowserItem::setBrowsable(bool browsable) m_browsable = browsable; } +bool BrowserItem::disabled() const +{ + return m_disabled; +} + +void BrowserItem::setDisabled(bool disabled) +{ + m_disabled = disabled; +} + BrowserItem::BrowserIcon BrowserItem::icon() const { return m_icon; diff --git a/libnymea/types/browseritem.h b/libnymea/types/browseritem.h index 260a64ad..a4daba95 100644 --- a/libnymea/types/browseritem.h +++ b/libnymea/types/browseritem.h @@ -73,6 +73,9 @@ public: bool browsable() const; void setBrowsable(bool browsable); + bool disabled() const; + void setDisabled(bool disabled); + BrowserIcon icon() const; void setIcon(BrowserIcon icon); @@ -91,6 +94,7 @@ private: QString m_description; bool m_browsable = false; bool m_executable = false; + bool m_disabled = false; BrowserIcon m_icon = BrowserIconNone; QString m_thumbnail; diff --git a/libnymea/types/browseritemaction.cpp b/libnymea/types/browseritemaction.cpp index e9236482..94868283 100644 --- a/libnymea/types/browseritemaction.cpp +++ b/libnymea/types/browseritemaction.cpp @@ -22,10 +22,13 @@ #include "browseritemaction.h" -BrowserItemAction::BrowserItemAction(const DeviceId &deviceId, const QString &itemId): + +BrowserItemAction::BrowserItemAction(const DeviceId &deviceId, const QString &itemId, const ActionTypeId &actionTypeId, const ParamList ¶ms): m_id(ActionId::createActionId()), m_deviceId(deviceId), - m_itemId(itemId) + m_itemId(itemId), + m_actionTypeId(actionTypeId), + m_params(params) { } @@ -33,7 +36,9 @@ BrowserItemAction::BrowserItemAction(const DeviceId &deviceId, const QString &it BrowserItemAction::BrowserItemAction(const BrowserItemAction &other): m_id(other.id()), m_deviceId(other.deviceId()), - m_itemId(other.itemId()) + m_itemId(other.itemId()), + m_actionTypeId(other.actionTypeId()), + m_params(other.params()) { } @@ -58,9 +63,21 @@ QString BrowserItemAction::itemId() const return m_itemId; } +ActionTypeId BrowserItemAction::actionTypeId() const +{ + return m_actionTypeId; +} + +ParamList BrowserItemAction::params() const +{ + return m_params; +} + void BrowserItemAction::operator=(const BrowserItemAction &other) { m_id = other.id(); m_deviceId = other.deviceId(); m_itemId = other.itemId(); + m_actionTypeId = other.actionTypeId(); + m_params = other.params(); } diff --git a/libnymea/types/browseritemaction.h b/libnymea/types/browseritemaction.h index e5ee311d..aa58cfe3 100644 --- a/libnymea/types/browseritemaction.h +++ b/libnymea/types/browseritemaction.h @@ -24,13 +24,12 @@ #define BROWSERITEMACTION_H #include "typeutils.h" +#include "types/param.h" class BrowserItemAction { public: - BrowserItemAction(); - - explicit BrowserItemAction(const DeviceId &deviceId = DeviceId(), const QString &itemId = QString()); + explicit BrowserItemAction(const DeviceId &deviceId = DeviceId(), const QString &itemId = QString(), const ActionTypeId &actionTypeId = ActionTypeId(), const ParamList ¶ms = ParamList()); BrowserItemAction(const BrowserItemAction &other); ActionId id() const; @@ -39,12 +38,19 @@ public: DeviceId deviceId() const; QString itemId() const; + ActionTypeId actionTypeId() const; + + ParamList params() const; + void setParams(const ParamList ¶ms); + Param param(const ParamTypeId ¶mTypeId) const; void operator=(const BrowserItemAction &other); private: ActionId m_id; DeviceId m_deviceId; QString m_itemId; + ActionTypeId m_actionTypeId; + ParamList m_params; }; #endif // BROWSERITEMACTION_H diff --git a/libnymea/types/deviceclass.cpp b/libnymea/types/deviceclass.cpp index 6ddb7ab0..7a62f26e 100644 --- a/libnymea/types/deviceclass.cpp +++ b/libnymea/types/deviceclass.cpp @@ -213,24 +213,24 @@ bool DeviceClass::hasActionType(const ActionTypeId &actionTypeId) return false; } -/*! Returns the browserActionTypes of this DeviceClass. \{Device}{Devices} created +/*! Returns the browserItemActionTypes of this DeviceClass. \{Device}{Devices} created from this \l{DeviceClass} may set those actions to their browser items. */ -ActionTypes DeviceClass::browserActionTypes() const +ActionTypes DeviceClass::browserItemActionTypes() const { - return m_browserActionTypes; + return m_browserItemActionTypes; } /*! Set the \a browserActionTypes of this DeviceClass. \{Device}{Devices} created from this \l{DeviceClass} may set those actions to their browser items. */ -void DeviceClass::setBrowserActionTypes(const ActionTypes &browserActionTypes) +void DeviceClass::setBrowserItemActionTypes(const ActionTypes &browserItemActionTypes) { - m_browserActionTypes = browserActionTypes; + m_browserItemActionTypes = browserItemActionTypes; } /*! Returns true if this DeviceClass has a \l{ActionType} with the given \a actionTypeId. */ -bool DeviceClass::hasBrowserActionType(const ActionTypeId &actionTypeId) +bool DeviceClass::hasBrowserItemActionType(const ActionTypeId &actionTypeId) { - foreach (const ActionType &actionType, m_actionTypes) { + foreach (const ActionType &actionType, m_browserItemActionTypes) { if (actionType.id() == actionTypeId) { return true; } @@ -333,11 +333,13 @@ void DeviceClass::setInterfaces(const QStringList &interfaces) m_interfaces = interfaces; } +/*! Returns whether \l{Device}{Devices} created from this \l{DeviceClass} are browsable */ bool DeviceClass::browsable() const { return m_browsable; } +/*! Sets whether \l{Device}{Devices} created from this \l{DeviceClass} are browsable */ void DeviceClass::setBrowsable(bool browsable) { m_browsable = browsable; diff --git a/libnymea/types/deviceclass.h b/libnymea/types/deviceclass.h index d40b5324..d01846d6 100644 --- a/libnymea/types/deviceclass.h +++ b/libnymea/types/deviceclass.h @@ -82,9 +82,12 @@ public: void setActionTypes(const ActionTypes &actionTypes); bool hasActionType(const ActionTypeId &actionTypeId); - ActionTypes browserActionTypes() const; - void setBrowserActionTypes(const ActionTypes &browserActionTypes); - bool hasBrowserActionType(const ActionTypeId &actionTypeId); + bool browsable() const; + void setBrowsable(bool browsable); + + ActionTypes browserItemActionTypes() const; + void setBrowserItemActionTypes(const ActionTypes &browserItemActionTypes); + bool hasBrowserItemActionType(const ActionTypeId &actionTypeId); ParamTypes paramTypes() const; void setParamTypes(const ParamTypes ¶mTypes); @@ -107,9 +110,6 @@ public: QStringList interfaces() const; void setInterfaces(const QStringList &interfaces); - bool browsable() const; - void setBrowsable(bool browsable); - bool operator==(const DeviceClass &device) const; private: @@ -122,7 +122,7 @@ private: StateTypes m_stateTypes; EventTypes m_eventTypes; ActionTypes m_actionTypes; - ActionTypes m_browserActionTypes; + ActionTypes m_browserItemActionTypes; ParamTypes m_paramTypes; ParamTypes m_settingsTypes; ParamTypes m_discoveryParamTypes; diff --git a/plugins/mock/devicepluginmock.cpp b/plugins/mock/devicepluginmock.cpp index 531031dd..4b80cb91 100644 --- a/plugins/mock/devicepluginmock.cpp +++ b/plugins/mock/devicepluginmock.cpp @@ -51,7 +51,7 @@ DevicePluginMock::DevicePluginMock() { - + generateBrowseItems(); } DevicePluginMock::~DevicePluginMock() @@ -239,17 +239,21 @@ Device::DeviceError DevicePluginMock::displayPin(const PairingTransactionId &pai return Device::DeviceErrorNoError; } -Device::BrowseResult DevicePluginMock::browseDevice(Device *device, Device::BrowseResult result, const QString &nodeId) +Device::BrowseResult DevicePluginMock::browseDevice(Device *device, Device::BrowseResult result, const QString &itemId) { 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 { + QTimer::singleShot(1000, device, [this, device, result, itemId]() mutable { if (device->paramValue(mockDeviceBrokenParamTypeId).toBool()) { result.status = Device::DeviceErrorHardwareFailure; } else { - result = generateBrowseItems(nodeId, result); + VirtualFsNode *node = m_virtualFs->findNode(itemId); + foreach (VirtualFsNode *child, node->childs) { + result.items.append(child->item); + } + result.status = Device::DeviceErrorNoError; } emit browseRequestFinished(result); }); @@ -257,7 +261,11 @@ Device::BrowseResult DevicePluginMock::browseDevice(Device *device, Device::Brow else if (device->paramValue(mockDeviceBrokenParamTypeId).toBool()) { result.status = Device::DeviceErrorHardwareFailure; } else { - result = generateBrowseItems(nodeId, result); + VirtualFsNode *node = m_virtualFs->findNode(itemId); + foreach (VirtualFsNode *child, node->childs) { + result.items.append(child->item); + } + result.status = Device::DeviceErrorNoError; } } return result; @@ -393,8 +401,9 @@ Device::DeviceError DevicePluginMock::executeAction(Device *device, const Action return Device::DeviceErrorDeviceClassNotFound; } -Device::DeviceError DevicePluginMock::executeBrowserItem(Device *device, const BrowserItemAction &browserItemAction) +Device::DeviceError DevicePluginMock::executeBrowserItem(Device *device, const BrowserAction &browserAction) { + qCDebug(dcMockDevice()) << "ExecuteBrowserItem called"; bool broken = device->paramValue(mockDeviceBrokenParamTypeId).toBool(); bool async = device->paramValue(mockDeviceAsyncParamTypeId).toBool(); @@ -405,12 +414,46 @@ Device::DeviceError DevicePluginMock::executeBrowserItem(Device *device, const B return Device::DeviceErrorNoError; } - QTimer::singleShot(2000, device, [this, broken, browserItemAction](){ - emit this->browserItemExecutionFinished(browserItemAction.id(), broken ? Device::DeviceErrorHardwareFailure : Device::DeviceErrorNoError); + QTimer::singleShot(2000, device, [this, broken, browserAction](){ + emit this->browserItemExecutionFinished(browserAction.id(), broken ? Device::DeviceErrorHardwareFailure : Device::DeviceErrorNoError); }); return Device::DeviceErrorAsync; } +Device::DeviceError DevicePluginMock::executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction) +{ + qCDebug(dcMockDevice()) << "TODO" << device << browserItemAction.id(); + if (browserItemAction.actionTypeId() == mockAddToFavoritesBrowserItemActionTypeId) { + + VirtualFsNode *node = m_virtualFs->findNode(browserItemAction.itemId()); + if (!node) { + return Device::DeviceErrorInvalidParameter; + } + VirtualFsNode *favoritesNode = m_virtualFs->findNode("favorites"); + if (favoritesNode->findNode(browserItemAction.itemId())) { + return Device::DeviceErrorDeviceInUse; + } + BrowserItem newItem = node->item; + newItem.setActionTypeIs({mockRemoveFromFavoritesBrowserItemActionTypeId}); + VirtualFsNode *newNode = new VirtualFsNode(newItem); + favoritesNode->addChild(newNode); + return Device::DeviceErrorNoError; + } + + if (browserItemAction.actionTypeId() == mockRemoveFromFavoritesBrowserItemActionTypeId) { + VirtualFsNode *favoritesNode = m_virtualFs->findNode("favorites"); + VirtualFsNode *nodeToRemove = favoritesNode->findNode(browserItemAction.itemId()); + if (!nodeToRemove) { + return Device::DeviceErrorItemNotFound; + } + int idx = favoritesNode->childs.indexOf(nodeToRemove); + delete favoritesNode->childs.takeAt(idx); + return Device::DeviceErrorNoError; + } + + return Device::DeviceErrorActionTypeNotFound; +} + void DevicePluginMock::setState(const StateTypeId &stateTypeId, const QVariant &value) { HttpDaemon *daemon = qobject_cast(sender()); @@ -609,58 +652,42 @@ void DevicePluginMock::onPluginConfigChanged() } -Device::BrowseResult DevicePluginMock::generateBrowseItems(const QString &itemId, Device::BrowseResult result) +void DevicePluginMock::generateBrowseItems() { - result.status = Device::DeviceErrorNoError; + m_virtualFs = new VirtualFsNode(BrowserItem()); - if (itemId.isEmpty()) { - BrowserItem item = BrowserItem("0", "Item 0", true); - item.setDescription("I'm a folder"); - item.setIcon(BrowserItem::BrowserIconFolder); - result.items.append(item); + BrowserItem item = BrowserItem(QUuid::createUuid().toString(), "Item 0", true); + item.setDescription("I'm a folder"); + item.setIcon(BrowserItem::BrowserIconFolder); + m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem("1", "Item 1", false, true); - item.setDescription("I'm executable"); - item.setIcon(BrowserItem::BrowserIconApplication); - result.items.append(item); + item = BrowserItem(QUuid::createUuid().toString(), "Item 1", false, true); + item.setDescription("I'm executable"); + item.setIcon(BrowserItem::BrowserIconApplication); + item.setActionTypeIs({mockAddToFavoritesBrowserItemActionTypeId}); + m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem("2", "Item 2", false, true); - item.setDescription("I'm a file"); - item.setIcon(BrowserItem::BrowserIconFile); - result.items.append(item); + item = BrowserItem(QUuid::createUuid().toString(), "Item 2", false, true); + item.setDescription("I'm a file"); + item.setIcon(BrowserItem::BrowserIconFile); + item.setActionTypeIs({mockAddToFavoritesBrowserItemActionTypeId}); + m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem("3", "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"); - result.items.append(item); + item = BrowserItem(QUuid::createUuid().toString(), "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.setActionTypeIs({mockAddToFavoritesBrowserItemActionTypeId}); + m_virtualFs->addChild(new VirtualFsNode(item)); - item = BrowserItem("4", "Item 4", false, false); - item.setDescription("I'm disabled"); - item.setIcon(BrowserItem::BrowserIconFile); - result.items.append(item); + item = BrowserItem(QUuid::createUuid().toString(), "Item 4", false, false); + item.setDescription("I'm disabled"); + item.setDisabled(true); + item.setIcon(BrowserItem::BrowserIconFile); + m_virtualFs->addChild(new VirtualFsNode(item)); - } - else if (itemId == "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 (itemId == "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 (itemId == "10") { - result.items.append(BrowserItem("15", "Item 15")); - result.items.append(BrowserItem("16", "Item 16")); - } else { - result.status = Device::DeviceErrorInvalidParameter; - } - - return result; + item = BrowserItem("favorites", "Favorites", true, false); + item.setDescription("Yay! I'm the best!"); + item.setIcon(BrowserItem::BrowserIconFavorites); + m_virtualFs->addChild(new VirtualFsNode(item)); } diff --git a/plugins/mock/devicepluginmock.h b/plugins/mock/devicepluginmock.h index 521a3e17..fef7c73c 100644 --- a/plugins/mock/devicepluginmock.h +++ b/plugins/mock/devicepluginmock.h @@ -52,11 +52,12 @@ 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; + Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &itemId = QString()) override; public slots: Device::DeviceError executeAction(Device *device, const Action &action) override; - Device::DeviceError executeBrowserItem(Device *device, const BrowserItemAction &browserItemAction) override; + Device::DeviceError executeBrowserItem(Device *device, const BrowserAction &browserAction) override; + Device::DeviceError executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction) override; private slots: void setState(const StateTypeId &stateTypeId, const QVariant &value); @@ -76,9 +77,25 @@ private slots: void onPluginConfigChanged(); private: - Device::BrowseResult generateBrowseItems(const QString &itemId, Device::BrowseResult result); + void generateBrowseItems(); private: + class VirtualFsNode { + public: + VirtualFsNode(const BrowserItem &item):item(item) {} + BrowserItem item; + QList childs; + void addChild(VirtualFsNode* child) {childs.append(child); } + VirtualFsNode *findNode(const QString &id) { + if (item.id() == id) return this; + foreach (VirtualFsNode *child, childs) { + VirtualFsNode *node = child->findNode(id); + if (node) return node; + } + return nullptr; + } + }; + QHash m_daemons; QList m_asyncSetupDevices; QList > m_asyncActions; @@ -87,6 +104,8 @@ private: int m_discoveredDeviceCount; bool m_pushbuttonPressed; + + VirtualFsNode* m_virtualFs = nullptr; }; #endif // DEVICEPLUGINMOCK_H diff --git a/plugins/mock/devicepluginmock.json b/plugins/mock/devicepluginmock.json index 205b654e..6455525d 100644 --- a/plugins/mock/devicepluginmock.json +++ b/plugins/mock/devicepluginmock.json @@ -195,11 +195,16 @@ "displayName": "Mock Action 5 (async, broken)" } ], - "brwserItemActionTypes": [ + "browserItemActionTypes": [ { "id": "00b8f0a8-99ca-4aa4-833d-59eb8d4d6de3", - "name": "addToFolder", - "displayName": "Add to Favorites" + "name": "addToFavorites", + "displayName": "Add to favorites" + }, + { + "id": "da6faef8-2816-430e-93bb-57e8f9582d29", + "name": "removeFromFavorites", + "displayName": "Remove from favorites" } ] }, diff --git a/plugins/mock/extern-plugininfo.h b/plugins/mock/extern-plugininfo.h index cdce91d9..cb3d8438 100644 --- a/plugins/mock/extern-plugininfo.h +++ b/plugins/mock/extern-plugininfo.h @@ -50,6 +50,8 @@ extern ActionTypeId mockWithoutParamsActionTypeId; extern ActionTypeId mockAsyncActionTypeId; extern ActionTypeId mockFailingActionTypeId; extern ActionTypeId mockAsyncFailingActionTypeId; +extern ActionTypeId mockAddToFavoritesBrowserItemActionTypeId; +extern ActionTypeId mockRemoveFromFavoritesBrowserItemActionTypeId; extern DeviceClassId mockDeviceAutoDeviceClassId; extern ParamTypeId mockDeviceAutoDeviceHttpportParamTypeId; extern ParamTypeId mockDeviceAutoDeviceAsyncParamTypeId; diff --git a/plugins/mock/plugininfo.h b/plugins/mock/plugininfo.h index 4c8ad1d7..39e0e5c0 100644 --- a/plugins/mock/plugininfo.h +++ b/plugins/mock/plugininfo.h @@ -54,6 +54,8 @@ ActionTypeId mockWithoutParamsActionTypeId = ActionTypeId("{defd3ed6-1a0d-400b-8 ActionTypeId mockAsyncActionTypeId = ActionTypeId("{fbae06d3-7666-483e-a39e-ec50fe89054e}"); ActionTypeId mockFailingActionTypeId = ActionTypeId("{df3cf33d-26d5-4577-9132-9823bd33fad0}"); ActionTypeId mockAsyncFailingActionTypeId = ActionTypeId("{bfe89a1d-3497-4121-8318-e77c37537219}"); +ActionTypeId mockAddToFavoritesBrowserItemActionTypeId = ActionTypeId("{00b8f0a8-99ca-4aa4-833d-59eb8d4d6de3}"); +ActionTypeId mockRemoveFromFavoritesBrowserItemActionTypeId = ActionTypeId("{da6faef8-2816-430e-93bb-57e8f9582d29}"); DeviceClassId mockDeviceAutoDeviceClassId = DeviceClassId("{ab4257b3-7548-47ee-9bd4-7dc3004fd197}"); ParamTypeId mockDeviceAutoDeviceHttpportParamTypeId = ParamTypeId("{bfeb0613-dab6-408c-aa27-c362c921d0d1}"); ParamTypeId mockDeviceAutoDeviceAsyncParamTypeId = ParamTypeId("{a5c4315f-0624-4971-87c1-4bbfbfdbd16e}"); @@ -247,6 +249,9 @@ ActionTypeId mockInputTypeWritableTimestampUIntActionTypeId = ActionTypeId("{45d ParamTypeId mockInputTypeWritableTimestampUIntActionWritableTimestampUIntParamTypeId = ParamTypeId("{45d0069a-63ac-4265-8170-8152778608ee}"); const QString translations[] { + //: The name of the Browser Item ActionType ({00b8f0a8-99ca-4aa4-833d-59eb8d4d6de3}) of DeviceClass mock + QT_TRANSLATE_NOOP("mockDevice", "Add to favorites"), + //: The name of the ParamType (DeviceClass: mockInputType, EventType: bool, ID: {3bad3a09-5826-4ed7-a832-10e3e2ee2a7d}) QT_TRANSLATE_NOOP("mockDevice", "Bool"), @@ -412,6 +417,9 @@ const QString translations[] { //: The pairing info of deviceClass mockDisplayPin QT_TRANSLATE_NOOP("mockDevice", "Please enter the secret which normaly will be displayed on the device. For the mockdevice the pin is 243681."), + //: The name of the Browser Item ActionType ({da6faef8-2816-430e-93bb-57e8f9582d29}) of DeviceClass mock + QT_TRANSLATE_NOOP("mockDevice", "Remove from favorites"), + //: The name of the ParamType (DeviceClass: mockInputType, Type: device, ID: {22add8c9-ee4f-43ad-8931-58e999313ac3}) QT_TRANSLATE_NOOP("mockDevice", "Search text"), diff --git a/tools/nymea-plugininfocompiler/plugininfocompiler.cpp b/tools/nymea-plugininfocompiler/plugininfocompiler.cpp index 7c418d56..eb58f4ec 100644 --- a/tools/nymea-plugininfocompiler/plugininfocompiler.cpp +++ b/tools/nymea-plugininfocompiler/plugininfocompiler.cpp @@ -228,6 +228,7 @@ void PluginInfoCompiler::writeDeviceClass(const DeviceClass &deviceClass) writeStateTypes(deviceClass.stateTypes(), deviceClass.name()); writeEventTypes(deviceClass.eventTypes(), deviceClass.name()); writeActionTypes(deviceClass.actionTypes(), deviceClass.name()); + writeBrowserItemActionTypes(deviceClass.browserItemActionTypes(), deviceClass.name()); } void PluginInfoCompiler::writeStateTypes(const StateTypes &stateTypes, const QString &deviceClassName) @@ -276,8 +277,24 @@ void PluginInfoCompiler::writeActionTypes(const ActionTypes &actionTypes, const writeExtern(QString("extern ActionTypeId %1;").arg(variableName)); writeParams(actionType.paramTypes(), deviceClassName, "Action", actionType.name()); - } + } +} +void PluginInfoCompiler::writeBrowserItemActionTypes(const ActionTypes &actionTypes, const QString &deviceClassName) +{ + foreach (const ActionType &actionType, actionTypes) { + QString variableName = QString("%1%2BrowserItemActionTypeId").arg(deviceClassName, actionType.name()[0].toUpper() + actionType.name().right(actionType.name().length() - 1)); + if (m_variableNames.contains(variableName)) { + qWarning().nospace() << "Error: Duplicate name " << variableName << " for Browser Item ActionType " << actionType.name() << " in DeviceClass " << deviceClassName << ". Skipping entry."; + return; + } + m_variableNames.append(variableName); + write(QString("ActionTypeId %1 = ActionTypeId(\"%2\");").arg(variableName).arg(actionType.id().toString())); + m_translationStrings.insert(actionType.displayName(), QString("The name of the Browser Item ActionType (%1) of DeviceClass %2").arg(actionType.id().toString()).arg(deviceClassName)); + writeExtern(QString("extern ActionTypeId %1;").arg(variableName)); + + writeParams(actionType.paramTypes(), deviceClassName, "BrowserItemAction", actionType.name()); + } } void PluginInfoCompiler::write(const QString &line) diff --git a/tools/nymea-plugininfocompiler/plugininfocompiler.h b/tools/nymea-plugininfocompiler/plugininfocompiler.h index aea74c25..c816416c 100644 --- a/tools/nymea-plugininfocompiler/plugininfocompiler.h +++ b/tools/nymea-plugininfocompiler/plugininfocompiler.h @@ -45,6 +45,7 @@ private: void writeStateTypes(const StateTypes &stateTypes, const QString &deviceClassName); void writeEventTypes(const EventTypes &eventTypes, const QString &deviceClassName); void writeActionTypes(const ActionTypes &actionTypes, const QString &deviceClassName); + void writeBrowserItemActionTypes(const ActionTypes &actionTypes, const QString &deviceClassName); void write(const QString &line = QString()); void writeExtern(const QString &line = QString());