From 3fb74cc9e4c4fab223401d334f0867f481ad4759 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 4 Jan 2021 16:43:28 +0100 Subject: [PATCH] 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);