diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index df7fe8ee..ebfce796 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -767,6 +767,8 @@ void NymeaCore::onDeviceDisappeared(const DeviceId &deviceId) void NymeaCore::deviceManagerLoaded() { + m_ruleEngine->initRuleStates(); + // Do some houskeeping... qCDebug(dcApplication()) << "Starting housekeeping..."; QDateTime startTime = QDateTime::currentDateTime(); diff --git a/libnymea-core/ruleengine.cpp b/libnymea-core/ruleengine.cpp index 39119351..41a25cb1 100644 --- a/libnymea-core/ruleengine.cpp +++ b/libnymea-core/ruleengine.cpp @@ -396,7 +396,6 @@ RuleEngine::RuleEngine(QObject *parent) : rule.setExitActions(exitActions); rule.setEnabled(enabled); rule.setExecutable(executable); - rule.setStatesActive(rule.stateEvaluator().evaluate()); appendRule(rule); settings.endGroup(); } @@ -467,7 +466,7 @@ QList RuleEngine::evaluateEvent(const Event &event) qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "and all states match."; rules.append(rule); } else { - qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "but state are not matching."; + qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "and states are not matching."; rules.append(rule); } } @@ -1515,4 +1514,11 @@ void RuleEngine::saveRule(const Rule &rule) qCDebug(dcRuleEngineDebug()) << "Saved rule to config:" << rule; } +void RuleEngine::initRuleStates() +{ + foreach (const RuleId &ruleId, m_rules.keys()) { + m_rules[ruleId].setStatesActive(m_rules[ruleId].stateEvaluator().evaluate()); + } +} + } diff --git a/libnymea-core/ruleengine.h b/libnymea-core/ruleengine.h index 8f7b7259..d414d4ae 100644 --- a/libnymea-core/ruleengine.h +++ b/libnymea-core/ruleengine.h @@ -68,8 +68,9 @@ public: RemovePolicyUpdate }; - explicit RuleEngine(QObject *parent = 0); + explicit RuleEngine(QObject *parent = nullptr); ~RuleEngine(); + void initRuleStates(); QList evaluateEvent(const Event &event); QList evaluateTime(const QDateTime &dateTime); diff --git a/libnymea/devicemanager.cpp b/libnymea/devicemanager.cpp index 017780e8..b1c9fa8f 100644 --- a/libnymea/devicemanager.cpp +++ b/libnymea/devicemanager.cpp @@ -802,7 +802,7 @@ Device *DeviceManager::findConfiguredDevice(const DeviceId &id) const return device; } } - return 0; + return nullptr; } /*! Returns all configured \{Device}{Devices} in the system. */ diff --git a/plugins/mock/devicepluginmock.cpp b/plugins/mock/devicepluginmock.cpp index 88ae9ad8..bd48cb88 100644 --- a/plugins/mock/devicepluginmock.cpp +++ b/plugins/mock/devicepluginmock.cpp @@ -233,7 +233,13 @@ DeviceManager::DeviceError DevicePluginMock::executeAction(Device *device, const if (action.actionTypeId() == mockMockFailingActionTypeId) return DeviceManager::DeviceErrorSetupFailed; + if (action.actionTypeId() == mockPowerActionTypeId) { + qCDebug(dcMockDevice()) << "Setting power to" << action.param(mockPowerActionPowerParamTypeId).value().toBool(); + device->setStateValue(mockPowerStateTypeId, action.param(mockPowerActionPowerParamTypeId).value().toBool()); + return DeviceManager::DeviceErrorNoError; + } m_daemons.value(device)->actionExecuted(action.actionTypeId()); + return DeviceManager::DeviceErrorNoError; } else if (device->deviceClassId() == mockDeviceAutoDeviceClassId) { if (action.actionTypeId() == mockDeviceAutoMockActionAsyncActionTypeId || action.actionTypeId() == mockDeviceAutoMockActionAsyncBrokenActionTypeId) { diff --git a/tests/auto/nymeatestbase.cpp b/tests/auto/nymeatestbase.cpp index d91e01dd..95e39265 100644 --- a/tests/auto/nymeatestbase.cpp +++ b/tests/auto/nymeatestbase.cpp @@ -44,6 +44,7 @@ StateTypeId mockIntStateId = StateTypeId("80baec19-54de-4948-ac46-31eabfaceb83") StateTypeId mockBoolStateId = StateTypeId("9dd6a97c-dfd1-43dc-acbd-367932742310"); StateTypeId mockDoubleStateId = StateTypeId("7cac53ee-7048-4dc9-b000-7b585390f34c"); StateTypeId mockBatteryCriticalStateId = StateTypeId("580bc611-1a55-41f3-996f-8d3ccf543db3"); +StateTypeId mockPowerStateTypeId = StateTypeId("064aed0d-da4c-49d4-b236-60f97e98ff84"); ActionTypeId mockActionIdPower = ActionTypeId("064aed0d-da4c-49d4-b236-60f97e98ff84"); ActionTypeId mockActionIdWithParams = ActionTypeId("dea0f4e1-65e3-4981-8eaa-2701c53a9185"); ActionTypeId mockActionIdNoParams = ActionTypeId("defd3ed6-1a0d-400b-8879-a0202cf39935"); diff --git a/tests/auto/nymeatestbase.h b/tests/auto/nymeatestbase.h index 28c5540e..fbcf587a 100644 --- a/tests/auto/nymeatestbase.h +++ b/tests/auto/nymeatestbase.h @@ -51,6 +51,7 @@ extern StateTypeId mockIntStateId; extern StateTypeId mockDoubleStateId; extern StateTypeId mockBatteryCriticalStateId; extern StateTypeId mockBoolStateId; +extern StateTypeId mockPowerStateTypeId; // ParamTypes from mock devices extern ParamTypeId configParamIntParamTypeId; diff --git a/tests/auto/rules/testrules.cpp b/tests/auto/rules/testrules.cpp index f760d8a6..1a3b7e93 100644 --- a/tests/auto/rules/testrules.cpp +++ b/tests/auto/rules/testrules.cpp @@ -46,8 +46,12 @@ private: QVariant validIntStateBasedRule(const QString &name, const bool &executable, const bool &enabled); + void generateEvent(const EventTypeId &eventTypeId); + private slots: + void initTestCase(); + void cleanup(); void emptyRule(); void getInvalidRule(); @@ -101,6 +105,8 @@ private slots: void testRuleActionPAramsFromEventParameter_data(); void testRuleActionPAramsFromEventParameter(); + void testInitStatesActive(); + void testInterfaceBasedEventRule(); void testInterfaceBasedStateRule(); @@ -349,6 +355,27 @@ QVariant TestRules::validIntStateBasedRule(const QString &name, const bool &exec return params; } +void TestRules::generateEvent(const EventTypeId &eventTypeId) +{ + // Trigger an event + QNetworkAccessManager nam; + QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); + + // trigger event in mock device + QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(eventTypeId.toString()))); + QNetworkReply *reply = nam.get(request); + spy.wait(); + QCOMPARE(spy.count(), 1); + reply->deleteLater(); + +} + +void TestRules::initTestCase() +{ + NymeaTestBase::initTestCase(); + QLoggingCategory::setFilterRules("*.debug=false\nRuleEngine.debug=true\nRuleEngineDebug.debug=true\nMockDevice.*=true"); +} + void TestRules::addRemoveRules_data() { // RuleAction @@ -2525,6 +2552,101 @@ void TestRules::testRuleActionPAramsFromEventParameter() verifyRuleError(response, error); } +void TestRules::testInitStatesActive() +{ + + // Create a rule to toggle the power state on event 1 + QVariantMap params; + params.insert("name", "testrule"); + + QVariantMap eventDescriptor; + eventDescriptor.insert("deviceId", m_mockDeviceId); + eventDescriptor.insert("eventTypeId", mockEvent1Id); + QVariantList eventDescriptors; + eventDescriptors.append(eventDescriptor); + params.insert("eventDescriptors", eventDescriptors); + + QVariantMap stateEvaluator; + QVariantMap stateDescriptor; + stateDescriptor.insert("stateTypeId", mockPowerStateTypeId); + stateDescriptor.insert("operator", "ValueOperatorEquals"); + stateDescriptor.insert("value", false); + stateDescriptor.insert("deviceId", m_mockDeviceId); + stateEvaluator.insert("stateDescriptor", stateDescriptor); + params.insert("stateEvaluator", stateEvaluator); + + QVariantList actions; + QVariantMap action; + action.insert("actionTypeId", mockActionIdPower); + action.insert("deviceId", m_mockDeviceId); + QVariantList actionParams; + QVariantMap actionParam; + actionParam.insert("paramTypeId", mockActionIdPower); + actionParam.insert("value", true); + actionParams.append(actionParam); + action.insert("ruleActionParams", actionParams); + actions.append(action); + params.insert("actions", actions); + + QVariantList exitActions; + QVariantMap exitAction; + exitAction.insert("actionTypeId", mockActionIdPower); + exitAction.insert("deviceId", m_mockDeviceId); + QVariantList exitActionParams; + QVariantMap exitActionParam; + exitActionParam.insert("paramTypeId", mockActionIdPower); + exitActionParam.insert("value", false); + exitActionParams.append(exitActionParam); + exitAction.insert("ruleActionParams", exitActionParams); + exitActions.append(exitAction); + params.insert("exitActions", exitActions); + + QVariant response = injectAndWait("Rules.AddRule", params); + RuleId ruleId = RuleId::fromUuid(response.toMap().value("params").toMap().value("ruleId").toUuid()); + QVERIFY2(!ruleId.isNull(), "Error adding rule"); + + // Get the current state value, make sure it's false + params.clear(); + params.insert("deviceId", m_mockDeviceId); + params.insert("stateTypeId", mockPowerStateTypeId); + + response = injectAndWait("Devices.GetStateValue", params); + QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == false, "State initially true while it should be false"); + + + // Trigger the event + generateEvent(mockEvent1Id); + + + // Make sure the value changed after the event has triggered + response = injectAndWait("Devices.GetStateValue", params); + QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == true, "State is false while it should have changed to true"); + + + // Trigger the event again... + generateEvent(mockEvent1Id); + + + // ... and make sure the value changed back to false + response = injectAndWait("Devices.GetStateValue", params); + QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == false, "State is true while it should have changed to false"); + + restartServer(); + + // Make sure the value changed is still false + response = injectAndWait("Devices.GetStateValue", params); + QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == false, "State is true while it should have changed to false"); + + // Trigger the event + generateEvent(mockEvent1Id); + + + // Make sure the value changed after the event has triggered + response = injectAndWait("Devices.GetStateValue", params); + QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == true, "State is false while it should have changed to true"); + +} + void TestRules::testInterfaceBasedEventRule() { QNetworkAccessManager nam;