From 5f1855ff44696acc0123d3d47e6055b2295d2936 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 21 Sep 2020 00:13:55 +0200 Subject: [PATCH 01/12] Don't log all states by default any more --- libnymea-core/nymeacore.cpp | 12 +++++++++++- libnymea/integrations/thingutils.cpp | 3 +++ libnymea/interfaces/battery.json | 3 ++- libnymea/interfaces/button.json | 3 ++- libnymea/interfaces/closablesensor.json | 3 ++- libnymea/interfaces/co2sensor.json | 3 ++- libnymea/interfaces/conductivitysensor.json | 3 ++- libnymea/interfaces/connectable.json | 3 ++- libnymea/interfaces/daylightsensor.json | 3 ++- libnymea/interfaces/doorbell.json | 3 ++- libnymea/interfaces/extendedclosable.json | 3 ++- libnymea/interfaces/extendedsmartmeterconsumer.json | 3 ++- libnymea/interfaces/extendedsmartmeterproducer.json | 4 +++- libnymea/interfaces/fingerprintreader.json | 3 ++- libnymea/interfaces/garagegate.json | 3 ++- libnymea/interfaces/humiditysensor.json | 3 ++- libnymea/interfaces/impulsegaragedoor.json | 3 ++- libnymea/interfaces/inputtrigger.json | 3 ++- libnymea/interfaces/lightsensor.json | 3 ++- libnymea/interfaces/longpressbutton.json | 3 ++- libnymea/interfaces/longpressmultibutton.json | 3 ++- libnymea/interfaces/moisturesensor.json | 3 ++- libnymea/interfaces/multibutton.json | 3 ++- libnymea/interfaces/noisesensor.json | 3 ++- libnymea/interfaces/notifications.json | 4 ++-- libnymea/interfaces/power.json | 3 ++- libnymea/interfaces/presencesensor.json | 3 ++- libnymea/interfaces/pressuresensor.json | 3 ++- libnymea/interfaces/smartlock.json | 3 ++- libnymea/interfaces/statefulgaragedoor.json | 3 ++- libnymea/interfaces/temperaturesensor.json | 3 ++- libnymea/interfaces/useraccesscontrol.json | 10 ++++++---- libnymea/interfaces/windspeedsensor.json | 3 ++- libnymea/types/interfaceeventtype.cpp | 10 ++++++++++ libnymea/types/interfaceeventtype.h | 4 ++++ libnymea/types/interfacestatetype.cpp | 10 ++++++++++ libnymea/types/interfacestatetype.h | 4 ++++ 37 files changed, 109 insertions(+), 36 deletions(-) diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 4d0f05f7..8a15050a 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -660,7 +660,17 @@ ZigbeeManager *NymeaCore::zigbeeManager() const void NymeaCore::gotEvent(const Event &event) { - m_logger->logEvent(event); + Thing *thing = m_thingManager->findConfiguredThing(event.thingId()); + ThingClass thingClass = thing ? thing->thingClass() : ThingClass(); + EventType eventType = thingClass.eventTypes().findById(event.eventTypeId()); + foreach (const QString &interfaceName, thingClass.interfaces()) { + Interface iface = m_thingManager->supportedInterfaces().findByName(interfaceName); + if (iface.eventTypes().findByName(eventType.name()).logged()) { + m_logger->logEvent(event); + break; + } + } + emit eventTriggered(event); QList actions; diff --git a/libnymea/integrations/thingutils.cpp b/libnymea/integrations/thingutils.cpp index cdf52435..6c384562 100644 --- a/libnymea/integrations/thingutils.cpp +++ b/libnymea/integrations/thingutils.cpp @@ -200,6 +200,7 @@ Interface ThingUtils::loadInterface(const QString &name) stateType.setMinValue(stateVariant.toMap().value("minValue")); stateType.setMaxValue(stateVariant.toMap().value("maxValue")); stateType.setOptional(stateVariant.toMap().value("optional", false).toBool()); + stateType.setLogged(stateVariant.toMap().value("logged", false).toBool()); if (stateVariant.toMap().contains("unit")) { QMetaEnum unitEnum = QMetaEnum::fromType(); int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray()); @@ -214,6 +215,7 @@ Interface ThingUtils::loadInterface(const QString &name) InterfaceEventType stateChangeEventType; stateChangeEventType.setName(stateType.name()); stateChangeEventType.setOptional(stateType.optional()); + stateChangeEventType.setLogged(stateType.logged()); ParamType stateChangeEventParamType; stateChangeEventParamType.setName(stateType.name()); stateChangeEventParamType.setType(stateType.type()); @@ -254,6 +256,7 @@ Interface ThingUtils::loadInterface(const QString &name) InterfaceEventType eventType; eventType.setName(eventVariant.toMap().value("name").toString()); eventType.setOptional(eventVariant.toMap().value("optional").toBool()); + eventType.setLogged(eventVariant.toMap().value("logged").toBool()); ParamTypes paramTypes; foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) { ParamType paramType; diff --git a/libnymea/interfaces/battery.json b/libnymea/interfaces/battery.json index 8c580479..e6c0e3b2 100644 --- a/libnymea/interfaces/battery.json +++ b/libnymea/interfaces/battery.json @@ -2,7 +2,8 @@ "states": [ { "name": "batteryCritical", - "type": "bool" + "type": "bool", + "logged": true } ] } diff --git a/libnymea/interfaces/button.json b/libnymea/interfaces/button.json index 72211093..d98b2573 100644 --- a/libnymea/interfaces/button.json +++ b/libnymea/interfaces/button.json @@ -2,7 +2,8 @@ "description": "The base for all buttons that emit a pressed event.", "events": [ { - "name": "pressed" + "name": "pressed", + "logged": true } ] } diff --git a/libnymea/interfaces/closablesensor.json b/libnymea/interfaces/closablesensor.json index b8ef5b55..e8320fc5 100644 --- a/libnymea/interfaces/closablesensor.json +++ b/libnymea/interfaces/closablesensor.json @@ -4,7 +4,8 @@ "states": [ { "name": "closed", - "type": "bool" + "type": "bool", + "logged": true } ] } diff --git a/libnymea/interfaces/co2sensor.json b/libnymea/interfaces/co2sensor.json index 2174ebb8..e4673292 100644 --- a/libnymea/interfaces/co2sensor.json +++ b/libnymea/interfaces/co2sensor.json @@ -5,7 +5,8 @@ { "name": "co2", "type": "double", - "unit": "PartsPerMillion" + "unit": "PartsPerMillion", + "logged": true } ] } diff --git a/libnymea/interfaces/conductivitysensor.json b/libnymea/interfaces/conductivitysensor.json index d1d9e63c..4d4f7533 100644 --- a/libnymea/interfaces/conductivitysensor.json +++ b/libnymea/interfaces/conductivitysensor.json @@ -4,7 +4,8 @@ { "name": "conductivity", "type": "double", - "unit": "MicroSiemensPerCentimeter" + "unit": "MicroSiemensPerCentimeter", + "logged": true } ] } diff --git a/libnymea/interfaces/connectable.json b/libnymea/interfaces/connectable.json index bc061381..aca9566c 100644 --- a/libnymea/interfaces/connectable.json +++ b/libnymea/interfaces/connectable.json @@ -3,7 +3,8 @@ { "name": "connected", "type": "bool", - "defaultValue": false + "defaultValue": false, + "logged": true } ] } diff --git a/libnymea/interfaces/daylightsensor.json b/libnymea/interfaces/daylightsensor.json index db82c096..e391bec0 100644 --- a/libnymea/interfaces/daylightsensor.json +++ b/libnymea/interfaces/daylightsensor.json @@ -4,7 +4,8 @@ "states": [ { "name": "daylight", - "type": "bool" + "type": "bool", + "logged": true }, { "name": "sunriseTime", diff --git a/libnymea/interfaces/doorbell.json b/libnymea/interfaces/doorbell.json index e90aad65..bc6dd97d 100644 --- a/libnymea/interfaces/doorbell.json +++ b/libnymea/interfaces/doorbell.json @@ -2,7 +2,8 @@ "description": "An interface for doorbells. Emits \"doorbellPressed\" when the doorbell is pressed.", "events": [ { - "name": "doorbellPressed" + "name": "doorbellPressed", + "logged": true } ] } diff --git a/libnymea/interfaces/extendedclosable.json b/libnymea/interfaces/extendedclosable.json index d5ca5085..48b6342b 100644 --- a/libnymea/interfaces/extendedclosable.json +++ b/libnymea/interfaces/extendedclosable.json @@ -4,7 +4,8 @@ "states": [ { "name": "moving", - "type": "bool" + "type": "bool", + "logged": true }, { "name": "percentage", diff --git a/libnymea/interfaces/extendedsmartmeterconsumer.json b/libnymea/interfaces/extendedsmartmeterconsumer.json index f476bd9e..6aa31b31 100644 --- a/libnymea/interfaces/extendedsmartmeterconsumer.json +++ b/libnymea/interfaces/extendedsmartmeterconsumer.json @@ -5,7 +5,8 @@ { "name": "currentPower", "type": "double", - "unit": "Watt" + "unit": "Watt", + "logged": true } ] } diff --git a/libnymea/interfaces/extendedsmartmeterproducer.json b/libnymea/interfaces/extendedsmartmeterproducer.json index 0c8ad6c7..218f3bf0 100644 --- a/libnymea/interfaces/extendedsmartmeterproducer.json +++ b/libnymea/interfaces/extendedsmartmeterproducer.json @@ -4,7 +4,9 @@ "states": [ { "name": "currentPower", - "type": "double" + "type": "double", + "unit": "Watt", + "logged": true } ] } diff --git a/libnymea/interfaces/fingerprintreader.json b/libnymea/interfaces/fingerprintreader.json index 953222b5..5af7d805 100644 --- a/libnymea/interfaces/fingerprintreader.json +++ b/libnymea/interfaces/fingerprintreader.json @@ -24,7 +24,8 @@ "PinkyRight" ] } - ] + ], + "logged": true } ], "actions": [ diff --git a/libnymea/interfaces/garagegate.json b/libnymea/interfaces/garagegate.json index e692e7e9..653c68f8 100644 --- a/libnymea/interfaces/garagegate.json +++ b/libnymea/interfaces/garagegate.json @@ -5,7 +5,8 @@ { "name": "state", "type": "QString", - "allowedValues": ["open", "closed", "opening", "closing"] + "allowedValues": ["open", "closed", "opening", "closing"], + "logged": true }, { "name": "intermediatePosition", diff --git a/libnymea/interfaces/humiditysensor.json b/libnymea/interfaces/humiditysensor.json index 0f15fd86..a3629bf7 100644 --- a/libnymea/interfaces/humiditysensor.json +++ b/libnymea/interfaces/humiditysensor.json @@ -5,7 +5,8 @@ "name": "humidity", "type": "double", "minValue": 0, - "maxValue": 100 + "maxValue": 100, + "logged": true } ] } diff --git a/libnymea/interfaces/impulsegaragedoor.json b/libnymea/interfaces/impulsegaragedoor.json index 15155c3c..a662c9f2 100644 --- a/libnymea/interfaces/impulsegaragedoor.json +++ b/libnymea/interfaces/impulsegaragedoor.json @@ -3,7 +3,8 @@ "description": "This interface is for the simplest form of garage doors which can be controlled only via an impulse. Triggering the impulse will start moving the door, triggering it again will stop the movement. Triggering it yet another time will start movement in the reverse direction. Note that there is no feedback channel on such devices. The system has no chance of knowing the current state this device is actually in.", "actions": [ { - "name": "triggerImpulse" + "name": "triggerImpulse", + "logged": true } ] } diff --git a/libnymea/interfaces/inputtrigger.json b/libnymea/interfaces/inputtrigger.json index 393e47da..884a3797 100644 --- a/libnymea/interfaces/inputtrigger.json +++ b/libnymea/interfaces/inputtrigger.json @@ -1,7 +1,8 @@ { "events": [ { - "name": "triggered" + "name": "triggered", + "logged": true } ] } diff --git a/libnymea/interfaces/lightsensor.json b/libnymea/interfaces/lightsensor.json index 146b149a..54a425b2 100644 --- a/libnymea/interfaces/lightsensor.json +++ b/libnymea/interfaces/lightsensor.json @@ -4,7 +4,8 @@ { "name": "lightIntensity", "type": "double", - "unit": "Lux" + "unit": "Lux", + "logged": true } ] } diff --git a/libnymea/interfaces/longpressbutton.json b/libnymea/interfaces/longpressbutton.json index 75efeb8d..4a4ea4b5 100644 --- a/libnymea/interfaces/longpressbutton.json +++ b/libnymea/interfaces/longpressbutton.json @@ -3,7 +3,8 @@ "extends": "button", "events": [ { - "name": "longPressed" + "name": "longPressed", + "logged": true } ] } diff --git a/libnymea/interfaces/longpressmultibutton.json b/libnymea/interfaces/longpressmultibutton.json index 60fd2c7a..e696de50 100644 --- a/libnymea/interfaces/longpressmultibutton.json +++ b/libnymea/interfaces/longpressmultibutton.json @@ -9,7 +9,8 @@ "name": "buttonName", "type": "QString" } - ] + ], + "logged": true } ] } diff --git a/libnymea/interfaces/moisturesensor.json b/libnymea/interfaces/moisturesensor.json index bd01c0b9..a6c47379 100644 --- a/libnymea/interfaces/moisturesensor.json +++ b/libnymea/interfaces/moisturesensor.json @@ -3,7 +3,8 @@ "states": [ { "name": "moisture", - "type": "double" + "type": "double", + "logged": true } ] } diff --git a/libnymea/interfaces/multibutton.json b/libnymea/interfaces/multibutton.json index 8fc5ae08..20b94171 100644 --- a/libnymea/interfaces/multibutton.json +++ b/libnymea/interfaces/multibutton.json @@ -9,7 +9,8 @@ "name": "buttonName", "type": "QString" } - ] + ], + "logged": true } ] } diff --git a/libnymea/interfaces/noisesensor.json b/libnymea/interfaces/noisesensor.json index b69d0461..ec578352 100644 --- a/libnymea/interfaces/noisesensor.json +++ b/libnymea/interfaces/noisesensor.json @@ -5,7 +5,8 @@ { "name": "noise", "type": "double", - "unit": "Dezibel" + "unit": "Dezibel", + "logged": true } ] } diff --git a/libnymea/interfaces/notifications.json b/libnymea/interfaces/notifications.json index 1341ce3b..dc85d015 100644 --- a/libnymea/interfaces/notifications.json +++ b/libnymea/interfaces/notifications.json @@ -11,8 +11,8 @@ "name": "body", "type": "QString" } - ] + ], + "logged": true } - ] } diff --git a/libnymea/interfaces/power.json b/libnymea/interfaces/power.json index 160f9432..a70fbabf 100644 --- a/libnymea/interfaces/power.json +++ b/libnymea/interfaces/power.json @@ -3,7 +3,8 @@ { "name": "power", "type": "bool", - "writable": true + "writable": true, + "logged": true } ] } diff --git a/libnymea/interfaces/presencesensor.json b/libnymea/interfaces/presencesensor.json index 7806b3d9..6fcb8c00 100644 --- a/libnymea/interfaces/presencesensor.json +++ b/libnymea/interfaces/presencesensor.json @@ -4,7 +4,8 @@ "states": [ { "name": "isPresent", - "type": "bool" + "type": "bool", + "logged": true }, { "name": "lastSeenTime", diff --git a/libnymea/interfaces/pressuresensor.json b/libnymea/interfaces/pressuresensor.json index a8b45887..64802ed7 100644 --- a/libnymea/interfaces/pressuresensor.json +++ b/libnymea/interfaces/pressuresensor.json @@ -4,7 +4,8 @@ { "name": "pressure", "type": "double", - "unit": "MilliBar" + "unit": "MilliBar", + "logged": true } ] } diff --git a/libnymea/interfaces/smartlock.json b/libnymea/interfaces/smartlock.json index 498950d3..95c71fc2 100644 --- a/libnymea/interfaces/smartlock.json +++ b/libnymea/interfaces/smartlock.json @@ -4,7 +4,8 @@ { "name": "state", "type": "QString", - "allowedValues": ["locked", "locking", "unlocked", "unlocking", "unlatched", "unlatching"] + "allowedValues": ["locked", "locking", "unlocked", "unlocking", "unlatched", "unlatching"], + "logged": true } ], "actions": [ diff --git a/libnymea/interfaces/statefulgaragedoor.json b/libnymea/interfaces/statefulgaragedoor.json index cd0c9ecb..ed1271cd 100644 --- a/libnymea/interfaces/statefulgaragedoor.json +++ b/libnymea/interfaces/statefulgaragedoor.json @@ -5,7 +5,8 @@ { "name": "state", "type": "QString", - "allowedValues": ["open", "closed", "opening", "closing", "intermediate"] + "allowedValues": ["open", "closed", "opening", "closing", "intermediate"], + "logged": true } ] } diff --git a/libnymea/interfaces/temperaturesensor.json b/libnymea/interfaces/temperaturesensor.json index 04fafa42..7edad7f9 100644 --- a/libnymea/interfaces/temperaturesensor.json +++ b/libnymea/interfaces/temperaturesensor.json @@ -4,7 +4,8 @@ { "name": "temperature", "type": "double", - "unit": "DegreeCelsius" + "unit": "DegreeCelsius", + "logged": true } ] } diff --git a/libnymea/interfaces/useraccesscontrol.json b/libnymea/interfaces/useraccesscontrol.json index b1059651..5d43dccf 100644 --- a/libnymea/interfaces/useraccesscontrol.json +++ b/libnymea/interfaces/useraccesscontrol.json @@ -14,7 +14,8 @@ "name": "userId", "type": "QString" } - ] + ], + "logged": true } ], "actions": [ @@ -25,8 +26,8 @@ "name": "userId", "type": "QString" } - - ] + ], + "logged": true }, { "name": "removeUser", @@ -36,7 +37,8 @@ "type": "QString" } - ] + ], + "logged": true } ] } diff --git a/libnymea/interfaces/windspeedsensor.json b/libnymea/interfaces/windspeedsensor.json index 83bf0a01..62a61404 100644 --- a/libnymea/interfaces/windspeedsensor.json +++ b/libnymea/interfaces/windspeedsensor.json @@ -5,7 +5,8 @@ { "name": "windSpeed", "type": "double", - "unit": "MeterPerSecond" + "unit": "MeterPerSecond", + "logged": true } ] } diff --git a/libnymea/types/interfaceeventtype.cpp b/libnymea/types/interfaceeventtype.cpp index 1ee3f9ac..9d4ce6aa 100644 --- a/libnymea/types/interfaceeventtype.cpp +++ b/libnymea/types/interfaceeventtype.cpp @@ -15,6 +15,16 @@ void InterfaceEventType::setOptional(bool optional) m_optional = optional; } +bool InterfaceEventType::logged() const +{ + return m_logged; +} + +void InterfaceEventType::setLogged(bool logged) +{ + m_logged = logged; +} + InterfaceEventTypes::InterfaceEventTypes(const QList &other): QList(other) { diff --git a/libnymea/types/interfaceeventtype.h b/libnymea/types/interfaceeventtype.h index c81305b4..aa3a79c2 100644 --- a/libnymea/types/interfaceeventtype.h +++ b/libnymea/types/interfaceeventtype.h @@ -11,8 +11,12 @@ public: bool optional() const; void setOptional(bool optional); + bool logged() const; + void setLogged(bool logged); + private: bool m_optional = false; + bool m_logged = false; }; class InterfaceEventTypes: public QList diff --git a/libnymea/types/interfacestatetype.cpp b/libnymea/types/interfacestatetype.cpp index 97981bd1..e2b97191 100644 --- a/libnymea/types/interfacestatetype.cpp +++ b/libnymea/types/interfacestatetype.cpp @@ -15,6 +15,16 @@ void InterfaceStateType::setOptional(bool optional) m_optional = optional; } +bool InterfaceStateType::logged() const +{ + return m_logged; +} + +void InterfaceStateType::setLogged(bool logged) +{ + m_logged = logged; +} + InterfaceStateTypes::InterfaceStateTypes(const QList &other): QList(other) { diff --git a/libnymea/types/interfacestatetype.h b/libnymea/types/interfacestatetype.h index 247f3b48..88429916 100644 --- a/libnymea/types/interfacestatetype.h +++ b/libnymea/types/interfacestatetype.h @@ -11,8 +11,12 @@ public: bool optional() const; void setOptional(bool optional); + bool logged() const; + void setLogged(bool logged); + private: bool m_optional = false; + bool m_logged = false; }; class InterfaceStateTypes: public QList From 30a04692121c922d97d05baae2f79b410c5b23b1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 23 Sep 2020 20:15:24 +0200 Subject: [PATCH 02/12] Update tests --- libnymea-core/servers/webserver.cpp | 1 - plugins/mock/httpdaemon.cpp | 2 +- tests/auto/jsonrpc/testjsonrpc.cpp | 50 +---------------- tests/auto/logging/testlogging.cpp | 87 +++++++++++++++++++---------- 4 files changed, 61 insertions(+), 79 deletions(-) diff --git a/libnymea-core/servers/webserver.cpp b/libnymea-core/servers/webserver.cpp index 2ac0f391..7a93037c 100644 --- a/libnymea-core/servers/webserver.cpp +++ b/libnymea-core/servers/webserver.cpp @@ -301,7 +301,6 @@ void WebServer::readClient() HttpRequest request; if (m_incompleteRequests.contains(socket)) { - qCDebug(dcWebServer()) << "Append data to incomlete request"; request = m_incompleteRequests.take(socket); request.appendData(data); } else { diff --git a/plugins/mock/httpdaemon.cpp b/plugins/mock/httpdaemon.cpp index 4d684002..5d5ab4c9 100644 --- a/plugins/mock/httpdaemon.cpp +++ b/plugins/mock/httpdaemon.cpp @@ -104,7 +104,7 @@ void HttpDaemon::readClient() } else if (stateTypeId == mockDoubleStateTypeId) { stateValue.convert(QVariant::Double); } - qCDebug(dcMock()) << "Set state value" << stateValue; + qCDebug(dcMock()) << "Setting state value" << stateValue; emit setState(stateTypeId, stateValue); } else if (url.path() == "/generateevent") { emit triggerEvent(EventTypeId(query.queryItemValue("eventtypeid"))); diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index 4f7280bd..ab46d928 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -852,22 +852,6 @@ void TestJSONRPC::ruleActiveChangedNotifications() clientSpy.wait(); - waitForDBSync(); - - // Make sure the logg notification contains all the stuff we expect - QVariantList logEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); - QVERIFY2(!logEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); - bool found = false; - foreach (const QVariant &loggEntryAddedVariant, logEntryAddedVariants) { - if (loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("typeId").toUuid() == mockIntStateTypeId) { - found = true; - QCOMPARE(loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("source").toString(), QString("LoggingSourceStates")); - QCOMPARE(loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("value").toInt(), 20); - break; - } - } - QVERIFY2(found, "LogEntryAdded notification not received"); - spy.clear(); clientSpy.clear(); // set the rule inactive @@ -889,22 +873,6 @@ void TestJSONRPC::ruleActiveChangedNotifications() clientSpy.wait(); } - // Make sure the logg notification contains all the stuff we expect - logEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); - QVERIFY2(!logEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); - found = false; - foreach (const QVariant &logEntryAddedVariant, logEntryAddedVariants) { - qCDebug(dcTests()) << "Checking log entry" << mockIntStateTypeId << qUtf8Printable(QJsonDocument::fromVariant(logEntryAddedVariant).toJson()); - if (logEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("typeId").toUuid() == mockIntStateTypeId) { - found = true; - QCOMPARE(logEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("source").toString(), QString("LoggingSourceStates")); - QCOMPARE(logEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("value").toInt(), 42); - break; - } - } - QVERIFY2(found, "LogEntryAdded notification not received"); - - if (clientSpy.count() == 0) clientSpy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged"); verifyRuleError(response); @@ -960,26 +928,10 @@ void TestJSONRPC::stateChangeEmitsNotifications() // Devices.StateChanged // Devices.EventTriggered // Events.EventTriggered <-- deprecated - // Logging.LogEntryAdded - while (clientSpy.count() < 4) { + while (clientSpy.count() < 3) { clientSpy.wait(); } - // Make sure the logg notification contains all the stuff we expect - QVariantList logEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); - QVERIFY2(!logEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); - found = false; - foreach (const QVariant &loggEntryAddedVariant, logEntryAddedVariants) { - if (loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("typeId").toUuid() == stateTypeId) { - found = true; - QCOMPARE(loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("source").toString(), QString("LoggingSourceStates")); - QCOMPARE(loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("value").toInt(), newVal); - break; - } - } - - QVERIFY2(found, "Could not find the corresponding Logging.LogEntryAdded notification"); - // Make sure the notification contains all the stuff we expect QVariantList eventTriggeredVariants = checkNotifications(clientSpy, "Events.EventTriggered"); QVERIFY2(!eventTriggeredVariants.isEmpty(), "Did not get Events.EventTriggered notification."); diff --git a/tests/auto/logging/testlogging.cpp b/tests/auto/logging/testlogging.cpp index 10045f6d..f0cc833e 100644 --- a/tests/auto/logging/testlogging.cpp +++ b/tests/auto/logging/testlogging.cpp @@ -71,6 +71,7 @@ private slots: void invalidFilter_data(); void invalidFilter(); + void eventLogs_data(); void eventLogs(); void actionLog(); @@ -261,24 +262,53 @@ void TestLogging::invalidFilter() qDebug() << response.toMap().value("error").toString(); } +void TestLogging::eventLogs_data() +{ + QTest::addColumn("stateTypeId"); + QTest::addColumn("initValue"); + QTest::addColumn("newValue"); + QTest::addColumn("expectLogEntry"); + + QTest::newRow("logged event") << mockConnectedStateTypeId << QVariant(false) << QVariant(true) << true; + QTest::newRow("not logged event") << mockSignalStrengthStateTypeId << QVariant(10) << QVariant(20) << false; +} void TestLogging::eventLogs() { + QFETCH(StateTypeId, stateTypeId); + QFETCH(QVariant, initValue); + QFETCH(QVariant, newValue); + QFETCH(bool, expectLogEntry); + QList devices = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId); QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test"); Thing *device = devices.first(); - enableNotifications({"Events", "Logging"}); - // Setup connection to mock client QNetworkAccessManager nam; + int port = device->paramValue(mockThingHttpportParamTypeId).toInt(); + + // init state in mock device + QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(stateTypeId.toString()).arg(initValue.toString()))); + QNetworkReply *reply = nam.get(request); + { + QSignalSpy finishedSpy(reply, &QNetworkReply::finished); + finishedSpy.wait(); + } + + // Now snoop in for the events + clearLoggingDatabase(); + enableNotifications({"Events", "Logging"}); QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); - // trigger event in mock device - int port = device->paramValue(mockThingHttpportParamTypeId).toInt(); - QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(port).arg(mockEvent1EventTypeId.toString()))); - QNetworkReply *reply = nam.get(request); + // trigger state change in mock device + request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(stateTypeId.toString()).arg(newValue.toString()))); + reply = nam.get(request); + { + QSignalSpy finishedSpy(reply, &QNetworkReply::finished); + finishedSpy.wait(); + } // Lets wait for the notification QTest::qWait(200); @@ -286,41 +316,42 @@ void TestLogging::eventLogs() reply->deleteLater(); // Make sure the logg notification contains all the stuff we expect - QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); - QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); - qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded notifications"; + QVariantList logEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); + qDebug() << "got" << logEntryAddedVariants.count() << "Logging.LogEntryAdded notifications"; bool found = false; - qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded"; - foreach (const QVariant &loggEntryAddedVariant, loggEntryAddedVariants) { - QVariantMap logEntry = loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap(); + qDebug() << "got" << logEntryAddedVariants.count() << "Logging.LogEntryAdded"; + foreach (const QVariant &logEntryAddedVariant, logEntryAddedVariants) { + QVariantMap logEntry = logEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap(); if (logEntry.value("thingId").toUuid() == device->id()) { found = true; // Make sure the notification contains all the stuff we expect - QCOMPARE(logEntry.value("typeId").toUuid().toString(), mockEvent1EventTypeId.toString()); + QCOMPARE(logEntry.value("typeId").toUuid().toString(), stateTypeId.toString()); QCOMPARE(logEntry.value("eventType").toString(), enumValueName(Logging::LoggingEventTypeTrigger)); - QCOMPARE(logEntry.value("source").toString(), enumValueName(Logging::LoggingSourceEvents)); + QCOMPARE(logEntry.value("source").toString(), enumValueName(Logging::LoggingSourceStates)); QCOMPARE(logEntry.value("loggingLevel").toString(), enumValueName(Logging::LoggingLevelInfo)); break; } } - if (!found) - qDebug() << QJsonDocument::fromVariant(loggEntryAddedVariants).toJson(); - QVERIFY2(found, "Could not find the corresponding Logging.LogEntryAdded notification"); + QVERIFY2(found == expectLogEntry, "Could not find the corresponding Logging.LogEntryAdded notification"); - // get this logentry with filter - QVariantMap params; - params.insert("thingIds", QVariantList() << device->id()); - params.insert("loggingSources", QVariantList() << enumValueName(Logging::LoggingSourceEvents)); - params.insert("eventTypes", QVariantList() << enumValueName(Logging::LoggingEventTypeTrigger)); - params.insert("typeIds", QVariantList() << mockEvent1EventTypeId); + if (expectLogEntry) { + // get this logentry with filter + QVariantMap params; + params.insert("thingIds", QVariantList() << device->id()); + params.insert("loggingSources", QVariantList() << enumValueName(Logging::LoggingSourceStates)); + params.insert("eventTypes", QVariantList() << enumValueName(Logging::LoggingEventTypeTrigger)); + params.insert("typeIds", QVariantList() << stateTypeId); - QVariant response = injectAndWait("Logging.GetLogEntries", params); - verifyLoggingError(response); + QVariant response = injectAndWait("Logging.GetLogEntries", params); + verifyLoggingError(response); + + QVariantList logEntries = response.toMap().value("params").toMap().value("logEntries").toList(); + qCDebug(dcTests()) << qUtf8Printable(QJsonDocument::fromVariant(logEntries).toJson()); + QCOMPARE(logEntries.count(), 1); + } - QVariantList logEntries = response.toMap().value("params").toMap().value("logEntries").toList(); - QVERIFY(logEntries.count() == 1); // disable notifications QCOMPARE(disableNotifications(), true); @@ -629,7 +660,7 @@ void TestLogging::testHouseKeeping() // Trigger something that creates a logging entry QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); - QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(6667).arg(mockIntStateTypeId.toString()).arg(4321))); + QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(6667).arg(mockConnectedStateTypeId.toString()).arg(false))); QNetworkReply *reply = nam.get(request); connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); spy.wait(); From 59140f7bd40ff89d472a5f21f0bf922c1abc4bcc Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Oct 2020 17:17:26 +0100 Subject: [PATCH 03/12] log weather states --- libnymea/interfaces/weather.json | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/libnymea/interfaces/weather.json b/libnymea/interfaces/weather.json index 5560ed43..49d82623 100644 --- a/libnymea/interfaces/weather.json +++ b/libnymea/interfaces/weather.json @@ -2,32 +2,39 @@ "states": [ { "name": "weatherDescription", - "type": "QString" + "type": "QString", + "logged": true }, { "name": "weatherCondition", "type": "QString", - "allowedValues": ["clear-day", "clear-night", "few-clouds-day", "few-clouds-night", "clouds", "overcast", "light-rain", "shower-rain", "thunderstorm", "snow", "fog"] + "allowedValues": ["clear-day", "clear-night", "few-clouds-day", "few-clouds-night", "clouds", "overcast", "light-rain", "shower-rain", "thunderstorm", "snow", "fog"], + "logged": true }, { "name": "temperature", - "type": "double" + "type": "double", + "logged": true }, { "name": "humidity", - "type": "int" + "type": "int", + "logged": true }, { "name": "pressure", - "type": "double" + "type": "double", + "logged": true }, { "name": "windSpeed", - "type": "double" + "type": "double", + "logged": true }, { "name": "windDirection", - "type": "int" + "type": "int", + "logged": true } ] } From 56448543dfc48664d521637cc3564362aea30dbe Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 15 Dec 2020 11:14:42 +0100 Subject: [PATCH 04/12] An attempt to make it configurable --- libnymea/integrations/thing.cpp | 18 ++++++++++++++++++ libnymea/integrations/thing.h | 3 +++ libnymea/types/state.cpp | 20 ++++++++++++++++++++ libnymea/types/state.h | 10 ++++++++++ libnymea/types/statetype.cpp | 10 ++++++++++ libnymea/types/statetype.h | 8 ++++++++ libnymea/typeutils.h | 6 ++++++ 7 files changed, 75 insertions(+) diff --git a/libnymea/integrations/thing.cpp b/libnymea/integrations/thing.cpp index fb62b57f..48581061 100644 --- a/libnymea/integrations/thing.cpp +++ b/libnymea/integrations/thing.cpp @@ -383,6 +383,24 @@ State Thing::state(const StateTypeId &stateTypeId) const return State(StateTypeId(), ThingId()); } +void Thing::setStateLogged(const StateTypeId &stateTypeId, bool logged) +{ + for (int i = 0; i < m_states.count(); i++) { + if (m_states.at(i).stateTypeId() == stateTypeId) { + m_states[i].setLogged(logged); + } + } +} + +void Thing::setStateFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter) +{ + for (int i = 0; i < m_states.count(); i++) { + if (m_states.at(i).stateTypeId() == stateTypeId) { + m_states[i].setFilter(filter); + } + } +} + /*! Returns the \l{ThingId} of the parent of this thing. If the parentId is not set, this thing does not have a parent. */ diff --git a/libnymea/integrations/thing.h b/libnymea/integrations/thing.h index 2e739aa0..f5b46244 100644 --- a/libnymea/integrations/thing.h +++ b/libnymea/integrations/thing.h @@ -133,6 +133,9 @@ public: Q_INVOKABLE State state(const StateTypeId &stateTypeId) const; + void setStateLogged(const StateTypeId &stateTypeId, bool logged); + void setStateFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter); + ThingId parentId() const; void setParentId(const ThingId &parentId); diff --git a/libnymea/types/state.cpp b/libnymea/types/state.cpp index 8df85f32..3717bf85 100644 --- a/libnymea/types/state.cpp +++ b/libnymea/types/state.cpp @@ -80,6 +80,26 @@ void State::setValue(const QVariant &value) m_value = value; } +Types::StateValueFilter State::filter() const +{ + return m_filter; +} + +void State::setFilter(Types::StateValueFilter filter) +{ + m_filter = filter; +} + +bool State::logged() const +{ + return m_logged; +} + +void State::setLogged(bool logged) +{ + m_logged = logged; +} + /*! Writes the stateTypeId, the deviceId and the value of the given \a state to \a dbg. */ QDebug operator<<(QDebug dbg, const State &state) { diff --git a/libnymea/types/state.h b/libnymea/types/state.h index f6b43659..754fd843 100644 --- a/libnymea/types/state.h +++ b/libnymea/types/state.h @@ -42,6 +42,8 @@ class LIBNYMEA_EXPORT State Q_GADGET Q_PROPERTY(QUuid stateTypeId READ stateTypeId) Q_PROPERTY(QVariant value READ value) + Q_PROPERTY(Types::StateValueFilter filter READ filter) + Q_PROPERTY(bool logged READ logged) public: State(); @@ -53,10 +55,18 @@ public: QVariant value() const; void setValue(const QVariant &value); + Types::StateValueFilter filter() const; + void setFilter(Types::StateValueFilter filter); + + bool logged() const; + void setLogged(bool logged); + private: StateTypeId m_stateTypeId; ThingId m_thingId; QVariant m_value; + Types::StateValueFilter m_filter; + bool m_logged = false; }; Q_DECLARE_METATYPE(State) diff --git a/libnymea/types/statetype.cpp b/libnymea/types/statetype.cpp index 2550380b..d566752c 100644 --- a/libnymea/types/statetype.cpp +++ b/libnymea/types/statetype.cpp @@ -208,6 +208,16 @@ void StateType::setCached(bool cached) m_cached = cached; } +Types::StateValueFilter StateType::filter() const +{ + return m_filter; +} + +void StateType::setFilter(Types::StateValueFilter filter) +{ + m_filter = filter; +} + /*! Returns a list of all valid properties a DeviceClass definition can have. */ QStringList StateType::typeProperties() { diff --git a/libnymea/types/statetype.h b/libnymea/types/statetype.h index 1532a434..0b542360 100644 --- a/libnymea/types/statetype.h +++ b/libnymea/types/statetype.h @@ -93,6 +93,12 @@ public: bool cached() const; void setCached(bool cached); + bool logged() const; + void setLogged(bool logged); + + Types::StateValueFilter filter() const; + void setFilter(Types::StateValueFilter filter); + static QStringList typeProperties(); static QStringList mandatoryTypeProperties(); @@ -112,6 +118,8 @@ private: Types::IOType m_ioType = Types::IOTypeNone; bool m_writable = false; bool m_cached = true; + bool m_logged = false; + Types::StateValueFilter m_filter = Types::StateValueFilterNone; }; Q_DECLARE_METATYPE(StateType) diff --git a/libnymea/typeutils.h b/libnymea/typeutils.h index 39f914b7..eaa8cddb 100644 --- a/libnymea/typeutils.h +++ b/libnymea/typeutils.h @@ -173,6 +173,12 @@ public: IOTypeAnalogOutput }; Q_ENUM(IOType) + + enum StateValueFilter { + StateValueFilterNone, + StateValueFilterAdaptive + }; + Q_ENUM(StateValueFilter) }; Q_DECLARE_METATYPE(Types::InputType) From ae38e185b6fec1b31afcd032bf1cc20e0a00a987 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 23 Dec 2020 13:00:08 +0100 Subject: [PATCH 05/12] more work... not really working yet --- .../thingmanagerimplementation.cpp | 45 ++++++++++++++++- .../integrations/thingmanagerimplementation.h | 2 +- libnymea-core/jsonrpc/actionhandler.cpp | 2 +- libnymea-core/jsonrpc/devicehandler.cpp | 3 +- libnymea-core/jsonrpc/integrationshandler.cpp | 3 +- libnymea-core/logging/logengine.cpp | 18 ++++++- libnymea-core/logging/logengine.h | 17 +++++-- libnymea-core/nymeacore.cpp | 34 ++----------- libnymea-core/nymeacore.h | 1 - libnymea/integrations/pluginmetadata.cpp | 48 ++++++++++-------- .../statevaluefilters/statevaluefilter.cpp | 6 +++ .../statevaluefilters/statevaluefilter.h | 18 +++++++ .../statevaluefilteradaptive.cpp | 11 +++++ .../statevaluefilteradaptive.h | 15 ++++++ libnymea/integrations/thing.cpp | 49 ++++++++++++------- libnymea/integrations/thing.h | 10 +++- libnymea/integrations/thingmanager.h | 1 + libnymea/integrations/thingutils.cpp | 17 +++++-- libnymea/jsonrpc/jsonhandler.cpp | 22 ++++++++- libnymea/libnymea.pro | 2 + libnymea/types/actiontype.cpp | 33 +++++++++++++ libnymea/types/actiontype.h | 3 ++ libnymea/types/event.cpp | 10 ++++ libnymea/types/event.h | 5 ++ libnymea/types/eventtype.cpp | 46 ++++++++++++++++- libnymea/types/eventtype.h | 7 +++ libnymea/types/interfaceeventtype.cpp | 8 +-- libnymea/types/interfaceeventtype.h | 6 +-- libnymea/types/interfacestatetype.cpp | 8 +-- libnymea/types/interfacestatetype.h | 6 +-- libnymea/types/state.cpp | 10 ---- libnymea/types/state.h | 7 +-- libnymea/types/statetype.cpp | 34 ++++++++++++- libnymea/types/statetype.h | 6 ++- 34 files changed, 390 insertions(+), 123 deletions(-) create mode 100644 libnymea/integrations/statevaluefilters/statevaluefilter.cpp create mode 100644 libnymea/integrations/statevaluefilters/statevaluefilter.h create mode 100644 libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp create mode 100644 libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h diff --git a/libnymea-core/integrations/thingmanagerimplementation.cpp b/libnymea-core/integrations/thingmanagerimplementation.cpp index 694fd9a3..14e7a992 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.cpp +++ b/libnymea-core/integrations/thingmanagerimplementation.cpp @@ -650,6 +650,14 @@ ThingPairingInfo *ThingManagerImplementation::confirmPairing(const PairingTransa ParamList settings = buildParams(thingClass.settingsTypes(), ParamList()); thing->setSettings(settings); + QList loggedEventTypeIds; + foreach (const EventType &eventType, thingClass.eventTypes()) { + if (eventType.suggestLogging()) { + loggedEventTypeIds.append(eventType.id()); + } + } + thing->setLoggedEventTypeIds(loggedEventTypeIds); + ThingSetupInfo *info = setupThing(thing); connect(info, &ThingSetupInfo::finished, thing, [this, info, externalInfo, addNewThing](){ @@ -746,6 +754,14 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi ParamList settings = buildParams(thingClass.settingsTypes(), ParamList()); thing->setSettings(settings); + QList loggedEventTypeIds; + foreach (const EventType &eventType, thingClass.eventTypes()) { + if (eventType.suggestLogging()) { + loggedEventTypeIds.append(eventType.id()); + } + } + thing->setLoggedEventTypeIds(loggedEventTypeIds); + ThingSetupInfo *info = setupThing(thing); connect(info, &ThingSetupInfo::finished, this, [this, info](){ if (info->status() != Thing::ThingErrorNoError) { @@ -1258,6 +1274,10 @@ ThingActionInfo *ThingManagerImplementation::executeAction(const Action &action) return info; } + connect(info, &ThingActionInfo::finished, this, [=](){ + emit actionExecuted(action, info->status()); + }); + plugin->executeAction(info); return info; @@ -1541,6 +1561,14 @@ void ThingManagerImplementation::loadConfiguredThings() thing->setSettings(thingSettings); + QList loggedEventTypeIds; + foreach (const EventType &eventType, thingClass.eventTypes()) { + if (eventType.suggestLogging()) { + loggedEventTypeIds.append(eventType.id()); + } + } + thing->setLoggedEventTypeIds(loggedEventTypeIds); + settings.endGroup(); // ThingId // We always add the thing to the list in this case. If it's in the stored things @@ -1659,6 +1687,14 @@ void ThingManagerImplementation::onAutoThingsAppeared(const ThingDescriptors &th thing->setSettings(settings); thing->setParentId(thingDescriptor.parentId()); + QList loggedEventTypeIds; + foreach (const EventType &eventType, thingClass.eventTypes()) { + if (eventType.suggestLogging()) { + loggedEventTypeIds.append(eventType.id()); + } + } + thing->setLoggedEventTypeIds(loggedEventTypeIds); + qCDebug(dcThingManager()) << "Setting up auto thing:" << thing->name() << thing->id().toString(); ThingSetupInfo *info = setupThing(thing); @@ -1725,7 +1761,7 @@ void ThingManagerImplementation::cleanupThingStateCache() } } -void ThingManagerImplementation::onEventTriggered(const Event &event) +void ThingManagerImplementation::onEventTriggered(Event event) { // Doing some sanity checks here... Thing *thing = m_configuredThings.value(event.thingId()); @@ -1738,7 +1774,12 @@ void ThingManagerImplementation::onEventTriggered(const Event &event) qCWarning(dcThingManager()) << "The given thing does not have an event type of id " + event.eventTypeId().toString() + ". Not forwarding event."; return; } - // All good, forward the event + // configure logging + if (thing->loggedEventTypeIds().contains(event.eventTypeId())) { + event.setLogged(true); + } + + // Forward the event emit eventTriggered(event); } diff --git a/libnymea-core/integrations/thingmanagerimplementation.h b/libnymea-core/integrations/thingmanagerimplementation.h index e8628e81..df742069 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.h +++ b/libnymea-core/integrations/thingmanagerimplementation.h @@ -140,7 +140,7 @@ private slots: void onAutoThingDisappeared(const ThingId &thingId); void onLoaded(); void cleanupThingStateCache(); - void onEventTriggered(const Event &event); + void onEventTriggered(Event event); // Only connect this to Things. It will query the sender() void slotThingStateValueChanged(const StateTypeId &stateTypeId, const QVariant &value); diff --git a/libnymea-core/jsonrpc/actionhandler.cpp b/libnymea-core/jsonrpc/actionhandler.cpp index 4a6721fe..b9f2999f 100644 --- a/libnymea-core/jsonrpc/actionhandler.cpp +++ b/libnymea-core/jsonrpc/actionhandler.cpp @@ -107,7 +107,7 @@ JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms, const JsonCon JsonReply *jsonReply = createAsyncReply("ExecuteAction"); - ThingActionInfo *info = NymeaCore::instance()->executeAction(action); + ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action); connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){ QVariantMap data; data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device")); diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index c8b86c67..281869a7 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -62,6 +62,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : registerEnum(); registerEnum(); registerEnum(); + registerEnum(); registerEnum(); registerEnum(); registerEnum(); @@ -900,7 +901,7 @@ JsonReply *DeviceHandler::ExecuteAction(const QVariantMap ¶ms, const JsonCon JsonReply *jsonReply = createAsyncReply("ExecuteAction"); - ThingActionInfo *info = NymeaCore::instance()->executeAction(action); + ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action); connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){ QVariantMap data; data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device")); diff --git a/libnymea-core/jsonrpc/integrationshandler.cpp b/libnymea-core/jsonrpc/integrationshandler.cpp index 08243330..d42e7e3c 100644 --- a/libnymea-core/jsonrpc/integrationshandler.cpp +++ b/libnymea-core/jsonrpc/integrationshandler.cpp @@ -61,6 +61,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa registerEnum(); registerEnum(); registerEnum(); + registerEnum(); registerEnum(); registerEnum(); registerEnum(); @@ -958,7 +959,7 @@ JsonReply *IntegrationsHandler::ExecuteAction(const QVariantMap ¶ms, const J JsonReply *jsonReply = createAsyncReply("ExecuteAction"); - ThingActionInfo *info = NymeaCore::instance()->executeAction(action); + ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action); connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){ QVariantMap data; data.insert("thingError", enumValueName(info->status())); diff --git a/libnymea-core/logging/logengine.cpp b/libnymea-core/logging/logengine.cpp index 5f6630fc..4927e5a9 100644 --- a/libnymea-core/logging/logengine.cpp +++ b/libnymea-core/logging/logengine.cpp @@ -34,6 +34,8 @@ #include "logging.h" #include "logvaluetool.h" +#include "integrations/thingmanager.h" + #include #include #include @@ -104,6 +106,13 @@ LogEngine::~LogEngine() m_db.close(); } +void LogEngine::setThingManager(ThingManager *thingManager) +{ + m_thingManager = thingManager; + connect(thingManager, &ThingManager::eventTriggered, this, &LogEngine::logEvent); + connect(thingManager, &ThingManager::actionExecuted, this, &LogEngine::logAction); +} + LogEntriesFetchJob *LogEngine::fetchLogEntries(const LogFilter &filter) { QList results; @@ -222,6 +231,10 @@ void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging:: void LogEngine::logEvent(const Event &event) { + if (!event.logged()) { + return; + } + QVariantList valueList; Logging::LoggingSource sourceType; if (event.isStateChangeEvent()) { @@ -249,9 +262,10 @@ void LogEngine::logEvent(const Event &event) appendLogEntry(entry); } -void LogEngine::logAction(const Action &action, Logging::LoggingLevel level, int errorCode) +void LogEngine::logAction(const Action &action, Thing::ThingError status) { - LogEntry entry(level, Logging::LoggingSourceActions, errorCode); + Logging::LoggingLevel level = status == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert; + LogEntry entry(QDateTime::currentDateTime(), level, Logging::LoggingSourceActions, status); entry.setTypeId(action.actionTypeId()); entry.setThingId(action.thingId()); diff --git a/libnymea-core/logging/logengine.h b/libnymea-core/logging/logengine.h index 8ce94c8c..a3749a2e 100644 --- a/libnymea-core/logging/logengine.h +++ b/libnymea-core/logging/logengine.h @@ -38,6 +38,7 @@ #include "types/browseritemaction.h" #include "types/browseraction.h" #include "ruleengine/rule.h" +#include "integrations/thingmanager.h" #include #include @@ -60,6 +61,8 @@ public: LogEngine(const QString &driver, const QString &dbName, const QString &hostname = QString("127.0.0.1"), const QString &username = QString(), const QString &password = QString(), int maxDBSize = 50000, QObject *parent = nullptr); ~LogEngine(); + void setThingManager(ThingManager *thingManager); + LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter()); ThingsFetchJob *fetchThings(); @@ -68,9 +71,11 @@ public: void setMaxLogEntries(int maxLogEntries, int trimSize); void clearDatabase(); + void removeThingLogs(const ThingId &thingId); + void removeRuleLogs(const RuleId &ruleId); + +public slots: void logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level = Logging::LoggingLevelInfo); - void logEvent(const Event &event); - void logAction(const Action &action, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); void logBrowserAction(const BrowserAction &browserAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); void logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); void logRuleTriggered(const Rule &rule); @@ -78,8 +83,10 @@ public: void logRuleEnabledChanged(const Rule &rule, const bool &enabled); void logRuleActionsExecuted(const Rule &rule); void logRuleExitActionsExecuted(const Rule &rule); - void removeThingLogs(const ThingId &thingId); - void removeRuleLogs(const RuleId &ruleId); + +private slots: + void logEvent(const Event &event); + void logAction(const Action &action, Thing::ThingError status); signals: void logEntryAdded(const LogEntry &logEntry); @@ -114,6 +121,8 @@ private: bool m_initialized = false; bool m_dbMalformed = false; + ThingManager *m_thingManager = nullptr; + // When maxQueueLength is exceeded, jobs will be flagged and discarded if this source logs more events int m_maxQueueLength; QHash> m_flaggedJobs; diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 8a15050a..a746f8b8 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -99,9 +99,6 @@ void NymeaCore::init(const QStringList &additionalInterfaces) { } m_timeManager = new TimeManager(this); - qCDebug(dcCore) << "Creating Log Engine"; - m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this); - qCDebug(dcCore()) << "Creating User Manager"; m_userManager = new UserManager(NymeaSettings::settingsPath() + "/user-db.sqlite", this); @@ -120,6 +117,10 @@ void NymeaCore::init(const QStringList &additionalInterfaces) { qCDebug(dcCore) << "Creating Rule Engine"; m_ruleEngine = new RuleEngine(this); + qCDebug(dcCore) << "Creating Log Engine"; + m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this); + m_logger->setThingManager(m_thingManager); + qCDebug(dcCore()) << "Creating Script Engine"; m_scriptEngine = new ScriptEngine(m_thingManager, this); m_serverManager->jsonServer()->registerHandler(new ScriptsHandler(m_scriptEngine, m_scriptEngine)); @@ -380,20 +381,6 @@ Thing::ThingError NymeaCore::removeConfiguredThing(const ThingId &thingId, const return removeError; } -ThingActionInfo* NymeaCore::executeAction(const Action &action) -{ - ThingActionInfo *info = m_thingManager->executeAction(action); - connect(info, &ThingActionInfo::finished, this, [this, info](){ - if (info->status() == Thing::ThingErrorNoError) { - m_logger->logAction(info->action()); - } else { - m_logger->logAction(info->action(), Logging::LoggingLevelAlert, info->status()); - } - }); - - return info; -} - BrowserActionInfo* NymeaCore::executeBrowserItem(const BrowserAction &browserAction) { BrowserActionInfo *info = m_thingManager->executeBrowserItem(browserAction); @@ -512,7 +499,7 @@ void NymeaCore::executeRuleActions(const QList ruleActions) foreach (const Action &action, actions) { qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params(); - ThingActionInfo *info = executeAction(action); + ThingActionInfo *info = m_thingManager->executeAction(action); connect(info, &ThingActionInfo::finished, this, [info](){ if (info->status() != Thing::ThingErrorNoError) { qCWarning(dcRuleEngine) << "Error executing action:" << info->status() << info->displayMessage(); @@ -660,17 +647,6 @@ ZigbeeManager *NymeaCore::zigbeeManager() const void NymeaCore::gotEvent(const Event &event) { - Thing *thing = m_thingManager->findConfiguredThing(event.thingId()); - ThingClass thingClass = thing ? thing->thingClass() : ThingClass(); - EventType eventType = thingClass.eventTypes().findById(event.eventTypeId()); - foreach (const QString &interfaceName, thingClass.interfaces()) { - Interface iface = m_thingManager->supportedInterfaces().findByName(interfaceName); - if (iface.eventTypes().findByName(eventType.name()).logged()) { - m_logger->logEvent(event); - break; - } - } - emit eventTriggered(event); QList actions; diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h index 3391c258..3201b4d6 100644 --- a/libnymea-core/nymeacore.h +++ b/libnymea-core/nymeacore.h @@ -84,7 +84,6 @@ public: QPair >removeConfiguredThing(const ThingId &thingId, const QHash &removePolicyList); Thing::ThingError removeConfiguredThing(const ThingId &thingId, const RuleEngine::RemovePolicy &removePolicy); - ThingActionInfo *executeAction(const Action &action); BrowserActionInfo* executeBrowserItem(const BrowserAction &browserAction); BrowserItemActionInfo* executeBrowserItemAction(const BrowserItemAction &browserItemAction); diff --git a/libnymea/integrations/pluginmetadata.cpp b/libnymea/integrations/pluginmetadata.cpp index e63ad508..5e2b1f7d 100644 --- a/libnymea/integrations/pluginmetadata.cpp +++ b/libnymea/integrations/pluginmetadata.cpp @@ -469,6 +469,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) break; } stateType.setIOType(ioType); + stateType.setSuggestLogging(st.value("suggestLogging").toBool()); } stateTypes.append(stateType); @@ -485,6 +486,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) paramType.setUnit(stateType.unit()); eventType.setParamTypes(QList() << paramType); eventType.setIndex(stateType.index()); + eventType.setSuggestLogging(st.value("suggestLogging").toBool()); eventTypes.append(eventType); // ActionTypes for writeable StateTypes @@ -497,7 +499,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) actionTypes.append(actionType); } } - thingClass.setStateTypes(stateTypes); // ActionTypes index = 0; @@ -544,7 +545,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) actionTypes.append(actionType); } - thingClass.setActionTypes(actionTypes); // EventTypes index = 0; @@ -580,6 +580,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) EventType eventType(eventTypeId); eventType.setName(eventTypeName); eventType.setDisplayName(et.value("displayName").toString()); + eventType.setSuggestLogging(et.value("suggestLogging").toBool()); eventType.setIndex(index++); QPair > paramVerification = parseParamTypes(et.value("paramTypes").toArray()); @@ -590,7 +591,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) } eventTypes.append(eventType); } - thingClass.setEventTypes(eventTypes); // BrowserItemActionTypes index = 0; @@ -637,7 +637,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) browserItemActionTypes.append(actionType); } - thingClass.setBrowserItemActionTypes(browserItemActionTypes); // Read interfaces QStringList interfaces; @@ -646,22 +645,18 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) if (!iface.isValid()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" uses non-existing interface \"" + value.toString() + "\""); hasError = true; + continue; } - StateTypes stateTypes(thingClass.stateTypes()); - ActionTypes actionTypes(thingClass.actionTypes()); - EventTypes eventTypes(thingClass.eventTypes()); - foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) { - StateType stateType = stateTypes.findByName(ifaceStateType.name()); - if (stateType.id().isNull()) { + if (!stateTypes.contains(ifaceStateType.name())) { if (!ifaceStateType.optional()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\""); hasError = true; - } else { - continue; } + continue; } + StateType &stateType = stateTypes[ifaceStateType.name()]; if (ifaceStateType.type() != stateType.type()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching type: \"" + QVariant::typeToName(stateType.type()) + "\" != \"" + QVariant::typeToName(ifaceStateType.type()) + "\""); hasError = true; @@ -697,18 +692,22 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching unit: \"" + unitEnum.valueToKey(ifaceStateType.unit()) + "\" != \"" + unitEnum.valueToKey(stateType.unit())); hasError = true; } + + // Override logged property as the interface has higher priority than the plugin dev + if (ifaceStateType.loggingOverride()) { + stateType.setSuggestLogging(ifaceStateType.suggestLogging()); + } } foreach (const InterfaceActionType &ifaceActionType, iface.actionTypes()) { - ActionType actionType = actionTypes.findByName(ifaceActionType.name()); - if (actionType.id().isNull()) { + if (!actionTypes.contains(ifaceActionType.name())) { if (!ifaceActionType.optional()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\""); hasError = true; - } else { - continue; } + continue; } + ActionType &actionType = actionTypes[ifaceActionType.name()]; // Verify the params as required by the interface are available foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) { ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name()); @@ -779,15 +778,14 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) } foreach (const InterfaceEventType &ifaceEventType, iface.eventTypes()) { - EventType eventType = eventTypes.findByName(ifaceEventType.name()); - if (!eventType.isValid()) { + if (!eventTypes.contains(ifaceEventType.name())) { if (!ifaceEventType.optional()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\""); hasError = true; - } else { - continue; } + continue; } + EventType &eventType = eventTypes[ifaceEventType.name()]; // Verify all the params as required by the interface are available foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) { @@ -842,6 +840,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) } } + // Override logging + if (ifaceEventType.loggingOverride()) { + eventType.setSuggestLogging(ifaceEventType.suggestLogging()); + } + // Note: No need to check for default values (as with actions) for additional params as // an emitted event always needs to have params filled with values. The client might use them or not... } @@ -851,6 +854,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) interfaces.removeDuplicates(); thingClass.setInterfaces(interfaces); + thingClass.setStateTypes(stateTypes); + thingClass.setActionTypes(actionTypes); + thingClass.setEventTypes(eventTypes); + thingClass.setBrowserItemActionTypes(browserItemActionTypes); + m_thingClasses.append(thingClass); } } diff --git a/libnymea/integrations/statevaluefilters/statevaluefilter.cpp b/libnymea/integrations/statevaluefilters/statevaluefilter.cpp new file mode 100644 index 00000000..68e88626 --- /dev/null +++ b/libnymea/integrations/statevaluefilters/statevaluefilter.cpp @@ -0,0 +1,6 @@ +#include "statevaluefilter.h" + +StateValueFilter::StateValueFilter() +{ + +} diff --git a/libnymea/integrations/statevaluefilters/statevaluefilter.h b/libnymea/integrations/statevaluefilters/statevaluefilter.h new file mode 100644 index 00000000..83b59a88 --- /dev/null +++ b/libnymea/integrations/statevaluefilters/statevaluefilter.h @@ -0,0 +1,18 @@ +#ifndef STATEVALUEFILTER_H +#define STATEVALUEFILTER_H + +#include + +class StateValueFilter +{ +public: + StateValueFilter(); + virtual ~StateValueFilter(); + + virtual void addValue(const QVariant &value) = 0; + + virtual QVariant filteredValue() const = 0; + +}; + +#endif // STATEVALUEFILTER_H diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp new file mode 100644 index 00000000..26f5217b --- /dev/null +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp @@ -0,0 +1,11 @@ +#include "statevaluefilteradaptive.h" + +StateValueFilterAdaptive::StateValueFilterAdaptive() +{ + +} + +void StateValueFilterAdaptive::addValue(const QVariant &value) +{ + +} diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h new file mode 100644 index 00000000..dbb85b84 --- /dev/null +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h @@ -0,0 +1,15 @@ +#ifndef STATEVALUEFILTERADAPTIVE_H +#define STATEVALUEFILTERADAPTIVE_H + +#include "statevaluefilter.h" + +class StateValueFilterAdaptive : public StateValueFilter +{ +public: + StateValueFilterAdaptive(); + + void addValue(const QVariant &value) override; + QVariant filteredValue() const override; +}; + +#endif // STATEVALUEFILTERADAPTIVE_H diff --git a/libnymea/integrations/thing.cpp b/libnymea/integrations/thing.cpp index 48581061..17bee3fe 100644 --- a/libnymea/integrations/thing.cpp +++ b/libnymea/integrations/thing.cpp @@ -133,6 +133,7 @@ #include "thing.h" #include "types/event.h" #include "loggingcategories.h" +#include "statevaluefilters/statevaluefilteradaptive.h" #include @@ -334,9 +335,6 @@ void Thing::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) - return; - QVariant newValue = value; if (!newValue.convert(stateType.type())) { qCWarning(dcThing()).nospace() << m_name << ": Invalid value " << value << " for state " << stateType.name() << ". Type mismatch. Expected type: " << QVariant::typeToName(stateType.type()) << " (Discarding change)"; @@ -355,8 +353,13 @@ void Thing::setStateValue(const StateTypeId &stateTypeId, const QVariant &value) return; } - QVariant oldValue = m_states.at(i).value(); + StateValueFilter *filter = m_stateValueFilters.value(stateTypeId); + if (filter) { + filter->addValue(newValue); + newValue = filter->filteredValue(); + } + QVariant oldValue = m_states.at(i).value(); if (oldValue == newValue) { qCDebug(dcThing()).nospace() << m_name << ": Discarding state change for " << stateType.name() << " as the value did not actually change. Old value:" << oldValue << "New value:" << newValue; return; @@ -383,22 +386,9 @@ State Thing::state(const StateTypeId &stateTypeId) const return State(StateTypeId(), ThingId()); } -void Thing::setStateLogged(const StateTypeId &stateTypeId, bool logged) +QList Thing::loggedEventTypeIds() const { - for (int i = 0; i < m_states.count(); i++) { - if (m_states.at(i).stateTypeId() == stateTypeId) { - m_states[i].setLogged(logged); - } - } -} - -void Thing::setStateFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter) -{ - for (int i = 0; i < m_states.count(); i++) { - if (m_states.at(i).stateTypeId() == stateTypeId) { - m_states[i].setFilter(filter); - } - } + return m_loggedEventTypeIds; } /*! Returns the \l{ThingId} of the parent of this thing. If the parentId @@ -460,6 +450,27 @@ void Thing::setSetupStatus(Thing::ThingSetupStatus status, Thing::ThingError set emit setupStatusChanged(); } +void Thing::setLoggedEventTypeIds(const QList loggedEventTypeIds) +{ + m_loggedEventTypeIds = loggedEventTypeIds; +} + +void Thing::setStateValueFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter) +{ + for (int i = 0; i < m_states.count(); i++) { + if (m_states.at(i).stateTypeId() == stateTypeId) { + m_states[i].setFilter(filter); + StateValueFilter *stateValueFilter = m_stateValueFilters.take(stateTypeId); + if (stateValueFilter) { + delete stateValueFilter; + } + if (filter == Types::StateValueFilterAdaptive) { + m_stateValueFilters.insert(stateTypeId, new StateValueFilterAdaptive()); + } + } + } +} + Things::Things(const QList &other) { foreach (Thing* thing, other) { diff --git a/libnymea/integrations/thing.h b/libnymea/integrations/thing.h index f5b46244..efe941c7 100644 --- a/libnymea/integrations/thing.h +++ b/libnymea/integrations/thing.h @@ -45,6 +45,7 @@ #include class IntegrationPlugin; +class StateValueFilter; class LIBNYMEA_EXPORT Thing: public QObject { @@ -60,6 +61,7 @@ class LIBNYMEA_EXPORT Thing: public QObject Q_PROPERTY(QString setupDisplayMessage READ setupDisplayMessage NOTIFY setupStatusChanged USER true) Q_PROPERTY(ThingError setupError READ setupError NOTIFY setupStatusChanged) Q_PROPERTY(QUuid parentId READ parentId USER true) + Q_PROPERTY(QList loggedEventTypeIds READ loggedEventTypeIds USER true) public: enum ThingError { @@ -133,8 +135,7 @@ public: Q_INVOKABLE State state(const StateTypeId &stateTypeId) const; - void setStateLogged(const StateTypeId &stateTypeId, bool logged); - void setStateFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter); + QList loggedEventTypeIds() const; ThingId parentId() const; void setParentId(const ThingId &parentId); @@ -164,6 +165,8 @@ private: Thing(const PluginId &pluginId, const ThingClass &thingClass, QObject *parent = nullptr); void setSetupStatus(ThingSetupStatus status, ThingError setupError, const QString &displayMessage = QString()); + void setLoggedEventTypeIds(const QList loggedEventTypeIds); + void setStateValueFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter); private: ThingClass m_thingClass; @@ -179,6 +182,9 @@ private: ThingSetupStatus m_setupStatus = ThingSetupStatusNone; ThingError m_setupError = ThingErrorNoError; QString m_setupDisplayMessage; + + QList m_loggedEventTypeIds; + QHash m_stateValueFilters; }; QDebug operator<<(QDebug dbg, Thing *device); diff --git a/libnymea/integrations/thingmanager.h b/libnymea/integrations/thingmanager.h index 06e78647..635a98bb 100644 --- a/libnymea/integrations/thingmanager.h +++ b/libnymea/integrations/thingmanager.h @@ -116,6 +116,7 @@ signals: void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value); void ioConnectionAdded(const IOConnection &ioConnection); void ioConnectionRemoved(const IOConnectionId &ioConnectionId); + void actionExecuted(const Action &action, Thing::ThingError status); }; #endif // THINGMANAGER_H diff --git a/libnymea/integrations/thingutils.cpp b/libnymea/integrations/thingutils.cpp index 6c384562..59c728af 100644 --- a/libnymea/integrations/thingutils.cpp +++ b/libnymea/integrations/thingutils.cpp @@ -200,7 +200,10 @@ Interface ThingUtils::loadInterface(const QString &name) stateType.setMinValue(stateVariant.toMap().value("minValue")); stateType.setMaxValue(stateVariant.toMap().value("maxValue")); stateType.setOptional(stateVariant.toMap().value("optional", false).toBool()); - stateType.setLogged(stateVariant.toMap().value("logged", false).toBool()); + if (stateVariant.toMap().contains("logged")) { + stateType.setLoggingOverride(true); + stateType.setSuggestLogging(stateVariant.toMap().value("logged", false).toBool()); + } if (stateVariant.toMap().contains("unit")) { QMetaEnum unitEnum = QMetaEnum::fromType(); int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray()); @@ -215,7 +218,8 @@ Interface ThingUtils::loadInterface(const QString &name) InterfaceEventType stateChangeEventType; stateChangeEventType.setName(stateType.name()); stateChangeEventType.setOptional(stateType.optional()); - stateChangeEventType.setLogged(stateType.logged()); + stateChangeEventType.setSuggestLogging(stateType.suggestLogging()); + stateChangeEventType.setLoggingOverride(stateType.loggingOverride()); ParamType stateChangeEventParamType; stateChangeEventParamType.setName(stateType.name()); stateChangeEventParamType.setType(stateType.type()); @@ -238,6 +242,10 @@ Interface ThingUtils::loadInterface(const QString &name) InterfaceActionType actionType; actionType.setName(actionVariant.toMap().value("name").toString()); actionType.setOptional(actionVariant.toMap().value("optional").toBool()); +// if (actionVariant.toMap().contains("logged")) { +// actionType.setLoggingOverride(true); +// actionType.setSuggestLogging(actionVariant.toMap().value("logged").toBool()); +// } ParamTypes paramTypes; foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) { ParamType paramType; @@ -256,7 +264,10 @@ Interface ThingUtils::loadInterface(const QString &name) InterfaceEventType eventType; eventType.setName(eventVariant.toMap().value("name").toString()); eventType.setOptional(eventVariant.toMap().value("optional").toBool()); - eventType.setLogged(eventVariant.toMap().value("logged").toBool()); + if (eventVariant.toMap().contains("logged")) { + eventType.setLoggingOverride(true); + eventType.setSuggestLogging(eventVariant.toMap().value("logged").toBool()); + } ParamTypes paramTypes; foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) { ParamType paramType; diff --git a/libnymea/jsonrpc/jsonhandler.cpp b/libnymea/jsonrpc/jsonhandler.cpp index 1dc446ee..48bb5b44 100644 --- a/libnymea/jsonrpc/jsonhandler.cpp +++ b/libnymea/jsonrpc/jsonhandler.cpp @@ -29,7 +29,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "jsonhandler.h" - +#include "typeutils.h" #include "loggingcategories.h" #include @@ -207,6 +207,9 @@ void JsonHandler::registerObject(const QMetaObject &metaObject) typeName = QString("$ref:BasicType"); } else if (QString(metaProperty.typeName()).startsWith("QList")) { QString elementType = QString(metaProperty.typeName()).remove("QList<").remove(">"); + if (elementType == "EventTypeId" || elementType == "StateTypeId" || elementType == "ActionTypeId") { + elementType = "QUuid"; + } QVariant::Type variantType = QVariant::nameToType(elementType.toUtf8()); typeName = QVariantList() << enumValueName(variantTypeToBasicType(variantType)); } else { @@ -344,6 +347,18 @@ QVariant JsonHandler::pack(const QMetaObject &metaObject, const void *value) con foreach (const QUuid &entry, propertyValue.value>()) { list << entry; } + } else if (propertyTypeName == "QList") { + foreach (const EventTypeId &entry, propertyValue.value>()) { + list << entry; + } + } else if (propertyTypeName == "QList") { + foreach (const EventTypeId &entry, propertyValue.value>()) { + list << entry; + } + } else if (propertyTypeName == "QList") { + foreach (const EventTypeId &entry, propertyValue.value>()) { + list << entry; + } } else { Q_ASSERT_X(false, this->metaObject()->className(), QString("Unhandled list type: %1").arg(propertyTypeName).toUtf8()); qCWarning(dcJsonRpc()) << "Cannot pack property of unhandled list type" << propertyTypeName; @@ -455,7 +470,10 @@ QVariant JsonHandler::unpack(const QMetaObject &metaObject, const QVariant &valu intList.append(val.toInt()); } metaProperty.writeOnGadget(ptr, QVariant::fromValue(intList)); - } else if (metaProperty.typeName() == QStringLiteral("QList")) { + } else if (metaProperty.typeName() == QStringLiteral("QList") + || metaProperty.typeName() == QStringLiteral("QList") + || metaProperty.typeName() == QStringLiteral("QList") + || metaProperty.typeName() == QStringLiteral("QList")) { QList uuidList; foreach (const QVariant &val, variant.toList()) { uuidList.append(val.toUuid()); diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index 74b577ca..8db49aff 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -126,6 +126,8 @@ SOURCES += \ integrations/thingsetupinfo.cpp \ integrations/thingutils.cpp \ integrations/servicedata.cpp \ + integrations/statevaluefilters/statevaluefilter.cpp \ + integrations/statevaluefilters/statevaluefilteradaptive.cpp \ jsonrpc/jsoncontext.cpp \ jsonrpc/jsonhandler.cpp \ jsonrpc/jsonreply.cpp \ diff --git a/libnymea/types/actiontype.cpp b/libnymea/types/actiontype.cpp index cb303e23..fe5f3045 100644 --- a/libnymea/types/actiontype.cpp +++ b/libnymea/types/actiontype.cpp @@ -130,6 +130,26 @@ ActionTypes::ActionTypes(const QList &other) } } +bool ActionTypes::contains(const ActionTypeId &id) const +{ + foreach (const ActionType &actionType, *this) { + if (actionType.id() == id) { + return true; + } + } + return false; +} + +bool ActionTypes::contains(const QString &name) const +{ + foreach (const ActionType &actionType, *this) { + if (actionType.name() == name) { + return true; + } + } + return false; +} + QVariant ActionTypes::get(int index) const { return QVariant::fromValue(at(index)); @@ -160,8 +180,21 @@ ActionType ActionTypes::findById(const ActionTypeId &id) return ActionType(ActionTypeId()); } +ActionType &ActionTypes::operator[](const QString &name) +{ + int index = -1; + for (int i = 0; i < count(); i++) { + if (at(i).name() == name) { + index = i; + break; + } + } + return QList::operator[](index); +} + QDebug operator<<(QDebug dbg, const ActionType &actionType) { dbg.nospace().noquote() << "ActionType: " << actionType.name() << actionType.displayName() << actionType.id(); return dbg; } + diff --git a/libnymea/types/actiontype.h b/libnymea/types/actiontype.h index f5ef88f5..9a216ab2 100644 --- a/libnymea/types/actiontype.h +++ b/libnymea/types/actiontype.h @@ -84,10 +84,13 @@ class ActionTypes: public QList public: ActionTypes() = default; ActionTypes(const QList &other); + bool contains(const ActionTypeId &id) const; + bool contains(const QString &name) const; Q_INVOKABLE QVariant get(int index) const; Q_INVOKABLE void put(const QVariant &variant); ActionType findByName(const QString &name); ActionType findById(const ActionTypeId &id); + ActionType &operator[](const QString &name); }; Q_DECLARE_METATYPE(ActionTypes) diff --git a/libnymea/types/event.cpp b/libnymea/types/event.cpp index da3e687f..72360375 100644 --- a/libnymea/types/event.cpp +++ b/libnymea/types/event.cpp @@ -126,6 +126,16 @@ bool Event::isStateChangeEvent() const return m_isStateChangeEvent; } +bool Event::logged() const +{ + return m_logged; +} + +void Event::setLogged(bool logged) +{ + m_logged = logged; +} + /*! Compare this Event to the Event given by \a other. * Events are equal (returns true) if eventTypeId, deviceId and params match. */ bool Event::operator ==(const Event &other) const diff --git a/libnymea/types/event.h b/libnymea/types/event.h index 93590b89..bb92e445 100644 --- a/libnymea/types/event.h +++ b/libnymea/types/event.h @@ -46,6 +46,7 @@ class LIBNYMEA_EXPORT Event Q_PROPERTY(QUuid thingId READ thingId) Q_PROPERTY(QUuid deviceId READ thingId REVISION 1) Q_PROPERTY(ParamList params READ params) + Q_PROPERTY(bool logged READ logged) public: Event(); Event(const EventTypeId &eventTypeId, const ThingId &thingId, const ParamList ¶ms = ParamList(), bool isStateChangeEvent = false); @@ -65,12 +66,16 @@ public: bool isStateChangeEvent() const; + bool logged() const; + void setLogged(bool logged); + private: EventTypeId m_eventTypeId; ThingId m_thingId; ParamList m_params; bool m_isStateChangeEvent; + bool m_logged = false; }; Q_DECLARE_METATYPE(Event) QDebug operator<<(QDebug dbg, const Event &event); diff --git a/libnymea/types/eventtype.cpp b/libnymea/types/eventtype.cpp index 9001837b..42e589bb 100644 --- a/libnymea/types/eventtype.cpp +++ b/libnymea/types/eventtype.cpp @@ -110,6 +110,16 @@ void EventType::setParamTypes(const ParamTypes ¶mTypes) m_paramTypes = paramTypes; } +bool EventType::suggestLogging() const +{ + return m_logged; +} + +void EventType::setSuggestLogging(bool logged) +{ + m_logged = logged; +} + /*! Returns true if this EventType has a valid id and name */ bool EventType::isValid() const { @@ -130,11 +140,31 @@ QStringList EventType::mandatoryTypeProperties() EventTypes::EventTypes(const QList &other) { - foreach (const EventType &at, other) { - append(at); + foreach (const EventType &et, other) { + append(et); } } +bool EventTypes::contains(const EventTypeId &id) const +{ + foreach (const EventType &eventType, *this) { + if (eventType.id() == id) { + return true; + } + } + return false; +} + +bool EventTypes::contains(const QString &name) const +{ + foreach (const EventType &eventType, *this) { + if (eventType.name() == name) { + return true; + } + } + return false; +} + QVariant EventTypes::get(int index) const { return QVariant::fromValue(at(index)); @@ -164,3 +194,15 @@ EventType EventTypes::findById(const EventTypeId &id) } return EventType(EventTypeId()); } + +EventType &EventTypes::operator[](const QString &name) +{ + int index = -1; + for (int i = 0; i < count(); i++) { + if (at(i).name() == name) { + index = i; + break; + } + } + return QList::operator[](index); +} diff --git a/libnymea/types/eventtype.h b/libnymea/types/eventtype.h index cbcee65d..237bb9bb 100644 --- a/libnymea/types/eventtype.h +++ b/libnymea/types/eventtype.h @@ -64,6 +64,9 @@ public: ParamTypes paramTypes() const; void setParamTypes(const ParamTypes ¶mTypes); + bool suggestLogging() const; + void setSuggestLogging(bool logged); + bool isValid() const; static QStringList typeProperties(); @@ -75,6 +78,7 @@ private: QString m_displayName; int m_index; QList m_paramTypes; + bool m_logged = false; }; Q_DECLARE_METATYPE(EventType) @@ -85,10 +89,13 @@ class EventTypes: public QList public: EventTypes() = default; EventTypes(const QList &other); + bool contains(const EventTypeId &id) const; + bool contains(const QString &name) const; Q_INVOKABLE QVariant get(int index) const; Q_INVOKABLE void put(const QVariant &variant); EventType findByName(const QString &name); EventType findById(const EventTypeId &id); + EventType &operator[](const QString &name); }; Q_DECLARE_METATYPE(EventTypes) diff --git a/libnymea/types/interfaceeventtype.cpp b/libnymea/types/interfaceeventtype.cpp index 9d4ce6aa..6b1017d3 100644 --- a/libnymea/types/interfaceeventtype.cpp +++ b/libnymea/types/interfaceeventtype.cpp @@ -15,14 +15,14 @@ void InterfaceEventType::setOptional(bool optional) m_optional = optional; } -bool InterfaceEventType::logged() const +bool InterfaceEventType::loggingOverride() const { - return m_logged; + return m_loggingOverride; } -void InterfaceEventType::setLogged(bool logged) +void InterfaceEventType::setLoggingOverride(bool loggingOverride) { - m_logged = logged; + m_loggingOverride = loggingOverride; } InterfaceEventTypes::InterfaceEventTypes(const QList &other): diff --git a/libnymea/types/interfaceeventtype.h b/libnymea/types/interfaceeventtype.h index aa3a79c2..84eca98f 100644 --- a/libnymea/types/interfaceeventtype.h +++ b/libnymea/types/interfaceeventtype.h @@ -11,12 +11,12 @@ public: bool optional() const; void setOptional(bool optional); - bool logged() const; - void setLogged(bool logged); + bool loggingOverride() const; + void setLoggingOverride(bool loggingOverride); private: bool m_optional = false; - bool m_logged = false; + bool m_loggingOverride = false; }; class InterfaceEventTypes: public QList diff --git a/libnymea/types/interfacestatetype.cpp b/libnymea/types/interfacestatetype.cpp index e2b97191..02c72567 100644 --- a/libnymea/types/interfacestatetype.cpp +++ b/libnymea/types/interfacestatetype.cpp @@ -15,14 +15,14 @@ void InterfaceStateType::setOptional(bool optional) m_optional = optional; } -bool InterfaceStateType::logged() const +bool InterfaceStateType::loggingOverride() const { - return m_logged; + return m_loggingOverride; } -void InterfaceStateType::setLogged(bool logged) +void InterfaceStateType::setLoggingOverride(bool loggingOverride) { - m_logged = logged; + m_loggingOverride = loggingOverride; } InterfaceStateTypes::InterfaceStateTypes(const QList &other): diff --git a/libnymea/types/interfacestatetype.h b/libnymea/types/interfacestatetype.h index 88429916..946ea404 100644 --- a/libnymea/types/interfacestatetype.h +++ b/libnymea/types/interfacestatetype.h @@ -11,12 +11,12 @@ public: bool optional() const; void setOptional(bool optional); - bool logged() const; - void setLogged(bool logged); + bool loggingOverride() const; + void setLoggingOverride(bool loggingOverride); private: bool m_optional = false; - bool m_logged = false; + bool m_loggingOverride = false; }; class InterfaceStateTypes: public QList diff --git a/libnymea/types/state.cpp b/libnymea/types/state.cpp index 3717bf85..20cba66f 100644 --- a/libnymea/types/state.cpp +++ b/libnymea/types/state.cpp @@ -90,16 +90,6 @@ void State::setFilter(Types::StateValueFilter filter) m_filter = filter; } -bool State::logged() const -{ - return m_logged; -} - -void State::setLogged(bool logged) -{ - m_logged = logged; -} - /*! Writes the stateTypeId, the deviceId and the value of the given \a state to \a dbg. */ QDebug operator<<(QDebug dbg, const State &state) { diff --git a/libnymea/types/state.h b/libnymea/types/state.h index 754fd843..a5d77d3a 100644 --- a/libnymea/types/state.h +++ b/libnymea/types/state.h @@ -43,7 +43,6 @@ class LIBNYMEA_EXPORT State Q_PROPERTY(QUuid stateTypeId READ stateTypeId) Q_PROPERTY(QVariant value READ value) Q_PROPERTY(Types::StateValueFilter filter READ filter) - Q_PROPERTY(bool logged READ logged) public: State(); @@ -58,15 +57,11 @@ public: Types::StateValueFilter filter() const; void setFilter(Types::StateValueFilter filter); - bool logged() const; - void setLogged(bool logged); - private: StateTypeId m_stateTypeId; ThingId m_thingId; QVariant m_value; - Types::StateValueFilter m_filter; - bool m_logged = false; + Types::StateValueFilter m_filter = Types::StateValueFilterNone; }; Q_DECLARE_METATYPE(State) diff --git a/libnymea/types/statetype.cpp b/libnymea/types/statetype.cpp index d566752c..74b70ad5 100644 --- a/libnymea/types/statetype.cpp +++ b/libnymea/types/statetype.cpp @@ -208,6 +208,16 @@ void StateType::setCached(bool cached) m_cached = cached; } +bool StateType::suggestLogging() const +{ + return m_logged; +} + +void StateType::setSuggestLogging(bool logged) +{ + m_logged = logged; +} + Types::StateValueFilter StateType::filter() const { return m_filter; @@ -223,7 +233,7 @@ QStringList StateType::typeProperties() { return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue" << "cached" << "unit" << "minValue" << "maxValue" << "possibleValues" << "writable" - << "displayNameAction" << "ioType"; + << "displayNameAction" << "ioType" << "logged"; } /*! Returns a list of mandatory properties a DeviceClass definition must have. */ @@ -255,6 +265,16 @@ bool StateTypes::contains(const StateTypeId &stateTypeId) return false; } +bool StateTypes::contains(const QString &name) +{ + foreach (const StateType &stateType, *this) { + if (stateType.name() == name) { + return true; + } + } + return false; +} + QVariant StateTypes::get(int index) const { return QVariant::fromValue(at(index)); @@ -284,3 +304,15 @@ StateType StateTypes::findById(const StateTypeId &id) } return StateType(StateTypeId()); } + +StateType &StateTypes::operator[](const QString &name) +{ + int index = -1; + for (int i = 0; i < count(); i++) { + if (at(i).name() == name) { + index = i; + break; + } + } + return QList::operator[](index); +} diff --git a/libnymea/types/statetype.h b/libnymea/types/statetype.h index 0b542360..b1504e55 100644 --- a/libnymea/types/statetype.h +++ b/libnymea/types/statetype.h @@ -93,8 +93,8 @@ public: bool cached() const; void setCached(bool cached); - bool logged() const; - void setLogged(bool logged); + bool suggestLogging() const; + void setSuggestLogging(bool logged); Types::StateValueFilter filter() const; void setFilter(Types::StateValueFilter filter); @@ -131,10 +131,12 @@ public: StateTypes() = default; StateTypes(const QList &other); bool contains(const StateTypeId &stateTypeId); + bool contains(const QString &name); Q_INVOKABLE QVariant get(int index) const; Q_INVOKABLE void put(const QVariant &variant); StateType findByName(const QString &name); StateType findById(const StateTypeId &id); + StateType &operator[](const QString &name); }; Q_DECLARE_METATYPE(StateTypes) From eeb1feade05e8f82d47bc1eebafb5d05b1c44564 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 2 Jan 2021 14:32:03 +0100 Subject: [PATCH 06/12] Add a jitter filtering mechanism --- .../thingmanagerimplementation.cpp | 6 +- libnymea/integrations/pluginmetadata.cpp | 20 ++- .../statevaluefilters/statevaluefilter.cpp | 9 + .../statevaluefilters/statevaluefilter.h | 3 + .../statevaluefilteradaptive.cpp | 161 +++++++++++++++++- .../statevaluefilteradaptive.h | 19 +++ libnymea/types/statetype.cpp | 14 -- libnymea/types/statetype.h | 3 - plugins/mock/extern-plugininfo.h | 7 +- plugins/mock/integrationpluginmock.json | 3 +- plugins/mock/plugininfo.h | 7 +- 11 files changed, 227 insertions(+), 25 deletions(-) diff --git a/libnymea-core/integrations/thingmanagerimplementation.cpp b/libnymea-core/integrations/thingmanagerimplementation.cpp index 14e7a992..6ec1820d 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.cpp +++ b/libnymea-core/integrations/thingmanagerimplementation.cpp @@ -1796,7 +1796,7 @@ void ThingManagerImplementation::slotThingStateValueChanged(const StateTypeId &s Param valueParam(ParamTypeId(stateTypeId.toString()), value); Event event(EventTypeId(stateTypeId.toString()), thing->id(), ParamList() << valueParam, true); - emit eventTriggered(event); + onEventTriggered(event); syncIOConnection(thing, stateTypeId); } @@ -2063,6 +2063,8 @@ void ThingManagerImplementation::loadThingStates(Thing *thing) } else { thing->setStateValue(stateType.id(), stateType.defaultValue()); } + qWarning() << "-----" << stateType.name() << stateType.filter(); + thing->setStateValueFilter(stateType.id(), stateType.filter()); } settings.endGroup(); } @@ -2220,7 +2222,7 @@ IntegrationPlugin *ThingManagerImplementation::createCppIntegrationPlugin(const return nullptr; } - pluginIface->setMetaData(PluginMetadata(pluginInfo)); + pluginIface->setMetaData(metaData); return pluginIface; } diff --git a/libnymea/integrations/pluginmetadata.cpp b/libnymea/integrations/pluginmetadata.cpp index 5e2b1f7d..5bbd997e 100644 --- a/libnymea/integrations/pluginmetadata.cpp +++ b/libnymea/integrations/pluginmetadata.cpp @@ -318,7 +318,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) QJsonObject st = stateTypesJson.toObject(); bool writableState = false; - QPair verificationResult = verifyFields(StateType::typeProperties(), StateType::mandatoryTypeProperties(), st); + QStringList stateTypeProperties = {"id", "name", "displayName", "displayNameEvent", "type", "defaultValue", "cached", + "unit", "minValue", "maxValue", "possibleValues", "writable", "displayNameAction", + "ioType", "logged", "filter"}; + QStringList mandatoryStateTypeProperties = {"id", "name", "displayName", "displayNameEvent", "type", "defaultValue"}; + QPair verificationResult = verifyFields(stateTypeProperties, mandatoryStateTypeProperties, st); // Check mandatory fields if (!verificationResult.first.isEmpty()) { @@ -469,7 +473,19 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) break; } stateType.setIOType(ioType); - stateType.setSuggestLogging(st.value("suggestLogging").toBool()); + } + + stateType.setSuggestLogging(st.value("suggestLogging").toBool()); + + if (st.contains("filter")) { + QString filter = st.value("filter").toString(); + if (filter == "adaptive") { + qWarning() << "++++++++++++++++++++++++++++" << stateType.name(); + stateType.setFilter(Types::StateValueFilterAdaptive); + } else if (!filter.isEmpty()) { + m_validationErrors.append("Thing class \"" + thingClass.name() + "\" state type \"" + stateTypeName + "\" has invalid filter value \"" + filter + "\". Supported filters are: \"adaptive\""); + hasError = true; + } } stateTypes.append(stateType); diff --git a/libnymea/integrations/statevaluefilters/statevaluefilter.cpp b/libnymea/integrations/statevaluefilters/statevaluefilter.cpp index 68e88626..c1f37c32 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilter.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilter.cpp @@ -1,6 +1,15 @@ #include "statevaluefilter.h" +#include "loggingcategories.h" + +NYMEA_LOGGING_CATEGORY(dcStateValueFilter, "StateValueFilter") + StateValueFilter::StateValueFilter() { } + +StateValueFilter::~StateValueFilter() +{ + +} diff --git a/libnymea/integrations/statevaluefilters/statevaluefilter.h b/libnymea/integrations/statevaluefilters/statevaluefilter.h index 83b59a88..bd1e2da0 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilter.h +++ b/libnymea/integrations/statevaluefilters/statevaluefilter.h @@ -2,6 +2,9 @@ #define STATEVALUEFILTER_H #include +#include + +Q_DECLARE_LOGGING_CATEGORY(dcStateValueFilter) class StateValueFilter { diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp index 26f5217b..c2273ffb 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp @@ -1,5 +1,7 @@ #include "statevaluefilteradaptive.h" +#include + StateValueFilterAdaptive::StateValueFilterAdaptive() { @@ -7,5 +9,162 @@ StateValueFilterAdaptive::StateValueFilterAdaptive() void StateValueFilterAdaptive::addValue(const QVariant &value) { - + qCDebug(dcStateValueFilter()) << "Adding value:" << value.toDouble(); + m_values.prepend(value.toDouble()); + m_inputValues++; + update(); +} + +QVariant StateValueFilterAdaptive::filteredValue() const +{ + return m_filteredValue; +} + +void StateValueFilterAdaptive::update() +{ + +// while (m_values.count() > m_windowSize + 1) { +// m_values.removeLast(); +// } + +// if (m_values.isEmpty()) { +// m_filteredValue = 0; +// return; +// } + +// if (m_values.count() == 1) { +// m_filteredValue = m_values.first(); +// m_outputValues++; +// return; +// } + + + +//// m_filteredValue = m_values.first(); +//// m_outputValues++; + + +// double currentValue = m_values.first(); +// if (currentValue == 0) { +// m_filteredValue = 0; +// return; +// } + + +// // Calculate average of history, for all values and for all but the last one +// double sum = 0; +// for (int i = 1; i < m_values.count(); i++) { +// sum += m_values.at(i); +// } +// double average = sum / (m_values.count() - 1); + +// double absoluteJitter = currentValue - average; +// double relativeJitter = absoluteJitter / currentValue; + + +// // Outside of jitter window... Forward value directly +// if (qAbs(relativeJitter) > m_averageJitter * 3) { +// m_filteredValue = m_values.first(); +// m_values.clear(); +// m_values.prepend(m_filteredValue); +// m_outputValues++; +// qCDebug(dcStateValueFilter()) << "Updating output"; +// } else { + +// } +// // Adjust average jitter +// m_averageJitter = ((m_averageJitter * m_windowSize) + qAbs(relativeJitter)) / (m_windowSize + 1); + + +// qCDebug(dcStateValueFilter()) << "input" << currentValue << "output" << m_filteredValue << "average" << average << "jitter:" << absoluteJitter << "relative" << relativeJitter << "avg" << m_averageJitter; +// qCDebug(dcStateValueFilter()) << "Filter input values:" << m_inputValues << "output values:" << m_outputValues << "compression ratio:" << (1.0 * m_inputValues / m_outputValues); + + +// return; + + + + + + while (m_values.count() > m_windowSize) { + m_values.removeLast(); + } + + if (m_values.isEmpty()) { + m_filteredValue = 0; + return; + } + + if (m_values.count() == 1) { + // Not enough data + m_filteredValue = m_values.first(); + m_outputValues++; + return; + } + + + // Calculate average of history, for all values and for all but the last one + double sum = 0; + for (int i = 0; i < m_values.count(); i++) { + sum += m_values.at(i); + } + + double currentValue = m_values.first(); + if (qFuzzyCompare(currentValue, 0)) { + m_filteredValue = 0; + return; + } + + double filteredValue = sum / m_values.count(); + double previousFilteredValue = (sum - m_values.first()) / (m_values.count() - 1); + + if (qFuzzyCompare(previousFilteredValue, 0)) { + m_filteredValue = m_values.first(); + m_outputValues++; + return; + } + + // Calculate change ratio of the last value compared to the previous one, unflitered and filtered + double changeRatio = 1 - qAbs(currentValue / previousFilteredValue); + double changeRatioFiltered = 1 - qAbs(filteredValue / previousFilteredValue); + + // Add deviation of actual value vs filtered value up to have an idea how much we're off + m_totalDeviation += changeRatioFiltered - changeRatio; + + + // If the unfiltered value changes for more than 3 times the standard deviation of the jittering values + // it's a 99% chance a big change happened that's not jitter (e.g turned on/off) + // Discard the history and follow the new value right away + if (qAbs(changeRatio) > m_standardDeviation * 3) { + m_values.clear(); + m_values.prepend(currentValue); + m_totalDeviation = 0; + if (!qFuzzyCompare(m_filteredValue, filteredValue)) { + m_filteredValue = currentValue; + qCDebug(dcStateValueFilter()) << "Updating output value:" << m_filteredValue << "(input exceeds max jitter)"; + m_outputValues++; + } + + // If the filtered value changed for 5 percent or more, follow slowly + // In order to not get stuck on being off for 5% forever, also move closer + // to the new value when the deviation exceeds max deviation + } else if (qAbs(changeRatioFiltered) > m_standardDeviation || qAbs(m_totalDeviation) > m_maxTotalDeviation) { + m_totalDeviation = 0; + if (!qFuzzyCompare(m_filteredValue, filteredValue)) { + qCDebug(dcStateValueFilter()) << "Updating output value:" << filteredValue << "(drift compensation)"; + m_filteredValue = filteredValue; + m_outputValues++; + } + } + + // Poor mans solution to calculate standard deviation. Not as precise, but much faster than looping over history again + m_standardDeviation = ((m_standardDeviation * m_windowSize) + qAbs(changeRatio)) / (m_windowSize + 1); + qWarning(dcStateValueFilter()) << "New:" << currentValue << "Old:" << previousFilteredValue << "Filtered:" << filteredValue << "ratio:" << changeRatio << "filteredRatio" << changeRatioFiltered << "deviation" << m_totalDeviation << "averageJitter" << m_averageJitter; + + // correct stats on overflow of counters + if (m_inputValues < m_outputValues) { + m_outputValues = 0; + } + + qCDebug(dcStateValueFilter()) << "Filter input values:" << m_inputValues << "output values:" << m_outputValues << "compression ratio:" << (1.0 * m_inputValues / m_outputValues); } diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h index dbb85b84..c9ce5b6e 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h @@ -10,6 +10,25 @@ public: void addValue(const QVariant &value) override; QVariant filteredValue() const override; + +private: + void update(); + +private: + QList m_values; + + int m_windowSize = 20; + double m_standardDeviation = 0.05; + double m_maxTotalDeviation = 1; + + double m_filteredValue = 0; + double m_totalDeviation = 0; + + // Stats for debugging + quint64 m_inputValues = 0; + quint64 m_outputValues = 0; + + }; #endif // STATEVALUEFILTERADAPTIVE_H diff --git a/libnymea/types/statetype.cpp b/libnymea/types/statetype.cpp index 74b70ad5..997e7592 100644 --- a/libnymea/types/statetype.cpp +++ b/libnymea/types/statetype.cpp @@ -228,20 +228,6 @@ void StateType::setFilter(Types::StateValueFilter filter) m_filter = filter; } -/*! Returns a list of all valid properties a DeviceClass definition can have. */ -QStringList StateType::typeProperties() -{ - return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue" - << "cached" << "unit" << "minValue" << "maxValue" << "possibleValues" << "writable" - << "displayNameAction" << "ioType" << "logged"; -} - -/*! Returns a list of mandatory properties a DeviceClass definition must have. */ -QStringList StateType::mandatoryTypeProperties() -{ - return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue"; -} - /*! Returns true if this state type has an ID, a type and a name set. */ bool StateType::isValid() const { diff --git a/libnymea/types/statetype.h b/libnymea/types/statetype.h index b1504e55..aff27735 100644 --- a/libnymea/types/statetype.h +++ b/libnymea/types/statetype.h @@ -99,9 +99,6 @@ public: Types::StateValueFilter filter() const; void setFilter(Types::StateValueFilter filter); - static QStringList typeProperties(); - static QStringList mandatoryTypeProperties(); - bool isValid() const; private: diff --git a/plugins/mock/extern-plugininfo.h b/plugins/mock/extern-plugininfo.h index b7ba912a..e31016e9 100644 --- a/plugins/mock/extern-plugininfo.h +++ b/plugins/mock/extern-plugininfo.h @@ -1,5 +1,10 @@ /* This file is generated by the nymea build system. Any changes to this file will * - * be lost. If you want to change this file, edit the plugin's json file. */ + * be lost. If you want to change this file, edit the plugin's json file. * + * * + * NOTE: This file can be included only once per plugin. If you need to access * + * definitions from this file in multiple source files, use * + * #include extern-plugininfo.h * + * instead and re-run qmake. */ #ifndef EXTERNPLUGININFO_H #define EXTERNPLUGININFO_H diff --git a/plugins/mock/integrationpluginmock.json b/plugins/mock/integrationpluginmock.json index c3afe9e9..9bf6fd6c 100644 --- a/plugins/mock/integrationpluginmock.json +++ b/plugins/mock/integrationpluginmock.json @@ -152,7 +152,8 @@ "minValue": 0, "maxValue": 100, "defaultValue": 50, - "writable": true + "writable": true, + "filter": "adaptive" }, { "id": "ebc41327-53d5-40c2-8e7b-1164a8ff359e", diff --git a/plugins/mock/plugininfo.h b/plugins/mock/plugininfo.h index 51ee9c17..e7b3de50 100644 --- a/plugins/mock/plugininfo.h +++ b/plugins/mock/plugininfo.h @@ -1,5 +1,10 @@ /* This file is generated by the nymea build system. Any changes to this file will * - * be lost. If you want to change this file, edit the plugin's json file. */ + * be lost. If you want to change this file, edit the plugin's json file. * + * * + * NOTE: This file can be included only once per plugin. If you need to access * + * definitions from this file in multiple source files, use * + * #include extern-plugininfo.h * + * instead and re-run qmake. */ #ifndef PLUGININFO_H #define PLUGININFO_H From 32eb6d6dd7cdc9c4657d60036c2e77defb278d7d Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 3 Jan 2021 21:54:25 +0100 Subject: [PATCH 07/12] tune filter --- .../thingmanagerimplementation.cpp | 1 - libnymea/integrations/pluginmetadata.cpp | 1 - .../statevaluefilteradaptive.cpp | 168 ++++++------------ .../statevaluefilteradaptive.h | 11 +- 4 files changed, 62 insertions(+), 119 deletions(-) diff --git a/libnymea-core/integrations/thingmanagerimplementation.cpp b/libnymea-core/integrations/thingmanagerimplementation.cpp index 6ec1820d..8a0f257f 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.cpp +++ b/libnymea-core/integrations/thingmanagerimplementation.cpp @@ -2063,7 +2063,6 @@ void ThingManagerImplementation::loadThingStates(Thing *thing) } else { thing->setStateValue(stateType.id(), stateType.defaultValue()); } - qWarning() << "-----" << stateType.name() << stateType.filter(); thing->setStateValueFilter(stateType.id(), stateType.filter()); } settings.endGroup(); diff --git a/libnymea/integrations/pluginmetadata.cpp b/libnymea/integrations/pluginmetadata.cpp index 5bbd997e..bbf08c08 100644 --- a/libnymea/integrations/pluginmetadata.cpp +++ b/libnymea/integrations/pluginmetadata.cpp @@ -480,7 +480,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) if (st.contains("filter")) { QString filter = st.value("filter").toString(); if (filter == "adaptive") { - qWarning() << "++++++++++++++++++++++++++++" << stateType.name(); stateType.setFilter(Types::StateValueFilterAdaptive); } else if (!filter.isEmpty()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" state type \"" + stateTypeName + "\" has invalid filter value \"" + filter + "\". Supported filters are: \"adaptive\""); diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp index c2273ffb..63b0af84 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp @@ -10,161 +10,105 @@ StateValueFilterAdaptive::StateValueFilterAdaptive() void StateValueFilterAdaptive::addValue(const QVariant &value) { qCDebug(dcStateValueFilter()) << "Adding value:" << value.toDouble(); - m_values.prepend(value.toDouble()); - m_inputValues++; + m_inputValues.prepend(value.toDouble()); + m_inputValueCount++; update(); } QVariant StateValueFilterAdaptive::filteredValue() const { - return m_filteredValue; + return m_outputValue; } void StateValueFilterAdaptive::update() { - -// while (m_values.count() > m_windowSize + 1) { -// m_values.removeLast(); -// } - -// if (m_values.isEmpty()) { -// m_filteredValue = 0; -// return; -// } - -// if (m_values.count() == 1) { -// m_filteredValue = m_values.first(); -// m_outputValues++; -// return; -// } - - - -//// m_filteredValue = m_values.first(); -//// m_outputValues++; - - -// double currentValue = m_values.first(); -// if (currentValue == 0) { -// m_filteredValue = 0; -// return; -// } - - -// // Calculate average of history, for all values and for all but the last one -// double sum = 0; -// for (int i = 1; i < m_values.count(); i++) { -// sum += m_values.at(i); -// } -// double average = sum / (m_values.count() - 1); - -// double absoluteJitter = currentValue - average; -// double relativeJitter = absoluteJitter / currentValue; - - -// // Outside of jitter window... Forward value directly -// if (qAbs(relativeJitter) > m_averageJitter * 3) { -// m_filteredValue = m_values.first(); -// m_values.clear(); -// m_values.prepend(m_filteredValue); -// m_outputValues++; -// qCDebug(dcStateValueFilter()) << "Updating output"; -// } else { - -// } -// // Adjust average jitter -// m_averageJitter = ((m_averageJitter * m_windowSize) + qAbs(relativeJitter)) / (m_windowSize + 1); - - -// qCDebug(dcStateValueFilter()) << "input" << currentValue << "output" << m_filteredValue << "average" << average << "jitter:" << absoluteJitter << "relative" << relativeJitter << "avg" << m_averageJitter; -// qCDebug(dcStateValueFilter()) << "Filter input values:" << m_inputValues << "output values:" << m_outputValues << "compression ratio:" << (1.0 * m_inputValues / m_outputValues); - - -// return; - - - - - - while (m_values.count() > m_windowSize) { - m_values.removeLast(); + while (m_inputValues.count() > m_windowSize) { + m_inputValues.removeLast(); } - if (m_values.isEmpty()) { - m_filteredValue = 0; + if (m_inputValues.isEmpty()) { + m_outputValue = 0; return; } - if (m_values.count() == 1) { + if (m_inputValues.count() == 1) { // Not enough data - m_filteredValue = m_values.first(); - m_outputValues++; + m_outputValue = m_inputValues.first(); + m_outputValueCount++; return; } + double currentValue = m_inputValues.first(); + if (qFuzzyCompare(currentValue, 0)) { + m_outputValue = 0; + return; + } // Calculate average of history, for all values and for all but the last one double sum = 0; - for (int i = 0; i < m_values.count(); i++) { - sum += m_values.at(i); + for (int i = 0; i < m_inputValues.count(); i++) { + sum += m_inputValues.at(i); } - double currentValue = m_values.first(); - if (qFuzzyCompare(currentValue, 0)) { - m_filteredValue = 0; - return; - } - - double filteredValue = sum / m_values.count(); - double previousFilteredValue = (sum - m_values.first()) / (m_values.count() - 1); + double filteredValue = sum / m_inputValues.count(); + double previousFilteredValue = (sum - m_inputValues.first()) / (m_inputValues.count() - 1); if (qFuzzyCompare(previousFilteredValue, 0)) { - m_filteredValue = m_values.first(); - m_outputValues++; + m_outputValue = m_inputValues.first(); + m_outputValueCount++; return; } // Calculate change ratio of the last value compared to the previous one, unflitered and filtered - double changeRatio = 1 - qAbs(currentValue / previousFilteredValue); + double changeRatioToAverage = 1 - qAbs(currentValue / previousFilteredValue); + double changeRatioToCurrentOutput = 1 - qAbs(currentValue / m_outputValue); double changeRatioFiltered = 1 - qAbs(filteredValue / previousFilteredValue); - // Add deviation of actual value vs filtered value up to have an idea how much we're off - m_totalDeviation += changeRatioFiltered - changeRatio; - // If the unfiltered value changes for more than 3 times the standard deviation of the jittering values // it's a 99% chance a big change happened that's not jitter (e.g turned on/off) // Discard the history and follow the new value right away - if (qAbs(changeRatio) > m_standardDeviation * 3) { - m_values.clear(); - m_values.prepend(currentValue); + if (qAbs(changeRatioToAverage) > m_standardDeviation * 3) { + m_inputValues.clear(); + m_inputValues.prepend(currentValue); m_totalDeviation = 0; - if (!qFuzzyCompare(m_filteredValue, filteredValue)) { - m_filteredValue = currentValue; - qCDebug(dcStateValueFilter()) << "Updating output value:" << m_filteredValue << "(input exceeds max jitter)"; - m_outputValues++; + if (!qFuzzyCompare(m_outputValue, filteredValue)) { + m_outputValue = currentValue; + qCDebug(dcStateValueFilter()) << "Updating output value:" << m_outputValue << "(input exceeds max jitter)"; + m_outputValueCount++; } - // If the filtered value changed for 5 percent or more, follow slowly - // In order to not get stuck on being off for 5% forever, also move closer - // to the new value when the deviation exceeds max deviation - } else if (qAbs(changeRatioFiltered) > m_standardDeviation || qAbs(m_totalDeviation) > m_maxTotalDeviation) { - m_totalDeviation = 0; - if (!qFuzzyCompare(m_filteredValue, filteredValue)) { - qCDebug(dcStateValueFilter()) << "Updating output value:" << filteredValue << "(drift compensation)"; - m_filteredValue = filteredValue; - m_outputValues++; + // We're considering it jitter + } else { + // Add up the deviation from the current actual value to the currently filtered value + m_totalDeviation += changeRatioToCurrentOutput; + + // If the filtered value changed for more than the the standard deviation, follow slowly + // In order to not get stuck on being off for the standard deviation forever, also move closer + // to the new value when the summed up deviation exceeds the maximum allowed total deviation + if (qAbs(changeRatioFiltered) > m_standardDeviation || qAbs(m_totalDeviation) > m_maxTotalDeviation) { + m_totalDeviation = 0; + if (!qFuzzyCompare(m_outputValue, filteredValue)) { + qCDebug(dcStateValueFilter()) << "Updating output value:" << filteredValue << "(drift compensation)"; + m_outputValue = filteredValue; + m_outputValueCount++; + } } + + // Poor mans solution to calculate standard deviation. Not as precise, but much faster than looping over history again + m_standardDeviation = ((m_standardDeviation * m_windowSize) + qAbs(changeRatioToAverage)) / (m_windowSize + 1); } - // Poor mans solution to calculate standard deviation. Not as precise, but much faster than looping over history again - m_standardDeviation = ((m_standardDeviation * m_windowSize) + qAbs(changeRatio)) / (m_windowSize + 1); - qWarning(dcStateValueFilter()) << "New:" << currentValue << "Old:" << previousFilteredValue << "Filtered:" << filteredValue << "ratio:" << changeRatio << "filteredRatio" << changeRatioFiltered << "deviation" << m_totalDeviation << "averageJitter" << m_averageJitter; + // correct stats on overflow of counters - if (m_inputValues < m_outputValues) { - m_outputValues = 0; + if (m_inputValueCount < m_outputValueCount) { + m_outputValueCount = 0; } - qCDebug(dcStateValueFilter()) << "Filter input values:" << m_inputValues << "output values:" << m_outputValues << "compression ratio:" << (1.0 * m_inputValues / m_outputValues); + qCDebug(dcStateValueFilter()) << "Filter statistics for" << this; + qCDebug(dcStateValueFilter()) << "Input:" << currentValue << "AVG:" << previousFilteredValue << "Filtered:" << filteredValue; + qCDebug(dcStateValueFilter()) << "Change ratios: Input/average:" << changeRatioToAverage << "Filtered/average:" << changeRatioFiltered << "Input/output:" << changeRatioToCurrentOutput; + qCDebug(dcStateValueFilter()) << "Std deviation:" << m_standardDeviation << "Total deviation:" << m_totalDeviation; + qCDebug(dcStateValueFilter()) << "Compression ratio:" << (1.0 * m_inputValueCount / m_outputValueCount) << "(" << m_outputValueCount << "/" << m_inputValueCount << ")"; } diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h index c9ce5b6e..bc07f899 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h @@ -15,18 +15,19 @@ private: void update(); private: - QList m_values; + QList m_inputValues; int m_windowSize = 20; double m_standardDeviation = 0.05; - double m_maxTotalDeviation = 1; + double m_maxTotalDeviation = 0.4; - double m_filteredValue = 0; double m_totalDeviation = 0; + double m_outputValue = 0; + // Stats for debugging - quint64 m_inputValues = 0; - quint64 m_outputValues = 0; + quint64 m_inputValueCount = 0; + quint64 m_outputValueCount = 0; }; From 3fb74cc9e4c4fab223401d334f0867f481ad4759 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 4 Jan 2021 16:43:28 +0100 Subject: [PATCH 08/12] Add API to configure logging/filtering by the client --- .../thingmanagerimplementation.cpp | 41 +++++++++++++++++++ .../integrations/thingmanagerimplementation.h | 3 ++ libnymea-core/jsonrpc/integrationshandler.cpp | 36 ++++++++++++++++ libnymea-core/jsonrpc/integrationshandler.h | 2 + .../statevaluefilteradaptive.cpp | 30 +++++++------- libnymea/integrations/thingmanager.h | 5 ++- libnymea/types/event.h | 1 - 7 files changed, 100 insertions(+), 18 deletions(-) diff --git a/libnymea-core/integrations/thingmanagerimplementation.cpp b/libnymea-core/integrations/thingmanagerimplementation.cpp index 8a0f257f..f87c78d3 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.cpp +++ b/libnymea-core/integrations/thingmanagerimplementation.cpp @@ -506,6 +506,47 @@ Thing::ThingError ThingManagerImplementation::setThingSettings(const ThingId &th return Thing::ThingErrorNoError; } +Thing::ThingError ThingManagerImplementation::setEventLogging(const ThingId &thingId, const EventTypeId &eventTypeId, bool enabled) +{ + Thing *thing = m_configuredThings.value(thingId); + if (!thing) { + qCWarning(dcThingManager()) << "Cannot configure event logging. Thing" << thingId.toString() << "not found"; + return Thing::ThingErrorThingNotFound; + } + if (!thing->thingClass().eventTypes().findById(eventTypeId).isValid()) { + qCWarning(dcThingManager()) << "Cannot configure event logging. Thing" << thingId.toString() << "has no event type with id" << eventTypeId; + return Thing::ThingErrorEventTypeNotFound; + } + QList loggedEventTypes = thing->loggedEventTypeIds(); + if (enabled && !loggedEventTypes.contains(eventTypeId)) { + loggedEventTypes.append(eventTypeId); + thing->setLoggedEventTypeIds(loggedEventTypes); + emit thingChanged(thing); + } else if (!enabled && loggedEventTypes.contains(eventTypeId)) { + loggedEventTypes.removeAll(eventTypeId); + thing->setLoggedEventTypeIds(loggedEventTypes); + emit thingChanged(thing); + } + return Thing::ThingErrorNoError; +} + +Thing::ThingError ThingManagerImplementation::setStateFilter(const ThingId &thingId, const StateTypeId &stateTypeId, Types::StateValueFilter filter) +{ + Thing *thing = m_configuredThings.value(thingId); + if (!thing) { + qCWarning(dcThingManager()) << "Cannot configure state filter. Thing" << thingId.toString() << "not found"; + return Thing::ThingErrorThingNotFound; + } + if (!thing->thingClass().stateTypes().findById(stateTypeId).isValid()) { + qCWarning(dcThingManager()) << "Cannot configure state filter. Thing" << thingId.toString() << "has no state type with id" << stateTypeId; + return Thing::ThingErrorEventTypeNotFound; + } + + thing->setStateValueFilter(stateTypeId, filter); + emit thingChanged(thing); + return Thing::ThingErrorNoError; +} + ThingPairingInfo* ThingManagerImplementation::pairThing(const ThingClassId &thingClassId, const ParamList ¶ms, const QString &name) { PairingTransactionId transactionId = PairingTransactionId::createPairingTransactionId(); diff --git a/libnymea-core/integrations/thingmanagerimplementation.h b/libnymea-core/integrations/thingmanagerimplementation.h index df742069..61f87407 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.h +++ b/libnymea-core/integrations/thingmanagerimplementation.h @@ -106,6 +106,9 @@ public: Thing::ThingError editThing(const ThingId &thingId, const QString &name) override; Thing::ThingError setThingSettings(const ThingId &thingId, const ParamList &settings) override; + Thing::ThingError setEventLogging(const ThingId &thingId, const EventTypeId &eventTypeId, bool enabled) override; + Thing::ThingError setStateFilter(const ThingId &thingId, const StateTypeId &stateTypeId, Types::StateValueFilter filter) override; + Thing::ThingError removeConfiguredThing(const ThingId &thingId) override; ThingActionInfo* executeAction(const Action &action) override; diff --git a/libnymea-core/jsonrpc/integrationshandler.cpp b/libnymea-core/jsonrpc/integrationshandler.cpp index d42e7e3c..3110f746 100644 --- a/libnymea-core/jsonrpc/integrationshandler.cpp +++ b/libnymea-core/jsonrpc/integrationshandler.cpp @@ -242,6 +242,22 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa returns.insert("thingError", enumRef()); registerMethod("SetThingSettings", description, params, returns); + params.clear(); returns.clear(); + description = "Enable/disable logging for the given event type on the given thing."; + params.insert("thingId", enumValueName(Uuid)); + params.insert("eventTypeId", enumValueName(Uuid)); + params.insert("enabled", enumValueName(Bool)); + returns.insert("thingError", enumRef()); + registerMethod("SetEventLogging", description, params, returns); + + params.clear(); returns.clear(); + description = "Set the filter for the given state on the given thing."; + params.insert("thingId", enumValueName(Uuid)); + params.insert("stateTypeId", enumValueName(Uuid)); + params.insert("filter", enumRef()); + returns.insert("thingError", enumRef()); + registerMethod("SetStateFilter", description, params, returns); + params.clear(); returns.clear(); description = "Remove a thing from the system."; params.insert("thingId", enumValueName(Uuid)); @@ -839,6 +855,26 @@ JsonReply *IntegrationsHandler::SetThingSettings(const QVariantMap ¶ms) return createReply(statusToReply(status)); } +JsonReply *IntegrationsHandler::SetEventLogging(const QVariantMap ¶ms) +{ + ThingId thingId = ThingId(params.value("thingId").toString()); + EventTypeId eventTypeId = EventTypeId(params.value("eventTypeId").toUuid()); + bool enabled = params.value("enabled").toBool(); + Thing::ThingError status = NymeaCore::instance()->thingManager()->setEventLogging(thingId, eventTypeId, enabled); + return createReply(statusToReply(status)); +} + +JsonReply *IntegrationsHandler::SetStateFilter(const QVariantMap ¶ms) +{ + ThingId thingId = ThingId(params.value("thingId").toString()); + StateTypeId stateTypeId = StateTypeId(params.value("stateTypeId").toUuid()); + QString filterString = params.value("filter").toString(); + QMetaEnum metaEnum = QMetaEnum::fromType(); + Types::StateValueFilter filter = static_cast(metaEnum.keyToValue(filterString.toUtf8())); + Thing::ThingError status = NymeaCore::instance()->thingManager()->setStateFilter(thingId, stateTypeId, filter); + return createReply(statusToReply(status)); +} + JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const { ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString())); diff --git a/libnymea-core/jsonrpc/integrationshandler.h b/libnymea-core/jsonrpc/integrationshandler.h index f61233f0..98046db2 100644 --- a/libnymea-core/jsonrpc/integrationshandler.h +++ b/libnymea-core/jsonrpc/integrationshandler.h @@ -59,6 +59,8 @@ public: Q_INVOKABLE JsonReply *EditThing(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *RemoveThing(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *SetThingSettings(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply *SetEventLogging(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply *SetStateFilter(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const; Q_INVOKABLE JsonReply *GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const; diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp index 63b0af84..575ed439 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp @@ -9,7 +9,6 @@ StateValueFilterAdaptive::StateValueFilterAdaptive() void StateValueFilterAdaptive::addValue(const QVariant &value) { - qCDebug(dcStateValueFilter()) << "Adding value:" << value.toDouble(); m_inputValues.prepend(value.toDouble()); m_inputValueCount++; update(); @@ -40,6 +39,7 @@ void StateValueFilterAdaptive::update() double currentValue = m_inputValues.first(); if (qFuzzyCompare(currentValue, 0)) { + // If we went to 0, follow right away. m_outputValue = 0; return; } @@ -49,20 +49,20 @@ void StateValueFilterAdaptive::update() for (int i = 0; i < m_inputValues.count(); i++) { sum += m_inputValues.at(i); } + double normalizedValue = sum / m_inputValues.count(); + double previousNormalizedValue = (sum - m_inputValues.first()) / (m_inputValues.count() - 1); - double filteredValue = sum / m_inputValues.count(); - double previousFilteredValue = (sum - m_inputValues.first()) / (m_inputValues.count() - 1); - - if (qFuzzyCompare(previousFilteredValue, 0)) { - m_outputValue = m_inputValues.first(); + if (qFuzzyCompare(previousNormalizedValue, 0)) { + // We can't calculate anything if the history is at 0. Follow right away to the new value. + m_outputValue = currentValue; m_outputValueCount++; return; } // Calculate change ratio of the last value compared to the previous one, unflitered and filtered - double changeRatioToAverage = 1 - qAbs(currentValue / previousFilteredValue); + double changeRatioToAverage = 1 - qAbs(currentValue / previousNormalizedValue); double changeRatioToCurrentOutput = 1 - qAbs(currentValue / m_outputValue); - double changeRatioFiltered = 1 - qAbs(filteredValue / previousFilteredValue); + double changeRatioFiltered = 1 - qAbs(normalizedValue / previousNormalizedValue); // If the unfiltered value changes for more than 3 times the standard deviation of the jittering values @@ -72,7 +72,7 @@ void StateValueFilterAdaptive::update() m_inputValues.clear(); m_inputValues.prepend(currentValue); m_totalDeviation = 0; - if (!qFuzzyCompare(m_outputValue, filteredValue)) { + if (!qFuzzyCompare(m_outputValue, normalizedValue)) { m_outputValue = currentValue; qCDebug(dcStateValueFilter()) << "Updating output value:" << m_outputValue << "(input exceeds max jitter)"; m_outputValueCount++; @@ -88,9 +88,9 @@ void StateValueFilterAdaptive::update() // to the new value when the summed up deviation exceeds the maximum allowed total deviation if (qAbs(changeRatioFiltered) > m_standardDeviation || qAbs(m_totalDeviation) > m_maxTotalDeviation) { m_totalDeviation = 0; - if (!qFuzzyCompare(m_outputValue, filteredValue)) { - qCDebug(dcStateValueFilter()) << "Updating output value:" << filteredValue << "(drift compensation)"; - m_outputValue = filteredValue; + if (!qFuzzyCompare(m_outputValue, normalizedValue)) { + qCDebug(dcStateValueFilter()) << "Updating output value:" << normalizedValue << "(drift compensation)"; + m_outputValue = normalizedValue; m_outputValueCount++; } } @@ -99,15 +99,13 @@ void StateValueFilterAdaptive::update() m_standardDeviation = ((m_standardDeviation * m_windowSize) + qAbs(changeRatioToAverage)) / (m_windowSize + 1); } - - - // correct stats on overflow of counters + // reset stats on overflow of counters if (m_inputValueCount < m_outputValueCount) { m_outputValueCount = 0; } qCDebug(dcStateValueFilter()) << "Filter statistics for" << this; - qCDebug(dcStateValueFilter()) << "Input:" << currentValue << "AVG:" << previousFilteredValue << "Filtered:" << filteredValue; + qCDebug(dcStateValueFilter()) << "Input:" << currentValue << "AVG:" << previousNormalizedValue << "Filtered:" << normalizedValue; qCDebug(dcStateValueFilter()) << "Change ratios: Input/average:" << changeRatioToAverage << "Filtered/average:" << changeRatioFiltered << "Input/output:" << changeRatioToCurrentOutput; qCDebug(dcStateValueFilter()) << "Std deviation:" << m_standardDeviation << "Total deviation:" << m_totalDeviation; qCDebug(dcStateValueFilter()) << "Compression ratio:" << (1.0 * m_inputValueCount / m_outputValueCount) << "(" << m_outputValueCount << "/" << m_inputValueCount << ")"; diff --git a/libnymea/integrations/thingmanager.h b/libnymea/integrations/thingmanager.h index 635a98bb..e564f45d 100644 --- a/libnymea/integrations/thingmanager.h +++ b/libnymea/integrations/thingmanager.h @@ -81,6 +81,9 @@ public: virtual Thing::ThingError editThing(const ThingId &thingId, const QString &name) = 0; virtual Thing::ThingError setThingSettings(const ThingId &thingId, const ParamList &settings) = 0; + virtual Thing::ThingError setEventLogging(const ThingId &thingId, const EventTypeId &eventTypeId, bool enabled) = 0; + virtual Thing::ThingError setStateFilter(const ThingId &thingId, const StateTypeId &stateTypeId, Types::StateValueFilter filter) = 0; + virtual Thing::ThingError removeConfiguredThing(const ThingId &thingId) = 0; virtual ThingActionInfo* executeAction(const Action &action) = 0; @@ -112,7 +115,7 @@ signals: void thingRemoved(const ThingId &thingId); void thingDisappeared(const ThingId &thingId); void thingAdded(Thing *thing); - void thingChanged(Thing *device); + void thingChanged(Thing *thing); void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value); void ioConnectionAdded(const IOConnection &ioConnection); void ioConnectionRemoved(const IOConnectionId &ioConnectionId); diff --git a/libnymea/types/event.h b/libnymea/types/event.h index bb92e445..62ee3d33 100644 --- a/libnymea/types/event.h +++ b/libnymea/types/event.h @@ -46,7 +46,6 @@ class LIBNYMEA_EXPORT Event Q_PROPERTY(QUuid thingId READ thingId) Q_PROPERTY(QUuid deviceId READ thingId REVISION 1) Q_PROPERTY(ParamList params READ params) - Q_PROPERTY(bool logged READ logged) public: Event(); Event(const EventTypeId &eventTypeId, const ThingId &thingId, const ParamList ¶ms = ParamList(), bool isStateChangeEvent = false); From 386fa12a89973ec49747d3735db0df24b0ce34e3 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 4 Jan 2021 17:20:07 +0100 Subject: [PATCH 09/12] Bump api version --- nymea.pro | 2 +- tests/auto/api.json | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/nymea.pro b/nymea.pro index cecb7ecd..fcf3eea8 100644 --- a/nymea.pro +++ b/nymea.pro @@ -5,7 +5,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p" # define protocol versions JSON_PROTOCOL_VERSION_MAJOR=5 -JSON_PROTOCOL_VERSION_MINOR=3 +JSON_PROTOCOL_VERSION_MINOR=4 JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}" LIBNYMEA_API_VERSION_MAJOR=7 LIBNYMEA_API_VERSION_MINOR=0 diff --git a/tests/auto/api.json b/tests/auto/api.json index c6cdbf91..1c5f22d6 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -5.3 +5.4 { "enums": { "BasicType": [ @@ -244,6 +244,10 @@ "StateOperatorAnd", "StateOperatorOr" ], + "StateValueFilter": [ + "StateValueFilterNone", + "StateValueFilterAdaptive" + ], "TagError": [ "TagErrorNoError", "TagErrorThingNotFound", @@ -1215,6 +1219,17 @@ "thingError": "$ref:ThingError" } }, + "Integrations.SetEventLogging": { + "description": "Enable/disable logging for the given event type on the given thing.", + "params": { + "enabled": "Bool", + "eventTypeId": "Uuid", + "thingId": "Uuid" + }, + "returns": { + "thingError": "$ref:ThingError" + } + }, "Integrations.SetPluginConfiguration": { "description": "Set a plugin's params.", "params": { @@ -1225,6 +1240,17 @@ "thingError": "$ref:ThingError" } }, + "Integrations.SetStateFilter": { + "description": "Set the filter for the given state on the given thing.", + "params": { + "filter": "$ref:StateValueFilter", + "stateTypeId": "Uuid", + "thingId": "Uuid" + }, + "returns": { + "thingError": "$ref:ThingError" + } + }, "Integrations.SetThingSettings": { "description": "Change the settings of a thing.", "params": { @@ -2521,6 +2547,9 @@ "o:settings": "$ref:ParamList", "r:deviceClassId": "Uuid", "r:id": "Uuid", + "r:o:loggedEventTypeIds": [ + "Uuid" + ], "r:o:parentId": "Uuid", "r:o:setupDisplayMessage": "String", "r:params": "$ref:ParamList", @@ -2774,6 +2803,7 @@ "sslEnabled": "Bool" }, "State": { + "r:filter": "$ref:StateValueFilter", "r:stateTypeId": "Uuid", "r:value": "Variant" }, @@ -2833,6 +2863,9 @@ "o:name": "String", "o:settings": "$ref:ParamList", "r:id": "Uuid", + "r:o:loggedEventTypeIds": [ + "Uuid" + ], "r:o:parentId": "Uuid", "r:o:setupDisplayMessage": "String", "r:params": "$ref:ParamList", From e52b45c8151a7736a13973227fd8d2ddd61b8f47 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 4 Jan 2021 18:02:26 +0100 Subject: [PATCH 10/12] fix tests --- libnymea-core/logging/logengine.cpp | 1 + tests/auto/logging/testlogging.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libnymea-core/logging/logengine.cpp b/libnymea-core/logging/logengine.cpp index 4927e5a9..173f4be9 100644 --- a/libnymea-core/logging/logengine.cpp +++ b/libnymea-core/logging/logengine.cpp @@ -223,6 +223,7 @@ void LogEngine::clearDatabase() void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level) { + qCDebug(dcLogEngine()) << "Logging system event:" << active; LogEntry entry(dateTime, level, Logging::LoggingSourceSystem); entry.setEventType(Logging::LoggingEventTypeActiveChange); entry.setActive(active); diff --git a/tests/auto/logging/testlogging.cpp b/tests/auto/logging/testlogging.cpp index f0cc833e..9c9ac62a 100644 --- a/tests/auto/logging/testlogging.cpp +++ b/tests/auto/logging/testlogging.cpp @@ -199,6 +199,7 @@ void TestLogging::systemLogs() qWarning() << "Restarting server"; restartServer(); qWarning() << "Restart done"; + waitForDBSync(); // there should be 2 log entries, one for shutdown, one for startup (from server restart) response = injectAndWait("Logging.GetLogEntries", params); From 5bcda4c25babc6ec1f4a26ea0caaed9a69e3e2ae Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 3 Feb 2021 00:42:32 +0100 Subject: [PATCH 11/12] Add missing license headers --- .../statevaluefilters/statevaluefilter.cpp | 30 +++++++++++++++++++ .../statevaluefilters/statevaluefilter.h | 30 +++++++++++++++++++ .../statevaluefilteradaptive.cpp | 30 +++++++++++++++++++ .../statevaluefilteradaptive.h | 30 +++++++++++++++++++ 4 files changed, 120 insertions(+) diff --git a/libnymea/integrations/statevaluefilters/statevaluefilter.cpp b/libnymea/integrations/statevaluefilters/statevaluefilter.cpp index c1f37c32..618f8c5b 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilter.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilter.cpp @@ -1,3 +1,33 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "statevaluefilter.h" #include "loggingcategories.h" diff --git a/libnymea/integrations/statevaluefilters/statevaluefilter.h b/libnymea/integrations/statevaluefilters/statevaluefilter.h index bd1e2da0..239d5402 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilter.h +++ b/libnymea/integrations/statevaluefilters/statevaluefilter.h @@ -1,3 +1,33 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef STATEVALUEFILTER_H #define STATEVALUEFILTER_H diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp index 575ed439..e24bd052 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp @@ -1,3 +1,33 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "statevaluefilteradaptive.h" #include diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h index bc07f899..aebc89f6 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h @@ -1,3 +1,33 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef STATEVALUEFILTERADAPTIVE_H #define STATEVALUEFILTERADAPTIVE_H From 29f88d744db41ead3f85673e3cd2a3d3a8622140 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 24 Feb 2021 13:41:41 +0100 Subject: [PATCH 12/12] Fix suggestLogging property for plugin devs --- libnymea/integrations/pluginmetadata.cpp | 2 +- plugins/mock/integrationpluginmock.json | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libnymea/integrations/pluginmetadata.cpp b/libnymea/integrations/pluginmetadata.cpp index bbf08c08..5d6163be 100644 --- a/libnymea/integrations/pluginmetadata.cpp +++ b/libnymea/integrations/pluginmetadata.cpp @@ -320,7 +320,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) QStringList stateTypeProperties = {"id", "name", "displayName", "displayNameEvent", "type", "defaultValue", "cached", "unit", "minValue", "maxValue", "possibleValues", "writable", "displayNameAction", - "ioType", "logged", "filter"}; + "ioType", "suggestLogging", "filter"}; QStringList mandatoryStateTypeProperties = {"id", "name", "displayName", "displayNameEvent", "type", "defaultValue"}; QPair verificationResult = verifyFields(stateTypeProperties, mandatoryStateTypeProperties, st); diff --git a/plugins/mock/integrationpluginmock.json b/plugins/mock/integrationpluginmock.json index 9bf6fd6c..527672fb 100644 --- a/plugins/mock/integrationpluginmock.json +++ b/plugins/mock/integrationpluginmock.json @@ -82,7 +82,8 @@ "displayName": "Dummy int state", "displayNameEvent": "Dummy int state changed", "defaultValue": 10, - "type": "int" + "type": "int", + "suggestLogging": true }, { "id": "9dd6a97c-dfd1-43dc-acbd-367932742310", @@ -308,7 +309,8 @@ "displayName": "Dummy int state", "displayNameEvent": "Dummy int state changed", "defaultValue": 10, - "type": "int" + "type": "int", + "suggestLogging": true }, { "id": "978b0ba5-d008-41bd-b63d-a3bd23cb6469",