From 02a6e2cb961151d30b38d828f56b937e256881f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 1 Dec 2015 10:08:33 +0100 Subject: [PATCH] update plugins and add state desciptor validation --- guh.pri | 2 +- libguh/plugin/device.cpp | 9 ++- libguh/plugin/deviceplugin.cpp | 35 ++++---- .../deviceplugins/eq-3/deviceplugineq-3.json | 4 +- .../deviceplugingenericelements.json | 2 +- .../deviceplugins/kodi/devicepluginkodi.json | 9 +-- .../deviceplugins/mock/devicepluginmock.json | 24 +++--- .../philipshue/devicepluginphilipshue.json | 31 ++++---- .../deviceplugins/tune/deviceplugintune.json | 18 ++--- .../deviceplugins/wemo/devicepluginwemo.json | 2 +- plugins/guh-generateplugininfo | 17 ++-- server/jsonrpc/jsontypes.cpp | 16 ++-- server/jsonrpc/ruleshandler.cpp | 10 +++ server/rest/rulesresource.cpp | 4 + server/ruleengine.cpp | 21 +++-- server/ruleengine.h | 3 + server/stateevaluator.cpp | 79 +++++++++++++++++-- server/stateevaluator.h | 2 + tests/auto/api.json | 4 +- 19 files changed, 194 insertions(+), 98 deletions(-) diff --git a/guh.pri b/guh.pri index 7473c089..681a35e8 100644 --- a/guh.pri +++ b/guh.pri @@ -2,7 +2,7 @@ GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') # define protocol versions -JSON_PROTOCOL_VERSION=33 +JSON_PROTOCOL_VERSION=34 REST_API_VERSION=1 DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \ diff --git a/libguh/plugin/device.cpp b/libguh/plugin/device.cpp index 0fcad5dd..4bd35ba9 100644 --- a/libguh/plugin/device.cpp +++ b/libguh/plugin/device.cpp @@ -37,7 +37,6 @@ The \a value parameter describes the new value of the State. */ - #include "device.h" #include "types/event.h" #include "loggingcategories.h" @@ -187,9 +186,13 @@ void Device::setStateValue(const StateTypeId &stateTypeId, const QVariant &value { for (int i = 0; i < m_states.count(); ++i) { if (m_states.at(i).stateTypeId() == stateTypeId) { - if (m_states.at(i).value() == value) { + if (m_states.at(i).value() == value) return; - } + + + // TODO: check min/max value + possible values + // to prevent an invalid state type + State newState(stateTypeId, m_id); newState.setValue(value); m_states[i] = newState; diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index 229f9219..f1a12fa7 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -251,25 +251,30 @@ QList DevicePlugin::supportedDevices() const stateType.setType(t); stateType.setUnit(unitStringToUnit(st.value("unit").toString())); stateType.setDefaultValue(st.value("defaultValue").toVariant()); + if (st.contains("minValue")) + stateType.setMinValue(st.value("minValue").toVariant()); + + if (st.contains("maxValue")) + stateType.setMaxValue(st.value("maxValue").toVariant()); + + if (st.contains("possibleValues")) { + QVariantList possibleValues; + foreach (const QJsonValue &possibleValueJson, st.value("possibleValues").toArray()) { + possibleValues.append(possibleValueJson.toVariant()); + } + stateType.setPossibleValues(possibleValues); + } stateTypes.append(stateType); // create ActionType if this StateType is writable - if (st.contains("writable")) { - ActionType actionType(st.value("id").toString()); - actionType.setName("set " + st.value("name").toString()); + if (st.contains("writable") && st.value("writable").toBool()) { // Note: fields already checked in StateType - ParamType paramType(st.value("name").toString(), t, st.value("defaultValue").toVariant()); - if (st.value("writable").toObject().contains("allowedValues")) { - QVariantList allowedValues; - foreach (const QJsonValue &allowedTypesJson, st.value("writable").toObject().value("allowedValues").toArray()) { - allowedValues.append(allowedTypesJson.toVariant()); - } - paramType.setAllowedValues(allowedValues); - } - paramType.setInputType(inputTypeStringToInputType(st.value("writable").toObject().value("inputType").toString())); - paramType.setUnit(unitStringToUnit(st.value("unit").toString())); - paramType.setLimits(st.value("writable").toObject().value("minValue").toVariant(), - st.value("writable").toObject().value("maxValue").toVariant()); + ActionType actionType(ActionTypeId(stateType.id().toString())); + actionType.setName("set " + stateType.name()); + ParamType paramType(stateType.name(), t, stateType.defaultValue()); + paramType.setAllowedValues(stateType.possibleValues()); + paramType.setUnit(stateType.unit()); + paramType.setLimits(stateType.minValue(), stateType.maxValue()); actionType.setParamTypes(QList() << paramType); actionTypes.append(actionType); } diff --git a/plugins/deviceplugins/eq-3/deviceplugineq-3.json b/plugins/deviceplugins/eq-3/deviceplugineq-3.json index a92c9616..8805ac3b 100644 --- a/plugins/deviceplugins/eq-3/deviceplugineq-3.json +++ b/plugins/deviceplugins/eq-3/deviceplugineq-3.json @@ -212,7 +212,7 @@ "type": "double", "unit": "DegreeCelsius", "defaultValue": 0, - "writable": {} + "writable": true }, { @@ -375,7 +375,7 @@ "unit": "DegreeCelsius", "type": "double", "defaultValue": 0, - "writable": {} + "writable": true }, { "id": "576da571-9a65-478f-96bf-19256c8b9ece", diff --git a/plugins/deviceplugins/genericelements/deviceplugingenericelements.json b/plugins/deviceplugins/genericelements/deviceplugingenericelements.json index f3f9c6af..4d0dd533 100644 --- a/plugins/deviceplugins/genericelements/deviceplugingenericelements.json +++ b/plugins/deviceplugins/genericelements/deviceplugingenericelements.json @@ -27,7 +27,7 @@ "name": "state", "type": "bool", "defaultValue": false, - "writable": {} + "writable": true } ] }, diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.json b/plugins/deviceplugins/kodi/devicepluginkodi.json index 1dce9cf0..744831c1 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.json +++ b/plugins/deviceplugins/kodi/devicepluginkodi.json @@ -41,7 +41,7 @@ "idName": "mute", "name": "mute", "type": "bool", - "writable": {} + "writable": true }, { "id": "9dfe5d78-4c3f-497c-bab1-bb9fdf7e93a9", @@ -49,10 +49,9 @@ "name": "volume", "unit": "Percentage", "type": "int", - "writable": { - "minValue": 0, - "maxValue": 100 - } + "minValue": 0, + "maxValue": 100, + "writable": true } ], "eventTypes": [ diff --git a/plugins/deviceplugins/mock/devicepluginmock.json b/plugins/deviceplugins/mock/devicepluginmock.json index e77f5060..f8ff02e4 100644 --- a/plugins/deviceplugins/mock/devicepluginmock.json +++ b/plugins/deviceplugins/mock/devicepluginmock.json @@ -234,7 +234,7 @@ "name": "color", "type": "QColor", "defaultValue": "#000000", - "writable": {} + "writable": true }, { "id": "72981c04-267a-4ba0-a59e-9921d2f3af9c", @@ -243,10 +243,9 @@ "type": "int", "unit": "Percentage", "defaultValue": 0, - "writable": { - "minValue": 0, - "maxValue": 100 - } + "minValue": 0, + "maxValue": 100, + "writable": true }, { "id": "05f63f9c-f61e-4dcf-ad55-3f13fde2765b", @@ -254,14 +253,13 @@ "name": "allowed values", "type": "QString", "defaultValue": "String value 1", - "writable": { - "allowedValues": [ - "String value 1", - "String value 2", - "String value 3", - "String value 4" - ] - } + "possibleValues": [ + "String value 1", + "String value 2", + "String value 3", + "String value 4" + ], + "writable": true } ], "actionTypes": [ diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json index aa17580d..e25149c8 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json @@ -116,7 +116,7 @@ "name": "power", "type": "bool", "defaultValue": false, - "writable": {} + "writable": true }, { "id": "c0f4206f-f219-4f06-93c4-4ca515a56f79", @@ -125,10 +125,9 @@ "type": "int", "unit": "Mired", "defaultValue": 170, - "writable": { - "minValue": 153, - "maxValue": 500 - } + "minValue": 153, + "maxValue": 500, + "writable": true }, { "id": "d25423e7-b924-4b20-80b6-77eecc65d089", @@ -136,7 +135,7 @@ "name": "color", "type": "QColor", "defaultValue": "#000000", - "writable": {} + "writable": true }, { @@ -146,10 +145,10 @@ "type": "int", "unit": "Percentage", "defaultValue": 0, - "writable": { - "minValue": 0, - "maxValue": 100 - } + "minValue": 0, + "maxValue": 100, + "writable": true + }, { "id": "0b7cdd8d-4db8-4183-abe2-f3c01d1c9afc", @@ -157,13 +156,11 @@ "name": "effect", "type": "QString", "defaultValue": "none", - "writable": { - "allowedValues": [ - "none", - "color loop" - ] - } - + "possibleValues": [ + "none", + "color loop" + ], + "writable": true } ], "actionTypes": [ diff --git a/plugins/deviceplugins/tune/deviceplugintune.json b/plugins/deviceplugins/tune/deviceplugintune.json index 0960eedf..1f2ae0fd 100644 --- a/plugins/deviceplugins/tune/deviceplugintune.json +++ b/plugins/deviceplugins/tune/deviceplugintune.json @@ -55,7 +55,7 @@ "name": "active", "type": "bool", "defaultValue": false, - "writable": {} + "writable": true }, { "id": "cb8a89c2-dc12-4965-b047-57896058b421", @@ -64,10 +64,9 @@ "type": "int", "unit": "Percentage", "defaultValue": 0, - "writable": { - "minValue": 0, - "maxValue": 100 - } + "minValue": 0, + "maxValue": 100, + "writable": true } ] }, @@ -124,7 +123,7 @@ "name": "power", "type": "bool", "defaultValue": 0, - "writable": {} + "writable": true }, { "id": "677cd9ec-c264-47ee-9d2e-d3662237792c", @@ -133,10 +132,9 @@ "type": "int", "unit": "Percentage", "defaultValue": 0, - "writable": { - "minValue": 0, - "maxValue": 100 - } + "minValue": 0, + "maxValue": 100, + "writable": true } ] } diff --git a/plugins/deviceplugins/wemo/devicepluginwemo.json b/plugins/deviceplugins/wemo/devicepluginwemo.json index 71a35cd7..28699d9a 100644 --- a/plugins/deviceplugins/wemo/devicepluginwemo.json +++ b/plugins/deviceplugins/wemo/devicepluginwemo.json @@ -41,7 +41,7 @@ "name": "power", "type": "bool", "defaultValue": false, - "writable": {} + "writable": true }, { diff --git a/plugins/guh-generateplugininfo b/plugins/guh-generateplugininfo index e9d3c897..ac17ddc7 100755 --- a/plugins/guh-generateplugininfo +++ b/plugins/guh-generateplugininfo @@ -104,14 +104,15 @@ def extractStateTypes(deviceClassMap): print("duplicated variable name \"%s\" for StateTypeId %s -> skipping") % (variableName, stateType["id"]) # create ActionTypeId if the state is writable if 'writable' in stateType: - vName = "%sActionTypeId" % (stateType["idName"]) - if not vName in variableNames: - variableNames.append(vName) - print("define ActionTypeId %s for writable StateType %s = %s" % (vName, variableName, stateType["id"])) - writePluginInfo("ActionTypeId %s = ActionTypeId(\"%s\");" % (vName, stateType["id"])) - createExternDefinition("ActionTypeId", vName) - else: - print("duplicated variable name \"%s\" for ActionTypeId %s -> skipping") % (variableName, stateType["id"]) + if stateType['writable']: + vName = "%sActionTypeId" % (stateType["idName"]) + if not vName in variableNames: + variableNames.append(vName) + print("define ActionTypeId %s for writable StateType %s = %s" % (vName, variableName, stateType["id"])) + writePluginInfo("ActionTypeId %s = ActionTypeId(\"%s\");" % (vName, stateType["id"])) + createExternDefinition("ActionTypeId", vName) + else: + print("duplicated variable name \"%s\" for ActionTypeId %s -> skipping") % (variableName, stateType["id"]) except: pass except: diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 8ab1846d..7bcbf218 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -227,7 +227,6 @@ void JsonTypes::init() s_ruleDescription.insert("active", basicTypeToString(Bool)); s_ruleDescription.insert("executable", basicTypeToString(Bool)); - // LogEntry s_logEntry.insert("timestamp", basicTypeToString(Int)); s_logEntry.insert("loggingLevel", loggingLevelRef()); @@ -411,9 +410,17 @@ QVariantMap JsonTypes::packStateType(const StateType &stateType) variantMap.insert("type", basicTypeToString(stateType.type())); variantMap.insert("defaultValue", stateType.defaultValue()); - if(stateType.unit() != Types::UnitNone) { + if (stateType.maxValue().isValid()) + variantMap.insert("maxValue", stateType.maxValue()); + + if (stateType.minValue().isValid()) + variantMap.insert("minValue", stateType.minValue()); + + if (!stateType.possibleValues().isEmpty()) + variantMap.insert("possibleValues", stateType.possibleValues()); + + if(stateType.unit() != Types::UnitNone) variantMap.insert("unit", s_unit.at(stateType.unit())); - } return variantMap; } @@ -851,9 +858,8 @@ ParamList JsonTypes::unpackParams(const QVariantList ¶mList) RuleActionParam JsonTypes::unpackRuleActionParam(const QVariantMap &ruleActionParamMap) { - if (ruleActionParamMap.keys().count() == 0) { + if (ruleActionParamMap.keys().count() == 0) return RuleActionParam(); - } QString name = ruleActionParamMap.value("name").toString(); QVariant value = ruleActionParamMap.value("value"); diff --git a/server/jsonrpc/ruleshandler.cpp b/server/jsonrpc/ruleshandler.cpp index 10372c0d..c6619e22 100644 --- a/server/jsonrpc/ruleshandler.cpp +++ b/server/jsonrpc/ruleshandler.cpp @@ -222,6 +222,11 @@ JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) // Check and unpack stateEvaluator qCDebug(dcJsonRpc) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); + if (!stateEvaluator.isValid()) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidStateEvaluatorValue)); + return createReply(returns); + } // Check and unpack actions QPair, RuleEngine::RuleError> actionsVerification = JsonTypes::verifyActions(params, eventDescriptorList); @@ -277,6 +282,11 @@ JsonReply *RulesHandler::EditRule(const QVariantMap ¶ms) // Check and unpack stateEvaluator qCDebug(dcJsonRpc) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); + if (!stateEvaluator.isValid()) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidStateEvaluatorValue)); + return createReply(returns); + } // Check and unpack actions QPair, RuleEngine::RuleError> actionsVerification = JsonTypes::verifyActions(params, eventDescriptorList); diff --git a/server/rest/rulesresource.cpp b/server/rest/rulesresource.cpp index a7a318a7..7fe495a0 100644 --- a/server/rest/rulesresource.cpp +++ b/server/rest/rulesresource.cpp @@ -259,6 +259,8 @@ HttpReply *RulesResource::addRule(const QByteArray &payload) const // Check and unpack stateEvaluator qCDebug(dcRest) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); + if (!stateEvaluator.isValid()) + return createRuleErrorReply(HttpReply::BadRequest, RuleEngine::RuleErrorInvalidStateEvaluatorValue); // Check and unpack actions QPair, RuleEngine::RuleError> actionsVerification = JsonTypes::verifyActions(params, eventDescriptorList); @@ -362,6 +364,8 @@ HttpReply *RulesResource::editRule(const RuleId &ruleId, const QByteArray &paylo // Check and unpack stateEvaluator qCDebug(dcRest) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); + if (!stateEvaluator.isValid()) + return createRuleErrorReply(HttpReply::BadRequest, RuleEngine::RuleErrorInvalidStateEvaluatorValue); // Check and unpack actions QPair, RuleEngine::RuleError> actionsVerification = JsonTypes::verifyActions(params, eventDescriptorList); diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp index 2ec374b6..440d042b 100644 --- a/server/ruleengine.cpp +++ b/server/ruleengine.cpp @@ -104,7 +104,7 @@ RuleEngine::RuleEngine(QObject *parent) : QObject(parent) { GuhSettings settings(GuhSettings::SettingsRoleRules); - qCDebug(dcRuleEngine) << "laoding rules from" << settings.fileName(); + qCDebug(dcRuleEngine) << "loading rules from" << settings.fileName(); foreach (const QString &idString, settings.childGroups()) { settings.beginGroup(idString); @@ -217,9 +217,8 @@ QList RuleEngine::evaluateEvent(const Event &event) QList rules; foreach (const RuleId &id, m_ruleIds) { Rule rule = m_rules.value(id); - if (!rule.enabled()) { + if (!rule.enabled()) continue; - } if (rule.eventDescriptors().isEmpty()) { // This rule seems to have only states, check on state changed @@ -269,9 +268,9 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n */ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions, bool enabled, bool executable, bool fromEdit) { - if (ruleId.isNull()) { + if (ruleId.isNull()) return RuleErrorInvalidRuleId; - } + if (!findRule(ruleId).id().isNull()) { qCWarning(dcRuleEngine) << "Already have a rule with this id!"; return RuleErrorInvalidRuleId; @@ -298,6 +297,7 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n } + foreach (const RuleAction &action, actions) { Device *device = GuhCore::instance()->findConfiguredDevice(action.deviceId()); if (!device) { @@ -322,11 +322,10 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n } } } + } - } - if (actions.count() > 0) { + if (actions.count() > 0) qCDebug(dcRuleEngine) << "actions" << actions.last().actionTypeId() << actions.last().ruleActionParams(); - } foreach (const RuleAction &action, exitActions) { Device *device = GuhCore::instance()->findConfiguredDevice(action.deviceId()); @@ -351,9 +350,9 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n } } } - if (exitActions.count() > 0) { + + if (exitActions.count() > 0) qCDebug(dcRuleEngine) << "exit actions" << exitActions.last().actionTypeId() << exitActions.last().ruleActionParams(); - } Rule rule = Rule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions); rule.setEnabled(enabled); @@ -475,6 +474,7 @@ RuleEngine::RuleError RuleEngine::disableRule(const RuleId &ruleId) qCWarning(dcRuleEngine) << "Rule not found. Can't disable it"; return RuleErrorRuleNotFound; } + Rule rule = m_rules.value(ruleId); rule.setEnabled(false); m_rules[ruleId] = rule; @@ -538,7 +538,6 @@ RuleEngine::RuleError RuleEngine::executeExitActions(const RuleId &ruleId) return RuleErrorNoError; } - /*! Returns the \l{Rule} with the given \a ruleId. If the \l{Rule} does not exist, it will return \l{Rule::Rule()} */ Rule RuleEngine::findRule(const RuleId &ruleId) { diff --git a/server/ruleengine.h b/server/ruleengine.h index 5c7a3dc1..746172b9 100644 --- a/server/ruleengine.h +++ b/server/ruleengine.h @@ -24,6 +24,7 @@ #include "rule.h" #include "types/event.h" +#include "plugin/deviceclass.h" #include "stateevaluator.h" #include @@ -44,11 +45,13 @@ public: RuleErrorRuleNotFound, RuleErrorDeviceNotFound, RuleErrorEventTypeNotFound, + RuleErrorStateTypeNotFound, RuleErrorActionTypeNotFound, RuleErrorInvalidParameter, RuleErrorInvalidRuleFormat, RuleErrorMissingParameter, RuleErrorInvalidRuleActionParameter, + RuleErrorInvalidStateEvaluatorValue, RuleErrorTypesNotMatching, RuleErrorNotExecutable, RuleErrorContainsEventBasesAction, diff --git a/server/stateevaluator.cpp b/server/stateevaluator.cpp index 01b1c5d9..235b9187 100644 --- a/server/stateevaluator.cpp +++ b/server/stateevaluator.cpp @@ -47,7 +47,6 @@ StateEvaluator::StateEvaluator(const StateDescriptor &stateDescriptor): m_stateDescriptor(stateDescriptor), m_operatorType(Types::StateOperatorAnd) { - } /*! Constructs a new StateEvaluator for the given \a childEvaluators and \a stateOperator. */ @@ -136,9 +135,9 @@ bool StateEvaluator::evaluate() const /*! Returns true if this \l StateEvaluator has a \l Device in it with the given \a deviceId. */ bool StateEvaluator::containsDevice(const DeviceId &deviceId) const { - if (m_stateDescriptor.deviceId() == deviceId) { + if (m_stateDescriptor.deviceId() == deviceId) return true; - } + foreach (const StateEvaluator &childEvaluator, m_childEvaluators) { if (childEvaluator.containsDevice(deviceId)) { return true; @@ -150,9 +149,9 @@ bool StateEvaluator::containsDevice(const DeviceId &deviceId) const /*! Removes a \l Device with the given \a deviceId from this \l StateEvaluator. */ void StateEvaluator::removeDevice(const DeviceId &deviceId) { - if (m_stateDescriptor.deviceId() == deviceId) { + if (m_stateDescriptor.deviceId() == deviceId) m_stateDescriptor = StateDescriptor(); - } + for (int i = 0; i < m_childEvaluators.count(); i++) { m_childEvaluators[i].removeDevice(deviceId); } @@ -207,4 +206,74 @@ StateEvaluator StateEvaluator::loadFromSettings(GuhSettings &settings, const QSt return ret; } +/*! Returns true, if all child evaluators are valid, the devices exist and all descriptors are in allowed paramerters.*/ +bool StateEvaluator::isValid() const +{ + if (m_stateDescriptor.isValid()) { + Device *device = GuhCore::instance()->findConfiguredDevice(m_stateDescriptor.deviceId()); + if (!device) { + qCWarning(dcRuleEngine) << "State evaluator device does not exist!"; + return false; + } + + if (!device->hasState(m_stateDescriptor.stateTypeId())) { + qCWarning(dcRuleEngine) << "State evaluator device found, but it does not appear to have such a state!"; + return false; + } + + DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId()); + foreach (const StateType &stateType, deviceClass.stateTypes()) { + if (stateType.id() == m_stateDescriptor.stateTypeId()) { + + if (!m_stateDescriptor.stateValue().canConvert(stateType.type())) { + qCWarning(dcRuleEngine) << "Wrong state value for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Expected:" << QVariant::typeToName(stateType.type()); + return false; + } + + if (!m_stateDescriptor.stateValue().convert(stateType.type())) { + qCWarning(dcRuleEngine) << "Could not convert value of state descriptor" << m_stateDescriptor.stateTypeId() << " to:" << QVariant::typeToName(stateType.type()) << " Got:" << m_stateDescriptor.stateValue(); + return false; + } + + if (stateType.maxValue().isValid() && m_stateDescriptor.stateValue() > stateType.maxValue()) { + qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Max:" << stateType.maxValue(); + return false; + } + + if (stateType.minValue().isValid() && m_stateDescriptor.stateValue() < stateType.minValue()) { + qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Min:" << stateType.minValue(); + return false; + } + + if (!stateType.possibleValues().isEmpty() && !stateType.possibleValues().contains(m_stateDescriptor.stateValue())) { + QStringList possibleValues; + foreach (const QVariant &value, stateType.possibleValues()) { + possibleValues.append(value.toString()); + } + + qCWarning(dcRuleEngine) << "Value not in possible values for state type" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Possible values:" << possibleValues.join(", "); + return false; + } + } + } + + } + + if (m_operatorType == Types::StateOperatorOr) { + foreach (const StateEvaluator &stateEvaluator, m_childEvaluators) { + if (stateEvaluator.isValid()) { + return true; + } + } + return false; + } + + foreach (const StateEvaluator &stateEvaluator, m_childEvaluators) { + if (!stateEvaluator.isValid()) { + return false; + } + } + return true; +} + } diff --git a/server/stateevaluator.h b/server/stateevaluator.h index 0b50ee2c..c60310f2 100644 --- a/server/stateevaluator.h +++ b/server/stateevaluator.h @@ -54,6 +54,8 @@ public: void dumpToSettings(GuhSettings &settings, const QString &groupName) const; static StateEvaluator loadFromSettings(GuhSettings &settings, const QString &groupPrefix); + bool isValid() const; + private: StateDescriptor m_stateDescriptor; diff --git a/tests/auto/api.json b/tests/auto/api.json index 25da2a69..3b2d42fd 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -33 +34 { "methods": { "Actions.ExecuteAction": { @@ -756,11 +756,13 @@ "RuleErrorRuleNotFound", "RuleErrorDeviceNotFound", "RuleErrorEventTypeNotFound", + "RuleErrorStateTypeNotFound", "RuleErrorActionTypeNotFound", "RuleErrorInvalidParameter", "RuleErrorInvalidRuleFormat", "RuleErrorMissingParameter", "RuleErrorInvalidRuleActionParameter", + "RuleErrorInvalidStateEvaluatorValue", "RuleErrorTypesNotMatching", "RuleErrorNotExecutable", "RuleErrorContainsEventBasesAction",