From fc9b6f1887be564ce5e8ff161fb6262d4715f3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 20 Oct 2015 13:35:52 +0200 Subject: [PATCH] add device parent child relation add remove policy to REST expand remove policy --- libguh/devicemanager.cpp | 16 +++ libguh/devicemanager.h | 5 +- .../upnpdiscovery/upnpdiscoveryrequest.cpp | 2 +- libguh/plugin/device.cpp | 18 ++- libguh/plugin/device.h | 5 +- .../philipshue/devicepluginphilipshue.cpp | 7 +- .../philipshue/devicepluginphilipshue.json | 2 +- server/guhcore.cpp | 123 ++++++++++++++++-- server/guhcore.h | 8 +- server/jsonrpc/devicehandler.cpp | 31 ++++- server/jsonrpc/jsontypes.cpp | 5 + server/rest/devicesresource.cpp | 79 +++++++---- server/rest/devicesresource.h | 2 +- server/ruleengine.cpp | 50 ++++++- 14 files changed, 290 insertions(+), 63 deletions(-) diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index add42aba..07cec888 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -741,6 +741,18 @@ QList DeviceManager::findConfiguredDevices(const DeviceClassId &device return ret; } +/*! Returns all child \l{Device}{Devices} of the given \a device. */ +QList DeviceManager::findChildDevices(Device *device) const +{ + QList ret; + foreach (Device *d, m_configuredDevices) { + if (d->parentId() == device->id()) { + ret.append(d); + } + } + return ret; +} + /*! For conveninece, this returns the \l{DeviceClass} with the id given by \a deviceClassId. * Note: The returned \l{DeviceClass} may be invalid. */ DeviceClass DeviceManager::findDeviceClass(const DeviceClassId &deviceClassId) const @@ -881,6 +893,7 @@ void DeviceManager::loadConfiguredDevices() settings.beginGroup(idString); Device *device = new Device(PluginId(settings.value("pluginid").toString()), DeviceId(idString), DeviceClassId(settings.value("deviceClassId").toString()), this); device->setName(settings.value("devicename").toString()); + device->setParentId(DeviceId(settings.value("parentid", QUuid()).toString())); ParamList params; settings.beginGroup("Params"); @@ -914,6 +927,9 @@ void DeviceManager::storeConfiguredDevices() settings.setValue("devicename", device->name()); settings.setValue("deviceClassId", device->deviceClassId().toString()); settings.setValue("pluginid", device->pluginId().toString()); + if (!device->parentId().isNull()) + settings.setValue("parentid", device->parentId().toString()); + settings.beginGroup("Params"); foreach (const Param ¶m, device->params()) { settings.setValue(param.name(), param.value()); diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index db7f098e..7057358f 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -82,6 +82,8 @@ public: DeviceErrorAuthentificationFailure, DeviceErrorAsync, DeviceErrorDeviceInUse, + DeviceErrorDeviceInRule, + DeviceErrorDeviceIsChild, DeviceErrorPairingTransactionIdNotFound, DeviceErrorParameterNotWritable }; @@ -119,7 +121,8 @@ public: DeviceError removeConfiguredDevice(const DeviceId &deviceId); Device* findConfiguredDevice(const DeviceId &id) const; - QList findConfiguredDevices(const DeviceClassId &deviceClassId) const; + QList findConfiguredDevices(const DeviceClassId &deviceClassId) const; + QList findChildDevices(Device *device) const; DeviceClass findDeviceClass(const DeviceClassId &deviceClassId) const; signals: diff --git a/libguh/network/upnpdiscovery/upnpdiscoveryrequest.cpp b/libguh/network/upnpdiscovery/upnpdiscoveryrequest.cpp index f717f53b..f9c5497d 100644 --- a/libguh/network/upnpdiscovery/upnpdiscoveryrequest.cpp +++ b/libguh/network/upnpdiscovery/upnpdiscoveryrequest.cpp @@ -45,7 +45,7 @@ void UpnpDiscoveryRequest::discover() m_upnpDiscovery->sendToMulticast(ssdpSearchMessage); qCDebug(dcHardware) << "--> UPnP discovery called."; - m_timer->start(3000); + m_timer->start(5000); } void UpnpDiscoveryRequest::addDeviceDescriptor(const UpnpDeviceDescriptor &deviceDescriptor) diff --git a/libguh/plugin/device.cpp b/libguh/plugin/device.cpp index 81f45383..0fcad5dd 100644 --- a/libguh/plugin/device.cpp +++ b/libguh/plugin/device.cpp @@ -154,7 +154,7 @@ bool Device::hasParam(const QString ¶mName) const return false; } -/*! Sets the \a states of this Device. It must match the \l{StateType} description in the associated \l{DeviceClass}. */ +DeviceId m_id; void Device::setStates(const QList &states) { m_states = states; @@ -211,6 +211,22 @@ State Device::state(const StateTypeId &stateTypeId) const return State(StateTypeId(), DeviceId()); } +/*! Returns the \l{DeviceId} of the parent Device from Device. If the parentId + is not set, this device is a parent device. +*/ +DeviceId Device::parentId() const +{ + return m_parentId; +} + +/*! Sets the \a parentId of this Device. If the parentId + is not set, this device is a parent device. +*/ +void Device::setParentId(const DeviceId &parentId) +{ + m_parentId = parentId; +} + /*! Returns true, if setup of this Device is already completed. */ bool Device::setupComplete() const { diff --git a/libguh/plugin/device.h b/libguh/plugin/device.h index 5f706d3a..29922a7d 100644 --- a/libguh/plugin/device.h +++ b/libguh/plugin/device.h @@ -32,7 +32,6 @@ #include #include - class Device: public QObject { Q_OBJECT @@ -63,6 +62,9 @@ public: State state(const StateTypeId &stateTypeId) const; + DeviceId parentId() const; + void setParentId(const DeviceId &parentId); + bool setupComplete() const; signals: @@ -77,6 +79,7 @@ private: private: DeviceId m_id; + DeviceId m_parentId; DeviceClassId m_deviceClassId; PluginId m_pluginId; QString m_name; diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp index 6f3a976c..995ef4f4 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp @@ -85,6 +85,7 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceC { Q_UNUSED(deviceClassId) Q_UNUSED(params) + upnpDiscover("libhue:idl"); return DeviceManager::DeviceErrorAsync; } @@ -164,10 +165,9 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev device->paramValue("model id").toString(), DeviceId(device->paramValue("bridge").toString()), this); - connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); } - + device->setParentId(device->paramValue("bridge").toString()); m_lights.insert(hueLight, device); setLightName(device, device->paramValue("name").toString()); } @@ -181,8 +181,6 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device) HueBridge *bridge = m_bridges.key(device); m_bridges.remove(bridge); bridge->deleteLater(); - - // TODO: remove lights from this bridge (over GuhCore) } if (device->deviceClassId() == hueLightDeviceClassId) { @@ -321,7 +319,6 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) return; } processSetNameResponse(device, reply->readAll()); - } reply->deleteLater(); diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json index c5dd75dc..aa17580d 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json @@ -14,7 +14,7 @@ "name": "Hue Bridge", "createMethods": ["discovery"], "setupMethod": "pushButton", - "pairingInfo": "Please press within 30 seconds the button on the Hue Bridge before you continue", + "pairingInfo": "Please press the button on the Hue Bridge within 30 seconds before you continue", "paramTypes": [ { "name": "name", diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 1e4cc426..bee35864 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -180,11 +180,44 @@ QList GuhCore::supportedDevices(const VendorId &vendorId) const } /*! Removes a configured \l{Device} with the given \a deviceId and \a removePolicyList. */ -DeviceManager::DeviceError GuhCore::removeConfiguredDevice(const DeviceId &deviceId, const QHash &removePolicyList) +QPair > GuhCore::removeConfiguredDevice(const DeviceId &deviceId, const QHash &removePolicyList) { + // Check if this is a child device + Device *device = findConfiguredDevice(deviceId); + if (!device->parentId().isNull()) { + qCWarning(dcDeviceManager) << "The device is a child of" << device->parentId().toString() << ". Please remove the parent device."; + return QPair > (DeviceManager::DeviceErrorDeviceIsChild, QList()); + } + + // Check if this device has child devices + QList devicesToRemove; + devicesToRemove.append(device); + QList childDevices = m_deviceManager->findChildDevices(device); + if (!childDevices.isEmpty()) { + foreach (Device *child, childDevices) { + devicesToRemove.append(child); + } + } + + // check devices + QList offendingRules; + qCDebug(dcDeviceManager) << "Devices to remove:"; + foreach (Device *d, devicesToRemove) { + qCDebug(dcDeviceManager) << " -> " << d->name() << d->id().toString(); + + // Check if device is in a rule + foreach (const RuleId &ruleId, m_ruleEngine->findRules(d->id())) { + qCDebug(dcDeviceManager) << " -> in rule:" << ruleId.toString(); + if (!offendingRules.contains(ruleId)) { + offendingRules.append(ruleId); + } + } + } + + // check each offending rule if there is a corresponding remove policy QHash toBeChanged; QList unhandledRules; - foreach (const RuleId &ruleId, m_ruleEngine->findRules(deviceId)) { + foreach (const RuleId &ruleId, offendingRules) { bool found = false; foreach (const RuleId &policyRuleId, removePolicyList.keys()) { if (ruleId == policyRuleId) { @@ -193,14 +226,14 @@ DeviceManager::DeviceError GuhCore::removeConfiguredDevice(const DeviceId &devic break; } } - if (!found) { + if (!found) unhandledRules.append(ruleId); - } + } if (!unhandledRules.isEmpty()) { - qCWarning(dcDeviceManager) << "There are unhandled rules which depend on this device."; - return DeviceManager::DeviceErrorDeviceInUse; + qCWarning(dcDeviceManager) << "There are unhandled rules which depend on this device:\n" << unhandledRules; + return QPair > (DeviceManager::DeviceErrorDeviceInRule, unhandledRules); } // Update the rules... @@ -208,13 +241,87 @@ DeviceManager::DeviceError GuhCore::removeConfiguredDevice(const DeviceId &devic if (toBeChanged.value(ruleId) == RuleEngine::RemovePolicyCascade) { m_ruleEngine->removeRule(ruleId); } else if (toBeChanged.value(ruleId) == RuleEngine::RemovePolicyUpdate){ - m_ruleEngine->removeDeviceFromRule(ruleId, deviceId); + foreach (Device *d, devicesToRemove) { + m_ruleEngine->removeDeviceFromRule(ruleId, d->id()); + } } } + // remove the child devices + foreach (Device *d, childDevices) { + DeviceManager::DeviceError removeError = m_deviceManager->removeConfiguredDevice(d->id()); + if (removeError == DeviceManager::DeviceErrorNoError) { + m_logger->removeDeviceLogs(d->id()); + } + } + + // delete the devices DeviceManager::DeviceError removeError = m_deviceManager->removeConfiguredDevice(deviceId); - if (removeError == DeviceManager::DeviceErrorNoError) + if (removeError == DeviceManager::DeviceErrorNoError) { m_logger->removeDeviceLogs(deviceId); + } + + return QPair > (DeviceManager::DeviceErrorNoError, QList()); +} + +DeviceManager::DeviceError GuhCore::removeConfiguredDevice(const DeviceId &deviceId, const RuleEngine::RemovePolicy &removePolicy) +{ + // Check if this is a child device + Device *device = findConfiguredDevice(deviceId); + if (!device->parentId().isNull()) { + qCWarning(dcDeviceManager) << "The device is a child of" << device->parentId().toString() << ". Please remove the parent device."; + return DeviceManager::DeviceErrorDeviceIsChild; + } + + // Check if this device has child devices + QList devicesToRemove; + devicesToRemove.append(device); + QList childDevices = m_deviceManager->findChildDevices(device); + if (!childDevices.isEmpty()) { + foreach (Device *child, childDevices) { + devicesToRemove.append(child); + } + } + + // check devices + QList offendingRules; + qCDebug(dcDeviceManager) << "Devices to remove:"; + foreach (Device *d, devicesToRemove) { + qCDebug(dcDeviceManager) << " -> " << d->name() << d->id().toString(); + + // Check if device is in a rule + foreach (const RuleId &ruleId, m_ruleEngine->findRules(d->id())) { + qCDebug(dcDeviceManager) << " -> in rule:" << ruleId.toString(); + if (!offendingRules.contains(ruleId)) { + offendingRules.append(ruleId); + } + } + } + + // apply removepolicy for foreach rule + foreach (const RuleId &ruleId, offendingRules) { + if (removePolicy == RuleEngine::RemovePolicyCascade) { + m_ruleEngine->removeRule(ruleId); + } else if (removePolicy == RuleEngine::RemovePolicyUpdate){ + foreach (Device *d, devicesToRemove) { + m_ruleEngine->removeDeviceFromRule(ruleId, d->id()); + } + } + } + + // remove the child devices + foreach (Device *d, childDevices) { + DeviceManager::DeviceError removeError = m_deviceManager->removeConfiguredDevice(d->id()); + if (removeError == DeviceManager::DeviceErrorNoError) { + m_logger->removeDeviceLogs(d->id()); + } + } + + // delete the devices + DeviceManager::DeviceError removeError = m_deviceManager->removeConfiguredDevice(deviceId); + if (removeError == DeviceManager::DeviceErrorNoError) { + m_logger->removeDeviceLogs(deviceId); + } return removeError; } diff --git a/server/guhcore.h b/server/guhcore.h index 1ee0c6f7..b1f06aa3 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -70,12 +70,13 @@ public: DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms); DeviceManager::DeviceError addConfiguredDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const DeviceId &newId); DeviceManager::DeviceError addConfiguredDevice(const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId, const DeviceId &newId); - QList configuredDevices() const; + QList configuredDevices() const; Device *findConfiguredDevice(const DeviceId &deviceId) const; - QList findConfiguredDevices(const DeviceClassId &deviceClassId) 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); + QPair >removeConfiguredDevice(const DeviceId &deviceId, const QHash &removePolicyList); + DeviceManager::DeviceError removeConfiguredDevice(const DeviceId &deviceId, const RuleEngine::RemovePolicy &removePolicy); DeviceManager::DeviceError pairDevice(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId); DeviceManager::DeviceError pairDevice(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms); @@ -120,7 +121,6 @@ signals: void ruleActiveChanged(const Rule &rule); void ruleConfigurationChanged(const Rule &rule); - private: RuleEngine *ruleEngine() const; diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index a607889d..5fc2af15 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -113,13 +113,13 @@ DeviceHandler::DeviceHandler(QObject *parent) : returns.clear(); // Reused params from above! setDescription("PairDevice", "Pair a device. " - "Use this for DeviceClasses with a setupMethod different than SetupMethodJustAdd." + "Use this for DeviceClasses with a setupMethod different than SetupMethodJustAdd. " "Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. " "CreateMethodJustAdd takes the parameters you want to have with that device. " "CreateMethodDiscovery requires the use of a deviceDescriptorId. " "If success is true, the return values will contain a pairingTransactionId, a displayMessage and " "the setupMethod. Depending on the setupMethod you should either proceed with AddConfiguredDevice " - " or PairDevice." + "or PairDevice." ); setParams("PairDevice", params); returns.insert("deviceError", JsonTypes::deviceErrorRef()); @@ -181,9 +181,11 @@ DeviceHandler::DeviceHandler(QObject *parent) : policy.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); policy.insert("policy", JsonTypes::removePolicyRef()); removePolicyList.append(policy); + params.insert("o:removePolicy", JsonTypes::removePolicyRef()); params.insert("o:removePolicyList", removePolicyList); setParams("RemoveConfiguredDevice", params); returns.insert("deviceError", JsonTypes::deviceErrorRef()); + returns.insert("o:ruleIds", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Uuid)); setReturns("RemoveConfiguredDevice", returns); params.clear(); returns.clear(); @@ -459,7 +461,17 @@ JsonReply *DeviceHandler::EditDevice(const QVariantMap ¶ms) JsonReply* DeviceHandler::RemoveConfiguredDevice(const QVariantMap ¶ms) { + QVariantMap returns; DeviceId deviceId = DeviceId(params.value("deviceId").toString()); + + // global removePolicy has priority + if (params.contains("removePolicy")) { + RuleEngine::RemovePolicy removePolicy = params.value("removePolicy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate; + DeviceManager::DeviceError status = GuhCore::instance()->removeConfiguredDevice(deviceId, removePolicy); + returns.insert("deviceError", JsonTypes::deviceErrorToString(status)); + return createReply(returns); + } + QHash removePolicyList; foreach (const QVariant &variant, params.value("removePolicyList").toList()) { RuleId ruleId = RuleId(variant.toMap().value("ruleId").toString()); @@ -467,9 +479,17 @@ JsonReply* DeviceHandler::RemoveConfiguredDevice(const QVariantMap ¶ms) removePolicyList.insert(ruleId, policy); } - QVariantMap returns; - DeviceManager::DeviceError status = GuhCore::instance()->removeConfiguredDevice(deviceId, removePolicyList); - returns.insert("deviceError", JsonTypes::deviceErrorToString(status)); + QPair > status = GuhCore::instance()->removeConfiguredDevice(deviceId, removePolicyList); + returns.insert("deviceError", JsonTypes::deviceErrorToString(status.first)); + + if (!status.second.isEmpty()) { + QVariantList ruleIdList; + foreach (const RuleId &ruleId, status.second) { + ruleIdList.append(ruleId.toString()); + } + returns.insert("ruleIds", ruleIdList); + } + return createReply(returns); } @@ -614,7 +634,6 @@ void DeviceHandler::deviceSetupFinished(Device *device, DeviceManager::DeviceErr } reply->setData(returns); reply->finished(); - } void DeviceHandler::deviceEditFinished(Device *device, DeviceManager::DeviceError status) diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 99d6edce..2ab58210 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -199,6 +199,7 @@ void JsonTypes::init() s_device.insert("name", basicTypeToString(String)); s_device.insert("params", QVariantList() << paramRef()); s_device.insert("setupComplete", basicTypeToString(Bool)); + s_device.insert("o:parentId", basicTypeToString(Uuid)); // DeviceDescription s_deviceDescriptor.insert("id", basicTypeToString(Uuid)); @@ -561,6 +562,10 @@ QVariantMap JsonTypes::packDevice(Device *device) foreach (const Param ¶m, device->params()) { params.append(packParam(param)); } + + if (!device->parentId().isNull()) + variant.insert("parentId", device->parentId()); + variant.insert("params", params); variant.insert("setupComplete", device->setupComplete()); return variant; diff --git a/server/rest/devicesresource.cpp b/server/rest/devicesresource.cpp index f49c0955..a9a06ca8 100644 --- a/server/rest/devicesresource.cpp +++ b/server/rest/devicesresource.cpp @@ -155,10 +155,19 @@ HttpReply *DevicesResource::proccessDeleteRequest(const HttpRequest &request, co return createErrorReply(HttpReply::BadRequest); // DELETE /api/v1/devices/{deviceId} - if (urlTokens.count() == 4) - return removeDevice(m_device); + if (urlTokens.count() == 4) { + QVariantMap params; + if (request.urlQuery().hasQueryItem("params")) { + QString paramMapString = request.urlQuery().queryItemValue("params"); - // TODO: /api/v1/devices/{deviceId}?ruleId={ruleId}&removePolicy={RemovePolicy} + QPair verification = verifyPayload(paramMapString.toUtf8()); + if (!verification.first) + return createErrorReply(HttpReply::BadRequest); + + params = verification.second.toMap(); + } + return removeDevice(m_device, params); + } return createErrorReply(HttpReply::NotImplemented); } @@ -232,19 +241,8 @@ HttpReply *DevicesResource::getConfiguredDevices() const { qCDebug(dcRest) << "Get all configured devices"; HttpReply *reply = createSuccessReply(); - QVariantList devices = JsonTypes::packConfiguredDevices(); - - QVariantList finalDevices; - foreach (const QVariant &deviceVariant, devices) { - QVariantMap deviceMap = deviceVariant.toMap(); - Device* device = GuhCore::instance()->findConfiguredDevice(DeviceId(deviceMap.value("id").toString())); - QVariantList deviceStates = JsonTypes::packDeviceStates(device); - deviceMap.insert("states", deviceStates); - finalDevices.append(deviceMap); - } - reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(finalDevices).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packConfiguredDevices()).toJson()); return reply; } @@ -252,14 +250,8 @@ HttpReply *DevicesResource::getConfiguredDevice(Device *device) const { qCDebug(dcRest) << "Get configured device with id:" << device->id().toString(); HttpReply *reply = createSuccessReply(); - QVariantMap deviceMap = JsonTypes::packDevice(device); - QVariantList deviceStates = JsonTypes::packDeviceStates(device); - deviceMap.insert("states", deviceStates); - - qCDebug(dcRest) << deviceMap; - reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(deviceMap).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDevice(device)).toJson()); return reply; } @@ -283,18 +275,49 @@ HttpReply *DevicesResource::getDeviceStateValue(Device *device, const StateTypeI return reply; } -HttpReply *DevicesResource::removeDevice(Device *device) const +HttpReply *DevicesResource::removeDevice(Device *device, const QVariantMap ¶ms) const { qCDebug(dcRest) << "Remove device with id:" << device->id().toString(); - DeviceManager::DeviceError result = GuhCore::instance()->removeConfiguredDevice(device->id(), QHash()); + qCDebug(dcRest) << QJsonDocument::fromVariant(params).toJson(); - // TODO: /api/v1/devices/{deviceId}?ruleId={ruleId}&removePolicy={RemovePolicy} + // global removePolicy has priority + if (params.contains("removePolicy")) { + RuleEngine::RemovePolicy removePolicy = params.value("removePolicy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate; + DeviceManager::DeviceError result = GuhCore::instance()->removeConfiguredDevice(device->id(), removePolicy); + return createDeviceErrorReply(HttpReply::Ok, result); + } - if (result == DeviceManager::DeviceErrorNoError) { - HttpReply *reply = createDeviceErrorReply(HttpReply::Ok, result); + QHash removePolicyList; + foreach (const QVariant &variant, params.value("removePolicyList").toList()) { + RuleId ruleId = RuleId(variant.toMap().value("ruleId").toString()); + RuleEngine::RemovePolicy policy = variant.toMap().value("policy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate; + removePolicyList.insert(ruleId, policy); + } + + QPair > status = GuhCore::instance()->removeConfiguredDevice(device->id(), removePolicyList); + + // if there are offending rules + if (!status.second.isEmpty()) { + QVariantList ruleIdList; + QVariantMap returns; + returns.insert("deviceError", JsonTypes::deviceErrorToString(status.first)); + + foreach (const RuleId &ruleId, status.second) { + ruleIdList.append(ruleId.toString()); + } + returns.insert("ruleIds", ruleIdList); + + HttpReply *reply = createErrorReply(HttpReply::BadRequest); + reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); + reply->setPayload(QJsonDocument::fromVariant(returns).toJson()); return reply; } - return createDeviceErrorReply(HttpReply::BadRequest, result); + + if (status.first == DeviceManager::DeviceErrorNoError) + return createDeviceErrorReply(HttpReply::Ok, status.first); + + return createDeviceErrorReply(HttpReply::BadRequest, status.first); + } HttpReply *DevicesResource::executeAction(Device *device, const ActionTypeId &actionTypeId, const QByteArray &payload) const diff --git a/server/rest/devicesresource.h b/server/rest/devicesresource.h index 5b12d112..b50985cd 100644 --- a/server/rest/devicesresource.h +++ b/server/rest/devicesresource.h @@ -64,7 +64,7 @@ private: HttpReply *getDeviceStateValue(Device *device, const StateTypeId &stateTypeId) const; // Delete methods - HttpReply *removeDevice(Device *device) const; + HttpReply *removeDevice(Device *device, const QVariantMap ¶ms) const; // Post methods HttpReply *executeAction(Device *device, const ActionTypeId &actionTypeId, const QByteArray &payload) const; diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp index d3d626b8..d0418872 100644 --- a/server/ruleengine.cpp +++ b/server/ruleengine.cpp @@ -551,9 +551,10 @@ QList RuleEngine::findRules(const DeviceId &deviceId) break; } } - if (!offending && rule.stateEvaluator().containsDevice(deviceId)) { + + if (!offending && rule.stateEvaluator().containsDevice(deviceId)) offending = true; - } + if (!offending) { foreach (const RuleAction &action, rule.actions()) { if (action.deviceId() == deviceId) { @@ -562,9 +563,19 @@ QList RuleEngine::findRules(const DeviceId &deviceId) } } } - if (offending) { - offendingRules.append(rule.id()); + + if (!offending) { + foreach (const RuleAction &action, rule.exitActions()) { + if (action.deviceId() == deviceId) { + offending = true; + break; + } + } } + + if (offending) + offendingRules.append(rule.id()); + } return offendingRules; } @@ -572,10 +583,12 @@ QList RuleEngine::findRules(const DeviceId &deviceId) /*! Removes a \l{Device} from a \l{Rule} with the given \a id and \a deviceId. */ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId) { - if (!m_rules.contains(id)) { + if (!m_rules.contains(id)) return; - } + Rule rule = m_rules.value(id); + + // remove device from eventDescriptors QList eventDescriptors = rule.eventDescriptors(); QList removeIndexes; for (int i = 0; i < eventDescriptors.count(); i++) { @@ -586,9 +599,12 @@ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId while (removeIndexes.count() > 0) { eventDescriptors.takeAt(removeIndexes.takeLast()); } + + // remove device from state evaluators StateEvaluator stateEvalatuator = rule.stateEvaluator(); stateEvalatuator.removeDevice(deviceId); + // remove device from actions QList actions = rule.actions(); for (int i = 0; i < actions.count(); i++) { if (actions.at(i).deviceId() == deviceId) { @@ -598,8 +614,30 @@ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId while (removeIndexes.count() > 0) { actions.takeAt(removeIndexes.takeLast()); } + + // remove device from exit actions + QList exitActions = rule.exitActions(); + for (int i = 0; i < exitActions.count(); i++) { + if (exitActions.at(i).deviceId() == deviceId) { + removeIndexes.append(i); + } + } + while (removeIndexes.count() > 0) { + exitActions.takeAt(removeIndexes.takeLast()); + } + + // remove the rule from settings + GuhSettings settings(GuhSettings::SettingsRoleRules); + settings.beginGroup(id.toString()); + settings.remove(""); + settings.endGroup(); + Rule newRule(id, rule.name(), eventDescriptors, stateEvalatuator, actions); m_rules[id] = newRule; + + // save it + saveRule(newRule); + emit ruleConfigurationChanged(newRule); } bool RuleEngine::containsEvent(const Rule &rule, const Event &event)