diff --git a/debian/changelog b/debian/changelog index b5fc332d..9f1fc942 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +guh (0.4.0) vivid; urgency=medium + + * add EditDevice and notifications + + -- Simon Stürz Wed, 06 May 2015 10:13:58 +0200 + guh (0.3.0) utopic; urgency=medium * update rule engine and many other small changes diff --git a/guh.pri b/guh.pri index 982d9f50..8782c59d 100644 --- a/guh.pri +++ b/guh.pri @@ -1,6 +1,10 @@ # Parse and export GUH_VERSION_STRING GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') -DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" + +# define JSON protocol version +JSON_PROTOCOL_VERSION=21 + +DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" JSON_PROTOCOL_VERSION=21 QT+= network diff --git a/guh.pro b/guh.pro index 096ade74..ac478e48 100644 --- a/guh.pro +++ b/guh.pro @@ -22,7 +22,7 @@ test.commands = LD_LIBRARY_PATH=$$top_builddir/libguh make check QMAKE_EXTRA_TARGETS += licensecheck doc test -message("Building guh version $${GUH_VERSION_STRING}") +message("Building guh version $${GUH_VERSION_STRING} (API version $${JSON_PROTOCOL_VERSION})") coverage { message("Building coverage.") diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index bcce7c9e..5f9ed0f0 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -325,10 +325,8 @@ DeviceManager::DeviceError DeviceManager::addConfiguredDevice(const DeviceClassI return addConfiguredDeviceInternal(deviceClassId, descriptor.params(), deviceId); } -DeviceManager::DeviceError DeviceManager::editDevice(const DeviceId &deviceId, const ParamList ¶ms) +DeviceManager::DeviceError DeviceManager::editDevice(const DeviceId &deviceId, const ParamList ¶ms, const bool fromDiscovery) { - qDebug() << "EDIT DEVICE!!!!"; - Device *device = findConfiguredDevice(deviceId); if (!device) { return DeviceErrorDeviceNotFound; @@ -345,12 +343,15 @@ DeviceManager::DeviceError DeviceManager::editDevice(const DeviceId &deviceId, c return DeviceErrorPluginNotFound; } - // check if one of the given params is not editable - foreach (const ParamType ¶mType, deviceClass.paramTypes()) { - foreach (const Param ¶m, params) { - if (paramType.name() == param.name()) { - if (!paramType.editable()) - return DeviceErrorParameterNotEditable; + // if the params are discovered and not set by the user + if (!fromDiscovery) { + // check if one of the given params is not editable + foreach (const ParamType ¶mType, deviceClass.paramTypes()) { + foreach (const Param ¶m, params) { + if (paramType.name() == param.name()) { + if (!paramType.editable()) + return DeviceErrorParameterNotEditable; + } } } } @@ -373,27 +374,42 @@ DeviceManager::DeviceError DeviceManager::editDevice(const DeviceId &deviceId, c qWarning() << "Device edit failed. Not saving changes of device paramters."; return DeviceErrorSetupFailed; case DeviceSetupStatusAsync: - qDebug() << "Device edit async. Waiting for complete..."; m_asyncDeviceEdit.append(device); return DeviceErrorAsync; case DeviceSetupStatusSuccess: - qDebug() << "Device params edit complete."; + qDebug() << "Device edit complete."; break; } storeConfiguredDevices(); postSetupDevice(device); - emit deviceParamsChanged(device); + device->setupCompleted(); + emit deviceParamsChanged(device); return DeviceErrorNoError; } DeviceManager::DeviceError DeviceManager::editDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId) { - Q_UNUSED(deviceId) - Q_UNUSED(deviceDescriptorId) + Device *device = findConfiguredDevice(deviceId); + if (!device) { + return DeviceErrorDeviceNotFound; + } - return DeviceErrorNoError; + DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); + if (!deviceClass.isValid()) { + return DeviceErrorDeviceClassNotFound; + } + if (!deviceClass.createMethods().testFlag(DeviceClass::CreateMethodDiscovery)) { + return DeviceErrorCreationMethodNotSupported; + } + + DeviceDescriptor descriptor = m_discoveredDevices.take(deviceDescriptorId); + if (!descriptor.isValid()) { + return DeviceErrorDeviceDescriptorNotFound; + } + + return editDevice(deviceId, descriptor.params(), true); } /*! Initiates a pairing with a \l{DeviceClass}{Device} with the given \a pairingTransactionId, \a deviceClassId and \a params. @@ -836,6 +852,15 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic if (status == DeviceSetupStatusFailure) { if (m_configuredDevices.contains(device)) { + if (m_asyncDeviceEdit.contains(device)) { + m_asyncDeviceEdit.removeAll(device); + qWarning() << QString("Error in device setup after edit params. Device %1 (%2) would not be functional.").arg(device->name()).arg(device->id().toString()); + + storeConfiguredDevices(); + device->setupCompleted(); + // TODO: recover old params.?? + emit deviceEditFinished(device, DeviceError::DeviceErrorSetupFailed); + } qWarning() << QString("Error in device setup. Device %1 (%2) will not be functional.").arg(device->name()).arg(device->id().toString()); emit deviceSetupFinished(device, DeviceError::DeviceErrorSetupFailed); return; @@ -863,13 +888,13 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic m_pluginTimerUsers.append(device); } - // if this is a async device edit + // if this is a async device edit result if (m_asyncDeviceEdit.contains(device)) { m_asyncDeviceEdit.removeAll(device); storeConfiguredDevices(); device->setupCompleted(); - qDebug() << "emit Device edit finished!!!"; emit deviceEditFinished(device, DeviceManager::DeviceErrorNoError); + emit deviceParamsChanged(device); return; } @@ -1031,7 +1056,7 @@ void DeviceManager::replyReady(const PluginId &pluginId, QNetworkReply *reply) foreach (DevicePlugin *devicePlugin, m_devicePlugins) { if (devicePlugin->requiredHardware().testFlag(HardwareResourceNetworkManager) && devicePlugin->pluginId() == pluginId) { devicePlugin->networkManagerReplyReady(reply); - } + } } } void DeviceManager::upnpDiscoveryFinished(const QList &deviceDescriptorList, const PluginId &pluginId) @@ -1143,7 +1168,7 @@ DeviceManager::DeviceError DeviceManager::verifyParams(const QList pa } // This paramType has a default value... lets fill in that one. - if (!paramType.defaultValue().isNull()) { + if (!paramType.defaultValue().isNull() && !found) { found = true; params.append(Param(paramType.name(), paramType.defaultValue())); } diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index 1c9bb3ae..b53da077 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -99,7 +99,7 @@ public: DeviceError addConfiguredDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const DeviceId id = DeviceId::createDeviceId()); DeviceError addConfiguredDevice(const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId, const DeviceId &id = DeviceId::createDeviceId()); - DeviceError editDevice(const DeviceId &deviceId, const ParamList ¶ms); + DeviceError editDevice(const DeviceId &deviceId, const ParamList ¶ms, const bool fromDiscovery = false); DeviceError editDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId); DeviceError pairDevice(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms); diff --git a/libguh/types/paramtype.cpp b/libguh/types/paramtype.cpp index 045a5b52..71d9df7a 100644 --- a/libguh/types/paramtype.cpp +++ b/libguh/types/paramtype.cpp @@ -139,13 +139,13 @@ void ParamType::setAllowedValues(const QList allowedValues) m_allowedValues = allowedValues; } -/*! Returns true if this ParamType is editable by the user. By default each ParamType is editable. */ +/*! Returns true if this ParamType is editable by the user. By default a ParamType is always editable. */ bool ParamType::editable() const { return m_editable; } -/*! Sets this ParamType \a editable. By default each ParamType is editable. */ +/*! Sets this ParamType \a editable. By default a ParamType is always editable. */ void ParamType::setEditable(const bool &editable) { m_editable = editable; diff --git a/plugins/deviceplugins/mock/devicepluginmock.cpp b/plugins/deviceplugins/mock/devicepluginmock.cpp index c1f3e560..38dbe092 100644 --- a/plugins/deviceplugins/mock/devicepluginmock.cpp +++ b/plugins/deviceplugins/mock/devicepluginmock.cpp @@ -54,7 +54,11 @@ DeviceManager::DeviceError DevicePluginMock::discoverDevices(const DeviceClassId DeviceManager::DeviceSetupStatus DevicePluginMock::setupDevice(Device *device) { - qDebug() << "Mockdevice created returning true" << device->paramValue("httpport").toInt() << device->paramValue("async").toBool() << device->paramValue("broken").toBool(); + qDebug() << "Mockdevice created returning true" + << device->paramValue("name").toString() + << device->paramValue("httpport").toInt() + << device->paramValue("async").toBool() + << device->paramValue("broken").toBool(); if (device->paramValue("broken").toBool()) { qWarning() << "This device is intentionally broken."; @@ -82,7 +86,11 @@ DeviceManager::DeviceSetupStatus DevicePluginMock::setupDevice(Device *device) DeviceManager::DeviceSetupStatus DevicePluginMock::editDevice(Device *device) { - qDebug() << "Mockdevice edit params to" << device->paramValue("httpport").toInt() << device->paramValue("async").toBool() << device->paramValue("broken").toBool(); + qDebug() << "Mockdevice edit params to" + << device->paramValue("name").toString() + << device->paramValue("httpport").toInt() + << device->paramValue("async").toBool() + << device->paramValue("broken").toBool(); if (device->paramValue("broken").toBool()) { qWarning() << "This device is intentionally broken."; @@ -192,18 +200,22 @@ void DevicePluginMock::emitDevicesDiscovered() QList deviceDescriptors; if (m_discoveredDeviceCount > 0) { - DeviceDescriptor d1(mockDeviceClassId, "Mock Device (Discovered)"); + DeviceDescriptor d1(mockDeviceClassId, "Mock Device 1 (Discovered)", "55555"); ParamList params; + Param name("name", "Discovered Mock Device 1"); Param httpParam("httpport", "55555"); + params.append(name); params.append(httpParam); d1.setParams(params); deviceDescriptors.append(d1); } if (m_discoveredDeviceCount > 1) { - DeviceDescriptor d2(mockDeviceClassId, "Mock Device (Discovered)"); + DeviceDescriptor d2(mockDeviceClassId, "Mock Device 2 (Discovered)", "55556"); ParamList params; + Param name("name", "Discovered Mock Device 2"); Param httpParam("httpport", "55556"); + params.append(name); params.append(httpParam); d2.setParams(params); deviceDescriptors.append(d2); diff --git a/plugins/deviceplugins/mock/devicepluginmock.json b/plugins/deviceplugins/mock/devicepluginmock.json index 99ec0875..e32e23bf 100644 --- a/plugins/deviceplugins/mock/devicepluginmock.json +++ b/plugins/deviceplugins/mock/devicepluginmock.json @@ -20,6 +20,13 @@ } ], "paramTypes": [ + { + "name": "name", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "Mock device", + "editable": false + }, { "name": "httpport", "type": "int" @@ -32,8 +39,7 @@ { "name": "broken", "type": "bool", - "defaultValue": false, - "editable": false + "defaultValue": false } ], "stateTypes": [ @@ -109,6 +115,13 @@ "name": "Mock Device (Auto created)", "createMethods": ["auto"], "paramTypes": [ + { + "name": "name", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "Mock device", + "editable": false + }, { "name": "httpport", "type": "int" diff --git a/plugins/deviceplugins/mock/httpdaemon.cpp b/plugins/deviceplugins/mock/httpdaemon.cpp index 992e9668..5094a6b5 100644 --- a/plugins/deviceplugins/mock/httpdaemon.cpp +++ b/plugins/deviceplugins/mock/httpdaemon.cpp @@ -62,10 +62,13 @@ void HttpDaemon::actionExecuted(const ActionTypeId &actionTypeId) void HttpDaemon::updateDevice(Device *device) { + if (device->paramValue("httpport").toInt() != m_device->paramValue("httpport").toInt()) { + close(); + listen(QHostAddress::Any, device->paramValue("httpport").toInt()); + qDebug() << "Mockdevice httpport updated and listening now on" << device->paramValue("httpport").toInt(); + } + m_device = device; - close(); - listen(QHostAddress::Any, device->paramValue("httpport").toInt()); - qDebug() << "Mockdevice updated and listening now on" << device->paramValue("httpport").toInt(); } void HttpDaemon::readClient() diff --git a/plugins/generateplugininfo.py b/plugins/generateplugininfo.py index ec6ce024..e95ae11d 100755 --- a/plugins/generateplugininfo.py +++ b/plugins/generateplugininfo.py @@ -8,100 +8,105 @@ inputFile = open(sys.argv[1], "r") outputfile = open(sys.argv[2], "w") variableNames = [] -def out(line): - outputfile.write("%s\n" % line) - try: - pluginMap = json.loads(inputFile.read()) -except: - print("Error opening input file") + pluginMap = json.loads(inputFile.read()) + +except ValueError as e: + print " --> Error loading input file \"%s\"" % (sys.argv[1]) + print " %s" % (e) + exit -1 +def out(line): + outputfile.write("%s\n" % line) + def extractVendors(pluginMap): - for vendor in pluginMap['vendors']: - try: - out("VendorId %sVendorId = VendorId(\"%s\");" % pluginMap["idName"], pluginMap["id"]) - except: - pass - extractDeviceClasses(vendor) + for vendor in pluginMap['vendors']: + try: + out("VendorId %sVendorId = VendorId(\"%s\");" % pluginMap["idName"], pluginMap["id"]) + except: + pass + extractDeviceClasses(vendor) def extractDeviceClasses(vendorMap): - for deviceClass in vendorMap["deviceClasses"]: - print("have deviceclass %s" % deviceClass["deviceClassId"]) - try: - variableName = "%sDeviceClassId" % (deviceClass["idName"]) - if not variableName in variableNames: - variableNames.append(variableName) - out("DeviceClassId %s = DeviceClassId(\"%s\");" % (variableName, deviceClass["deviceClassId"])) - else: - print("duplicated variable name \"%s\" for DeviceClassId %s -> skipping") % (variableName, deviceClass["deviceClassId"]) - except: - pass - extractActionTypes(deviceClass) - extractStateTypes(deviceClass) - extractEventTypes(deviceClass) + for deviceClass in vendorMap["deviceClasses"]: + print("have deviceclass %s" % deviceClass["deviceClassId"]) + try: + variableName = "%sDeviceClassId" % (deviceClass["idName"]) + if not variableName in variableNames: + variableNames.append(variableName) + out("DeviceClassId %s = DeviceClassId(\"%s\");" % (variableName, deviceClass["deviceClassId"])) + else: + print("duplicated variable name \"%s\" for DeviceClassId %s -> skipping") % (variableName, deviceClass["deviceClassId"]) + except: + pass + extractActionTypes(deviceClass) + extractStateTypes(deviceClass) + extractEventTypes(deviceClass) def extractStateTypes(deviceClassMap): - try: - for stateType in deviceClassMap["stateTypes"]: - try: - variableName = "%sStateTypeId" % (stateType["idName"]) - if not variableName in variableNames: - variableNames.append(variableName) - out("StateTypeId %s = StateTypeId(\"%s\");" % (variableName, stateType["id"])) - else: - print("duplicated variable name \"%s\" for StateTypeId %s -> skipping") % (variableName, stateType["id"]) - - # create ActionTypeId if the state is writable - if 'writable' in stateType: - if stateType['writable'] == True: - print("create ActionTypeId for StateType %s" % stateType["id"]) - vName = "%sActionTypeId" % (stateType["idName"]) - if not vName in variableNames: - variableNames.append(vName) - out("ActionTypeId %s = ActionTypeId(\"%s\");" % (vName, stateType["id"])) - else: - print("duplicated variable name \"%s\" for ActionTypeId %s -> skipping") % (variableName, stateType["id"]) - except: - pass - except: - pass + try: + for stateType in deviceClassMap["stateTypes"]: + try: + variableName = "%sStateTypeId" % (stateType["idName"]) + if not variableName in variableNames: + variableNames.append(variableName) + out("StateTypeId %s = StateTypeId(\"%s\");" % (variableName, stateType["id"])) + else: + print("duplicated variable name \"%s\" for StateTypeId %s -> skipping") % (variableName, stateType["id"]) + # create ActionTypeId if the state is writable + if 'writable' in stateType: + if stateType['writable'] == True: + print("create ActionTypeId for StateType %s" % stateType["id"]) + vName = "%sActionTypeId" % (stateType["idName"]) + if not vName in variableNames: + variableNames.append(vName) + out("ActionTypeId %s = ActionTypeId(\"%s\");" % (vName, stateType["id"])) + else: + print("duplicated variable name \"%s\" for ActionTypeId %s -> skipping") % (variableName, stateType["id"]) + except: + pass + except: + pass def extractActionTypes(deviceClassMap): - try: - for actionType in deviceClassMap["actionTypes"]: - try: - variableName = "%sActionTypeId" % (actionType["idName"]) - if not variableName in variableNames: - variableNames.append(variableName) - out("ActionTypeId %s = ActionTypeId(\"%s\");" % (variableName, actionType["id"])) - else: - print("duplicated variable name \"%s\" for ActionTypeId %s -> skipping") % (variableName, actionType["id"]) - except: - pass - except: - pass + try: + for actionType in deviceClassMap["actionTypes"]: + try: + variableName = "%sActionTypeId" % (actionType["idName"]) + if not variableName in variableNames: + variableNames.append(variableName) + out("ActionTypeId %s = ActionTypeId(\"%s\");" % (variableName, actionType["id"])) + else: + print("duplicated variable name \"%s\" for ActionTypeId %s -> skipping") % (variableName, actionType["id"]) + except: + pass + except: + pass def extractEventTypes(deviceClassMap): - try: - for eventType in deviceClassMap["eventTypes"]: - try: - variableName = "%sEventTypeId" % (eventType["idName"]) - if not variableName in variableNames: - variableNames.append(variableName) - out("EventTypeId %s = EventTypeId(\"%s\");" % (variableName, eventType["id"])) - else: - print("duplicated variable name \"%s\" for EventTypeId %s -> skipping") % (variableName, eventType["id"]) - except: - pass - except: - pass + try: + for eventType in deviceClassMap["eventTypes"]: + try: + variableName = "%sEventTypeId" % (eventType["idName"]) + if not variableName in variableNames: + variableNames.append(variableName) + out("EventTypeId %s = EventTypeId(\"%s\");" % (variableName, eventType["id"])) + else: + print("duplicated variable name \"%s\" for EventTypeId %s -> skipping") % (variableName, eventType["id"]) + except: + pass + except: + pass +print " --> generate plugininfo.h" +print "PluginId for plugin \"%s\" = %s" %(pluginMap['name'], pluginMap['id']) + # write header out("/* This file is generated by the guh build system. Any changes to this file will") out(" * be lost.") @@ -115,6 +120,10 @@ out("#define PLUGININFO_H") out("#include \"typeutils.h\"") out("") out("PluginId pluginId = PluginId(\"%s\");" % pluginMap['id']) + extractVendors(pluginMap) + out("") out("#endif") + +print " --> finished writing \"%s\"" % (sys.argv[2]) diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 6c6db200..5212c0b4 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -271,6 +271,11 @@ DeviceManager::DeviceError GuhCore::editDevice(const DeviceId &deviceId, const P return m_deviceManager->editDevice(deviceId, params); } +DeviceManager::DeviceError GuhCore::editDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId) +{ + return m_deviceManager->editDevice(deviceId, deviceDescriptorId); +} + /*! Calls the metheod RuleEngine::rule(). * \sa RuleEngine, */ QList GuhCore::rules() const diff --git a/server/guhcore.h b/server/guhcore.h index 493eaaef..d9cad598 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -68,6 +68,7 @@ public: Device *findConfiguredDevice(const DeviceId &deviceId) const; QList findConfiguredDevices(const DeviceClassId &deviceClassId) const; DeviceManager::DeviceError editDevice(const DeviceId &deviceId, const ParamList ¶ms); + DeviceManager::DeviceError editDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId); DeviceManager::DeviceError removeConfiguredDevice(const DeviceId &deviceId, const QHash &removePolicyList); DeviceManager::DeviceError pairDevice(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId); @@ -92,6 +93,7 @@ signals: void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value); void deviceRemoved(const DeviceId &deviceId); void deviceAdded(Device *device); + void deviceParamsChanged(Device *device); void actionExecuted(const ActionId &id, DeviceManager::DeviceError status); void devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors); diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index 8b0d18a8..9b2bd30e 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -236,9 +236,15 @@ DeviceHandler::DeviceHandler(QObject *parent) : params.insert("device", JsonTypes::deviceRef()); setParams("DeviceAdded", params); + params.clear(); returns.clear(); + setDescription("DeviceParamsChanged", "Emitted whenever the params of a Device changed (by editing or rediscovering)."); + params.insert("device", JsonTypes::deviceRef()); + setParams("DeviceParamsChanged", params); + connect(GuhCore::instance(), &GuhCore::deviceStateChanged, this, &DeviceHandler::deviceStateChanged); connect(GuhCore::instance(), &GuhCore::deviceRemoved, this, &DeviceHandler::deviceRemovedNotification); connect(GuhCore::instance(), &GuhCore::deviceAdded, this, &DeviceHandler::deviceAddedNotification); + connect(GuhCore::instance(), &GuhCore::deviceParamsChanged, this, &DeviceHandler::deviceParamsChangedNotification); connect(GuhCore::instance(), &GuhCore::devicesDiscovered, this, &DeviceHandler::devicesDiscovered, Qt::QueuedConnection); connect(GuhCore::instance(), &GuhCore::deviceSetupFinished, this, &DeviceHandler::deviceSetupFinished); connect(GuhCore::instance(), &GuhCore::deviceEditFinished, this, &DeviceHandler::deviceEditFinished); @@ -439,13 +445,13 @@ JsonReply *DeviceHandler::EditDevice(const QVariantMap ¶ms) DeviceId deviceId = DeviceId(params.value("deviceId").toString()); ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); - DeviceManager::DeviceError status = GuhCore::instance()->editDevice(deviceId, deviceParams); - - // if (deviceDescriptorId.isNull()) { - // status = GuhCore::instance()->addConfiguredDevice(deviceClass, deviceParams, newDeviceId); - // } else { - // status = GuhCore::instance()->addConfiguredDevice(deviceClass, deviceDescriptorId, newDeviceId); - // } + DeviceManager::DeviceError status; + DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString()); + if (deviceDescriptorId.isNull()) { + status = GuhCore::instance()->editDevice(deviceId, deviceParams); + } else { + status = GuhCore::instance()->editDevice(deviceId, deviceDescriptorId); + } if (status == DeviceManager::DeviceErrorAsync) { JsonReply *asyncReply = createAsyncReply("EditDevice"); @@ -586,6 +592,14 @@ void DeviceHandler::deviceAddedNotification(Device *device) emit DeviceAdded(params); } +void DeviceHandler::deviceParamsChangedNotification(Device *device) +{ + QVariantMap params; + params.insert("device", JsonTypes::packDevice(device)); + + emit DeviceParamsChanged(params); +} + void DeviceHandler::devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors) { if (!m_discoverRequests.contains(deviceClassId)) { diff --git a/server/jsonrpc/devicehandler.h b/server/jsonrpc/devicehandler.h index 2592a23b..5c9c0954 100644 --- a/server/jsonrpc/devicehandler.h +++ b/server/jsonrpc/devicehandler.h @@ -71,6 +71,7 @@ signals: void StateChanged(const QVariantMap ¶ms); void DeviceRemoved(const QVariantMap ¶ms); void DeviceAdded(const QVariantMap ¶ms); + void DeviceParamsChanged(const QVariantMap ¶ms); private slots: void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value); @@ -79,6 +80,8 @@ private slots: void deviceAddedNotification(Device *device); + void deviceParamsChangedNotification(Device *device); + void devicesDiscovered(const DeviceClassId &deviceClassId, const QList deviceDescriptors); void deviceSetupFinished(Device *device, DeviceManager::DeviceError status); diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index b734abdf..aa7c728f 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -47,8 +47,6 @@ #include #include -#define JSON_PROTOCOL_VERSION 20 - JsonRPCServer::JsonRPCServer(QObject *parent): JsonHandler(parent), #ifdef TESTING_ENABLED diff --git a/tests/auto/api.json b/tests/auto/api.json index 7f2a4ecc..03f47ab9 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -20 +21 { "methods": { "Actions.ExecuteAction": { @@ -55,6 +55,19 @@ "o:deviceId": "Uuid" } }, + "Devices.EditDevice": { + "description": "Edit the parameters of a device. The device params will be set to the passed parameters and the setup device will be called. If the device is discoverable, you can perform a GetDiscoveredDevices before calling this method and pass the new DeviceDescriptor (rediscover). If a parameter is not editable, you will find a 'editable': false in the ParamType. By default, every Param is editable.", + "params": { + "deviceId": "Uuid", + "o:deviceDescriptorId": "Uuid", + "o:deviceParams": [ + "$ref:Param" + ] + }, + "returns": { + "deviceError": "$ref:DeviceError" + } + }, "Devices.GetActionTypes": { "description": "Get action types for a specified deviceClassId.", "params": { @@ -371,6 +384,12 @@ "device": "$ref:Device" } }, + "Devices.DeviceParamsChanged": { + "description": "Emitted whenever the params of a Device changed (by editing or rediscovering).", + "params": { + "device": "$ref:Device" + } + }, "Devices.DeviceRemoved": { "description": "Emitted whenever a Device was removed.", "params": { @@ -496,9 +515,11 @@ "DeviceErrorSetupMethodNotSupported", "DeviceErrorHardwareNotAvailable", "DeviceErrorHardwareFailure", + "DeviceErrorAuthentificationFailure", "DeviceErrorAsync", "DeviceErrorDeviceInUse", - "DeviceErrorPairingTransactionIdNotFound" + "DeviceErrorPairingTransactionIdNotFound", + "DeviceErrorParameterNotEditable" ], "Event": { "deviceId": "Uuid", @@ -578,6 +599,7 @@ "Variant" ], "o:defaultValue": "Variant", + "o:editable": "Bool", "o:inputType": "$ref:InputType", "o:maxValue": "Variant", "o:minValue": "Variant", diff --git a/tests/auto/devices/testdevices.cpp b/tests/auto/devices/testdevices.cpp index d9144073..9d18d1f1 100644 --- a/tests/auto/devices/testdevices.cpp +++ b/tests/auto/devices/testdevices.cpp @@ -73,6 +73,9 @@ private slots: void editDevices_data(); void editDevices(); + void editByDiscovery_data(); + void editByDiscovery(); + // Keep this the last one! It'll remove the configured mock device void removeDevice_data(); void removeDevice(); @@ -288,6 +291,10 @@ void TestDevices::storedDevices() QVariantMap params; params.insert("deviceClassId", mockDeviceClassId); QVariantList deviceParams; + QVariantMap nameParam; + nameParam.insert("name", "name"); + nameParam.insert("value", "Blub Blub device"); + deviceParams.append(nameParam); QVariantMap asyncParam; asyncParam.insert("name", "async"); asyncParam.insert("value", false); @@ -301,6 +308,7 @@ void TestDevices::storedDevices() httpportParam.insert("value", 8888); deviceParams.append(httpportParam); params.insert("deviceParams", deviceParams); + QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); verifyDeviceError(response); DeviceId addedDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); @@ -314,19 +322,16 @@ void TestDevices::storedDevices() bool found = false; foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { if (DeviceId(device.toMap().value("id").toString()) == addedDeviceId) { -// foreach (const QVariant ¶mVariant, device.toMap().value("params").toList()) { -// if () -// } - qDebug() << "found added device" << device.toMap().value("params"); qDebug() << "expected deviceParams:" << deviceParams; - QCOMPARE(device.toMap().value("params").toList(), deviceParams); + verifyParams(deviceParams, device.toMap().value("params").toList()); found = true; break; } } QVERIFY2(found, "Device missing in config!"); + params.clear(); params.insert("deviceId", addedDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); @@ -536,28 +541,39 @@ void TestDevices::editDevices_data() brokenParamDifferent.insert("value", true); brokenChangedDeviceParams.append(brokenParamDifferent); + QVariantList nameChangedDeviceParams; + QVariantMap nameParam; + nameParam.insert("name", "name"); + nameParam.insert("value", "Awesome Mockdevice"); + nameChangedDeviceParams.append(nameParam); + + QVariantList asyncAndPortChangeDeviceParams; asyncAndPortChangeDeviceParams.append(asyncParamDifferent); asyncAndPortChangeDeviceParams.append(httpportParamDifferent); - QVariantList changeAllDeviceParams; - changeAllDeviceParams.append(asyncParamDifferent); - changeAllDeviceParams.append(httpportParamDifferent); - changeAllDeviceParams.append(brokenParamDifferent); + + QVariantList changeAllEditableDeviceParams; + changeAllEditableDeviceParams.append(nameParam); + changeAllEditableDeviceParams.append(asyncParamDifferent); + changeAllEditableDeviceParams.append(httpportParamDifferent); + //changeAllEditableDeviceParams.append(brokenParamDifferent); + QTest::addColumn("broken"); QTest::addColumn("newDeviceParams"); QTest::addColumn("deviceError"); - QTest::newRow("valid - change async param") << asyncChangeDeviceParams << DeviceManager::DeviceErrorNoError; - QTest::newRow("valid - change httpport param") << httpportChangeDeviceParams << DeviceManager::DeviceErrorNoError; - QTest::newRow("valid - change httpport and async param") << asyncAndPortChangeDeviceParams << DeviceManager::DeviceErrorNoError; - QTest::newRow("invalid - change broken param (not editable)") << brokenChangedDeviceParams << DeviceManager::DeviceErrorParameterNotEditable; - QTest::newRow("invalid - change all params (also the not editable one)") << changeAllDeviceParams << DeviceManager::DeviceErrorParameterNotEditable; + QTest::newRow("valid - change async param") << false << asyncChangeDeviceParams << DeviceManager::DeviceErrorNoError; + QTest::newRow("valid - change httpport param") << false << httpportChangeDeviceParams << DeviceManager::DeviceErrorNoError; + QTest::newRow("valid - change httpport and async param") << false << asyncAndPortChangeDeviceParams << DeviceManager::DeviceErrorNoError; + QTest::newRow("invalid - change name param (not editable)") << false << nameChangedDeviceParams << DeviceManager::DeviceErrorParameterNotEditable; + QTest::newRow("invalid - change all params (except broken)") << false << changeAllEditableDeviceParams << DeviceManager::DeviceErrorParameterNotEditable; } void TestDevices::editDevices() { + QFETCH(bool, broken); QFETCH(QVariantList, newDeviceParams); QFETCH(DeviceManager::DeviceError, deviceError); @@ -565,13 +581,17 @@ void TestDevices::editDevices() QVariantMap params; params.insert("deviceClassId", mockDeviceClassId); QVariantList deviceParams; + QVariantMap nameParam; + nameParam.insert("name", "name"); + nameParam.insert("value", "Test edit mockdevice"); + deviceParams.append(nameParam); QVariantMap asyncParam; asyncParam.insert("name", "async"); asyncParam.insert("value", false); deviceParams.append(asyncParam); QVariantMap brokenParam; brokenParam.insert("name", "broken"); - brokenParam.insert("value", false); + brokenParam.insert("value", broken); deviceParams.append(brokenParam); QVariantMap httpportParam; httpportParam.insert("name", "httpport"); @@ -581,10 +601,13 @@ void TestDevices::editDevices() QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); verifyDeviceError(response); - // now edit the added device DeviceId deviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!deviceId.isNull()); + // now EDIT the added device + response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + + // edit the added and verified device QVariantMap editParams; editParams.insert("deviceId", deviceId); editParams.insert("deviceParams", newDeviceParams); @@ -593,40 +616,220 @@ void TestDevices::editDevices() response = injectAndWait("Devices.EditDevice", editParams); verifyDeviceError(response, deviceError); - if (deviceError != DeviceManager::DeviceErrorNoError) { + // if the edit should have been successfull + if (deviceError == DeviceManager::DeviceErrorNoError) { + + response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + + bool found = false; + foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { + if (DeviceId(device.toMap().value("id").toString()) == deviceId) { + qDebug() << "found added device" << device.toMap().value("params"); + qDebug() << "expected deviceParams:" << newDeviceParams; + // check if the edit was ok + verifyParams(newDeviceParams, device.toMap().value("params").toList(), false); + found = true; + break; + } + } + QVERIFY2(found, "Device missing in config!"); + + // Restart the core instance to check if settings are loaded at startup + restartServer(); + + response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + + found = false; + foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { + if (DeviceId(device.toMap().value("id").toString()) == deviceId) { + qDebug() << "found added device" << device.toMap().value("params"); + qDebug() << "expected deviceParams:" << newDeviceParams; + // check if the edit was ok + verifyParams(newDeviceParams, device.toMap().value("params").toList(), false); + found = true; + break; + } + } + QVERIFY2(found, "Device missing in config!"); + + // delete it params.clear(); params.insert("deviceId", deviceId); response.clear(); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response); return; + } else { + // The edit was not ok, check if the old params are still there + response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + + bool found = false; + foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { + if (DeviceId(device.toMap().value("id").toString()) == deviceId) { + qDebug() << "found added device" << device.toMap().value("params"); + qDebug() << "expected deviceParams:" << newDeviceParams; + // check if the params are unchanged + verifyParams(deviceParams, device.toMap().value("params").toList()); + found = true; + break; + } + } + QVERIFY2(found, "Device missing in config!"); + + // Restart the core instance to check if settings are loaded at startup + restartServer(); + + response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + + found = false; + foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { + if (DeviceId(device.toMap().value("id").toString()) == deviceId) { + qDebug() << "found added device" << device.toMap().value("params"); + qDebug() << "expected deviceParams:" << newDeviceParams; + // check if after the reboot the settings are unchanged + verifyParams(deviceParams, device.toMap().value("params").toList()); + found = true; + break; + } + } + QVERIFY2(found, "Device missing in config!"); } - // Restart the core instance to check if settings are loaded at startup - restartServer(); + // delete it + params.clear(); + params.insert("deviceId", deviceId); + response = injectAndWait("Devices.RemoveConfiguredDevice", params); + verifyDeviceError(response); +} + +void TestDevices::editByDiscovery_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + QTest::addColumn("error"); + QTest::addColumn("discoveryParams"); + + QVariantList discoveryParams; + QVariantMap resultCountParam; + resultCountParam.insert("name", "resultCount"); + resultCountParam.insert("value", 2); + discoveryParams.append(resultCountParam); + + QTest::newRow("discover 2 devices with params") << mockDeviceClassId << 2 << DeviceManager::DeviceErrorNoError << discoveryParams; +} + +void TestDevices::editByDiscovery() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + QFETCH(DeviceManager::DeviceError, error); + QFETCH(QVariantList, discoveryParams); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + params.insert("discoveryParams", discoveryParams); + QVariant response = injectAndWait("Devices.GetDiscoveredDevices", params); + + verifyDeviceError(response); + if (error == DeviceManager::DeviceErrorNoError) { + QCOMPARE(response.toMap().value("params").toMap().value("deviceDescriptors").toList().count(), resultCount); + } + + // add Discovered Device 1 port 55555 + QVariantList deviceDescriptors = response.toMap().value("params").toMap().value("deviceDescriptors").toList(); + + DeviceDescriptorId descriptorId1; + foreach (const QVariant &descriptor, deviceDescriptors) { + // find the device with port 55555 + if (descriptor.toMap().value("description").toString() == "55555") { + descriptorId1 = DeviceDescriptorId(descriptor.toMap().value("id").toString()); + qDebug() << descriptorId1.toString(); + break; + } + } + + qDebug() << "adding descriptorId 1" << descriptorId1; + + QVERIFY(!descriptorId1.isNull()); + + params.clear(); + response.clear(); + params.insert("deviceClassId", deviceClassId); + params.insert("deviceDescriptorId", descriptorId1); + response = injectAndWait("Devices.AddConfiguredDevice", params); + + DeviceId deviceId(response.toMap().value("params").toMap().value("deviceId").toString()); + QVERIFY(!deviceId.isNull()); + + // and now rediscover, and edit the first device with the second + params.clear(); + response.clear(); + params.insert("deviceClassId", deviceClassId); + params.insert("discoveryParams", discoveryParams); + response = injectAndWait("Devices.GetDiscoveredDevices", params); + + verifyDeviceError(response, error); + if (error == DeviceManager::DeviceErrorNoError) { + QCOMPARE(response.toMap().value("params").toMap().value("deviceDescriptors").toList().count(), resultCount); + } + + // get the second device + DeviceDescriptorId descriptorId2; + foreach (const QVariant &descriptor, deviceDescriptors) { + // find the device with port 55556 + if (descriptor.toMap().value("description").toString() == "55556") { + descriptorId2 = DeviceDescriptorId(descriptor.toMap().value("id").toString()); + break; + } + } + QVERIFY(!descriptorId2.isNull()); + + qDebug() << "edit device 1 (55555) with descriptor 2 (55556) " << descriptorId2; + + // EDIT + response.clear(); + params.clear(); + params.insert("deviceId", deviceId.toString()); + params.insert("deviceDescriptorId", descriptorId2); + response = injectAndWait("Devices.EditDevice", params); + verifyDeviceError(response, error); + + response.clear(); response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + QVariantMap deviceMap; bool found = false; foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { if (DeviceId(device.toMap().value("id").toString()) == deviceId) { qDebug() << "found added device" << device.toMap().value("params"); - - foreach (QVariant newParam, newDeviceParams) { - foreach (QVariant deviceParam, device.toMap().value("params").toList()) { - if (newParam.toMap().value("name").toString() == deviceParam.toMap().value("name").toString()) { - QCOMPARE(newParam.toMap().value("value"), deviceParam.toMap().value("value")); - } - } - } found = true; + deviceMap = device.toMap(); break; } } + + printJson(deviceMap); + QVERIFY2(found, "Device missing in config!"); + QCOMPARE(deviceMap.value("id").toString(), deviceId.toString()); + if (deviceMap.contains("setupComplete")) { + QVERIFY2(deviceMap.value("setupComplete").toBool(), "Setup not completed after edit"); + } + + // Note: this shows that by discovery a not editable param (name) can be changed! + foreach (QVariant param, deviceMap.value("params").toList()) { + if (param.toMap().value("name") == "name") { + QCOMPARE(param.toMap().value("value").toString(), QString("Discovered Mock Device 2")); + } + if (param.toMap().value("name") == "httpport") { + QCOMPARE(param.toMap().value("value").toInt(), 55556); + } + } + params.clear(); - params.insert("deviceId", deviceId); + params.insert("deviceId", deviceId.toString()); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response); } @@ -667,7 +870,6 @@ void TestDevices::removeDevice() } } - #include "testdevices.moc" QTEST_MAIN(TestDevices) diff --git a/tests/auto/guhtestbase.h b/tests/auto/guhtestbase.h index 46efa584..74ba70e3 100644 --- a/tests/auto/guhtestbase.h +++ b/tests/auto/guhtestbase.h @@ -88,6 +88,30 @@ protected: verifyError(response, "deviceError", JsonTypes::deviceErrorToString(error)); } + inline void verifyParams(const QVariantList &requestList, const QVariantList &responseList, bool allRequired = true) + { + if (allRequired) + QVERIFY2(requestList.count() == responseList.count(), "Not the same count of param in response."); + foreach (const QVariant &requestParam, requestList) { + bool found = false; + foreach (const QVariant &responseParam, responseList) { + if (requestParam.toMap().value("name") == responseParam.toMap().value("name")){ + QCOMPARE(requestParam.toMap().value("value"), responseParam.toMap().value("value")); + found = true; + break; + } + } + if (allRequired) + QVERIFY2(found, "Param missing"); + } + } + + // just for debugging + inline void printJson(const QVariant &response) { + QJsonDocument jsonDoc = QJsonDocument::fromVariant(response); + qDebug() << jsonDoc.toJson(); + } + void restartServer(); protected: