Merge PR #337: Support for configuring logging and filtering states/events
This commit is contained in:
commit
01e9ad8916
@ -506,6 +506,47 @@ Thing::ThingError ThingManagerImplementation::setThingSettings(const ThingId &th
|
|||||||
return Thing::ThingErrorNoError;
|
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<EventTypeId> 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)
|
ThingPairingInfo* ThingManagerImplementation::pairThing(const ThingClassId &thingClassId, const ParamList ¶ms, const QString &name)
|
||||||
{
|
{
|
||||||
PairingTransactionId transactionId = PairingTransactionId::createPairingTransactionId();
|
PairingTransactionId transactionId = PairingTransactionId::createPairingTransactionId();
|
||||||
@ -650,6 +691,14 @@ ThingPairingInfo *ThingManagerImplementation::confirmPairing(const PairingTransa
|
|||||||
ParamList settings = buildParams(thingClass.settingsTypes(), ParamList());
|
ParamList settings = buildParams(thingClass.settingsTypes(), ParamList());
|
||||||
thing->setSettings(settings);
|
thing->setSettings(settings);
|
||||||
|
|
||||||
|
QList<EventTypeId> loggedEventTypeIds;
|
||||||
|
foreach (const EventType &eventType, thingClass.eventTypes()) {
|
||||||
|
if (eventType.suggestLogging()) {
|
||||||
|
loggedEventTypeIds.append(eventType.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thing->setLoggedEventTypeIds(loggedEventTypeIds);
|
||||||
|
|
||||||
ThingSetupInfo *info = setupThing(thing);
|
ThingSetupInfo *info = setupThing(thing);
|
||||||
connect(info, &ThingSetupInfo::finished, thing, [this, info, externalInfo, addNewThing](){
|
connect(info, &ThingSetupInfo::finished, thing, [this, info, externalInfo, addNewThing](){
|
||||||
|
|
||||||
@ -746,6 +795,14 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi
|
|||||||
ParamList settings = buildParams(thingClass.settingsTypes(), ParamList());
|
ParamList settings = buildParams(thingClass.settingsTypes(), ParamList());
|
||||||
thing->setSettings(settings);
|
thing->setSettings(settings);
|
||||||
|
|
||||||
|
QList<EventTypeId> loggedEventTypeIds;
|
||||||
|
foreach (const EventType &eventType, thingClass.eventTypes()) {
|
||||||
|
if (eventType.suggestLogging()) {
|
||||||
|
loggedEventTypeIds.append(eventType.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thing->setLoggedEventTypeIds(loggedEventTypeIds);
|
||||||
|
|
||||||
ThingSetupInfo *info = setupThing(thing);
|
ThingSetupInfo *info = setupThing(thing);
|
||||||
connect(info, &ThingSetupInfo::finished, this, [this, info](){
|
connect(info, &ThingSetupInfo::finished, this, [this, info](){
|
||||||
if (info->status() != Thing::ThingErrorNoError) {
|
if (info->status() != Thing::ThingErrorNoError) {
|
||||||
@ -1258,6 +1315,10 @@ ThingActionInfo *ThingManagerImplementation::executeAction(const Action &action)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(info, &ThingActionInfo::finished, this, [=](){
|
||||||
|
emit actionExecuted(action, info->status());
|
||||||
|
});
|
||||||
|
|
||||||
plugin->executeAction(info);
|
plugin->executeAction(info);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
@ -1541,6 +1602,14 @@ void ThingManagerImplementation::loadConfiguredThings()
|
|||||||
|
|
||||||
thing->setSettings(thingSettings);
|
thing->setSettings(thingSettings);
|
||||||
|
|
||||||
|
QList<EventTypeId> loggedEventTypeIds;
|
||||||
|
foreach (const EventType &eventType, thingClass.eventTypes()) {
|
||||||
|
if (eventType.suggestLogging()) {
|
||||||
|
loggedEventTypeIds.append(eventType.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thing->setLoggedEventTypeIds(loggedEventTypeIds);
|
||||||
|
|
||||||
settings.endGroup(); // ThingId
|
settings.endGroup(); // ThingId
|
||||||
|
|
||||||
// We always add the thing to the list in this case. If it's in the stored things
|
// We always add the thing to the list in this case. If it's in the stored things
|
||||||
@ -1659,6 +1728,14 @@ void ThingManagerImplementation::onAutoThingsAppeared(const ThingDescriptors &th
|
|||||||
thing->setSettings(settings);
|
thing->setSettings(settings);
|
||||||
thing->setParentId(thingDescriptor.parentId());
|
thing->setParentId(thingDescriptor.parentId());
|
||||||
|
|
||||||
|
QList<EventTypeId> 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();
|
qCDebug(dcThingManager()) << "Setting up auto thing:" << thing->name() << thing->id().toString();
|
||||||
|
|
||||||
ThingSetupInfo *info = setupThing(thing);
|
ThingSetupInfo *info = setupThing(thing);
|
||||||
@ -1725,7 +1802,7 @@ void ThingManagerImplementation::cleanupThingStateCache()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThingManagerImplementation::onEventTriggered(const Event &event)
|
void ThingManagerImplementation::onEventTriggered(Event event)
|
||||||
{
|
{
|
||||||
// Doing some sanity checks here...
|
// Doing some sanity checks here...
|
||||||
Thing *thing = m_configuredThings.value(event.thingId());
|
Thing *thing = m_configuredThings.value(event.thingId());
|
||||||
@ -1738,7 +1815,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.";
|
qCWarning(dcThingManager()) << "The given thing does not have an event type of id " + event.eventTypeId().toString() + ". Not forwarding event.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// All good, forward the event
|
// configure logging
|
||||||
|
if (thing->loggedEventTypeIds().contains(event.eventTypeId())) {
|
||||||
|
event.setLogged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward the event
|
||||||
emit eventTriggered(event);
|
emit eventTriggered(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1755,7 +1837,7 @@ void ThingManagerImplementation::slotThingStateValueChanged(const StateTypeId &s
|
|||||||
|
|
||||||
Param valueParam(ParamTypeId(stateTypeId.toString()), value);
|
Param valueParam(ParamTypeId(stateTypeId.toString()), value);
|
||||||
Event event(EventTypeId(stateTypeId.toString()), thing->id(), ParamList() << valueParam, true);
|
Event event(EventTypeId(stateTypeId.toString()), thing->id(), ParamList() << valueParam, true);
|
||||||
emit eventTriggered(event);
|
onEventTriggered(event);
|
||||||
|
|
||||||
syncIOConnection(thing, stateTypeId);
|
syncIOConnection(thing, stateTypeId);
|
||||||
}
|
}
|
||||||
@ -2022,6 +2104,7 @@ void ThingManagerImplementation::loadThingStates(Thing *thing)
|
|||||||
} else {
|
} else {
|
||||||
thing->setStateValue(stateType.id(), stateType.defaultValue());
|
thing->setStateValue(stateType.id(), stateType.defaultValue());
|
||||||
}
|
}
|
||||||
|
thing->setStateValueFilter(stateType.id(), stateType.filter());
|
||||||
}
|
}
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
}
|
}
|
||||||
@ -2179,7 +2262,7 @@ IntegrationPlugin *ThingManagerImplementation::createCppIntegrationPlugin(const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginIface->setMetaData(PluginMetadata(pluginInfo));
|
pluginIface->setMetaData(metaData);
|
||||||
|
|
||||||
return pluginIface;
|
return pluginIface;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,6 +106,9 @@ public:
|
|||||||
Thing::ThingError editThing(const ThingId &thingId, const QString &name) override;
|
Thing::ThingError editThing(const ThingId &thingId, const QString &name) override;
|
||||||
Thing::ThingError setThingSettings(const ThingId &thingId, const ParamList &settings) 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;
|
Thing::ThingError removeConfiguredThing(const ThingId &thingId) override;
|
||||||
|
|
||||||
ThingActionInfo* executeAction(const Action &action) override;
|
ThingActionInfo* executeAction(const Action &action) override;
|
||||||
@ -140,7 +143,7 @@ private slots:
|
|||||||
void onAutoThingDisappeared(const ThingId &thingId);
|
void onAutoThingDisappeared(const ThingId &thingId);
|
||||||
void onLoaded();
|
void onLoaded();
|
||||||
void cleanupThingStateCache();
|
void cleanupThingStateCache();
|
||||||
void onEventTriggered(const Event &event);
|
void onEventTriggered(Event event);
|
||||||
|
|
||||||
// Only connect this to Things. It will query the sender()
|
// Only connect this to Things. It will query the sender()
|
||||||
void slotThingStateValueChanged(const StateTypeId &stateTypeId, const QVariant &value);
|
void slotThingStateValueChanged(const StateTypeId &stateTypeId, const QVariant &value);
|
||||||
|
|||||||
@ -107,7 +107,7 @@ JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms, const JsonCon
|
|||||||
|
|
||||||
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
|
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](){
|
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
|
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
|
||||||
|
|||||||
@ -62,6 +62,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
|
|||||||
registerEnum<Types::Unit>();
|
registerEnum<Types::Unit>();
|
||||||
registerEnum<Types::InputType>();
|
registerEnum<Types::InputType>();
|
||||||
registerEnum<Types::IOType>();
|
registerEnum<Types::IOType>();
|
||||||
|
registerEnum<Types::StateValueFilter>();
|
||||||
registerEnum<RuleEngine::RemovePolicy>();
|
registerEnum<RuleEngine::RemovePolicy>();
|
||||||
registerEnum<BrowserItem::BrowserIcon>();
|
registerEnum<BrowserItem::BrowserIcon>();
|
||||||
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
|
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
|
||||||
@ -900,7 +901,7 @@ JsonReply *DeviceHandler::ExecuteAction(const QVariantMap ¶ms, const JsonCon
|
|||||||
|
|
||||||
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
|
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](){
|
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
|
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
|
||||||
|
|||||||
@ -61,6 +61,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
registerEnum<Types::Unit>();
|
registerEnum<Types::Unit>();
|
||||||
registerEnum<Types::InputType>();
|
registerEnum<Types::InputType>();
|
||||||
registerEnum<Types::IOType>();
|
registerEnum<Types::IOType>();
|
||||||
|
registerEnum<Types::StateValueFilter>();
|
||||||
registerEnum<RuleEngine::RemovePolicy>();
|
registerEnum<RuleEngine::RemovePolicy>();
|
||||||
registerEnum<BrowserItem::BrowserIcon>();
|
registerEnum<BrowserItem::BrowserIcon>();
|
||||||
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
|
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
|
||||||
@ -241,6 +242,22 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("SetThingSettings", description, params, returns);
|
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<Thing::ThingError>());
|
||||||
|
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<Types::StateValueFilter>());
|
||||||
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
|
registerMethod("SetStateFilter", description, params, returns);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Remove a thing from the system.";
|
description = "Remove a thing from the system.";
|
||||||
params.insert("thingId", enumValueName(Uuid));
|
params.insert("thingId", enumValueName(Uuid));
|
||||||
@ -838,6 +855,26 @@ JsonReply *IntegrationsHandler::SetThingSettings(const QVariantMap ¶ms)
|
|||||||
return createReply(statusToReply(status));
|
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>();
|
||||||
|
Types::StateValueFilter filter = static_cast<Types::StateValueFilter>(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
|
JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||||
{
|
{
|
||||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||||
@ -958,7 +995,7 @@ JsonReply *IntegrationsHandler::ExecuteAction(const QVariantMap ¶ms, const J
|
|||||||
|
|
||||||
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
|
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](){
|
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
data.insert("thingError", enumValueName(info->status()));
|
data.insert("thingError", enumValueName(info->status()));
|
||||||
|
|||||||
@ -59,6 +59,8 @@ public:
|
|||||||
Q_INVOKABLE JsonReply *EditThing(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *EditThing(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE JsonReply *RemoveThing(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *RemoveThing(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE JsonReply *SetThingSettings(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 *GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const;
|
||||||
Q_INVOKABLE JsonReply *GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const;
|
Q_INVOKABLE JsonReply *GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const;
|
||||||
|
|||||||
@ -34,6 +34,8 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "logvaluetool.h"
|
#include "logvaluetool.h"
|
||||||
|
|
||||||
|
#include "integrations/thingmanager.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlDriver>
|
#include <QSqlDriver>
|
||||||
@ -104,6 +106,13 @@ LogEngine::~LogEngine()
|
|||||||
m_db.close();
|
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)
|
LogEntriesFetchJob *LogEngine::fetchLogEntries(const LogFilter &filter)
|
||||||
{
|
{
|
||||||
QList<LogEntry> results;
|
QList<LogEntry> results;
|
||||||
@ -214,6 +223,7 @@ void LogEngine::clearDatabase()
|
|||||||
|
|
||||||
void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level)
|
void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level)
|
||||||
{
|
{
|
||||||
|
qCDebug(dcLogEngine()) << "Logging system event:" << active;
|
||||||
LogEntry entry(dateTime, level, Logging::LoggingSourceSystem);
|
LogEntry entry(dateTime, level, Logging::LoggingSourceSystem);
|
||||||
entry.setEventType(Logging::LoggingEventTypeActiveChange);
|
entry.setEventType(Logging::LoggingEventTypeActiveChange);
|
||||||
entry.setActive(active);
|
entry.setActive(active);
|
||||||
@ -222,6 +232,10 @@ void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::
|
|||||||
|
|
||||||
void LogEngine::logEvent(const Event &event)
|
void LogEngine::logEvent(const Event &event)
|
||||||
{
|
{
|
||||||
|
if (!event.logged()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariantList valueList;
|
QVariantList valueList;
|
||||||
Logging::LoggingSource sourceType;
|
Logging::LoggingSource sourceType;
|
||||||
if (event.isStateChangeEvent()) {
|
if (event.isStateChangeEvent()) {
|
||||||
@ -249,9 +263,10 @@ void LogEngine::logEvent(const Event &event)
|
|||||||
appendLogEntry(entry);
|
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.setTypeId(action.actionTypeId());
|
||||||
entry.setThingId(action.thingId());
|
entry.setThingId(action.thingId());
|
||||||
|
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
#include "types/browseritemaction.h"
|
#include "types/browseritemaction.h"
|
||||||
#include "types/browseraction.h"
|
#include "types/browseraction.h"
|
||||||
#include "ruleengine/rule.h"
|
#include "ruleengine/rule.h"
|
||||||
|
#include "integrations/thingmanager.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
@ -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(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();
|
~LogEngine();
|
||||||
|
|
||||||
|
void setThingManager(ThingManager *thingManager);
|
||||||
|
|
||||||
LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter());
|
LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter());
|
||||||
ThingsFetchJob *fetchThings();
|
ThingsFetchJob *fetchThings();
|
||||||
|
|
||||||
@ -68,9 +71,11 @@ public:
|
|||||||
void setMaxLogEntries(int maxLogEntries, int trimSize);
|
void setMaxLogEntries(int maxLogEntries, int trimSize);
|
||||||
void clearDatabase();
|
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 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 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 logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
|
||||||
void logRuleTriggered(const Rule &rule);
|
void logRuleTriggered(const Rule &rule);
|
||||||
@ -78,8 +83,10 @@ public:
|
|||||||
void logRuleEnabledChanged(const Rule &rule, const bool &enabled);
|
void logRuleEnabledChanged(const Rule &rule, const bool &enabled);
|
||||||
void logRuleActionsExecuted(const Rule &rule);
|
void logRuleActionsExecuted(const Rule &rule);
|
||||||
void logRuleExitActionsExecuted(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:
|
signals:
|
||||||
void logEntryAdded(const LogEntry &logEntry);
|
void logEntryAdded(const LogEntry &logEntry);
|
||||||
@ -114,6 +121,8 @@ private:
|
|||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
bool m_dbMalformed = 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
|
// When maxQueueLength is exceeded, jobs will be flagged and discarded if this source logs more events
|
||||||
int m_maxQueueLength;
|
int m_maxQueueLength;
|
||||||
QHash<QString, QList<DatabaseJob*>> m_flaggedJobs;
|
QHash<QString, QList<DatabaseJob*>> m_flaggedJobs;
|
||||||
|
|||||||
@ -99,9 +99,6 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
|
|||||||
}
|
}
|
||||||
m_timeManager = new TimeManager(this);
|
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";
|
qCDebug(dcCore()) << "Creating User Manager";
|
||||||
m_userManager = new UserManager(NymeaSettings::settingsPath() + "/user-db.sqlite", this);
|
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";
|
qCDebug(dcCore) << "Creating Rule Engine";
|
||||||
m_ruleEngine = new RuleEngine(this);
|
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";
|
qCDebug(dcCore()) << "Creating Script Engine";
|
||||||
m_scriptEngine = new ScriptEngine(m_thingManager, this);
|
m_scriptEngine = new ScriptEngine(m_thingManager, this);
|
||||||
m_serverManager->jsonServer()->registerHandler(new ScriptsHandler(m_scriptEngine, m_scriptEngine));
|
m_serverManager->jsonServer()->registerHandler(new ScriptsHandler(m_scriptEngine, m_scriptEngine));
|
||||||
@ -380,20 +381,6 @@ Thing::ThingError NymeaCore::removeConfiguredThing(const ThingId &thingId, const
|
|||||||
return removeError;
|
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* NymeaCore::executeBrowserItem(const BrowserAction &browserAction)
|
||||||
{
|
{
|
||||||
BrowserActionInfo *info = m_thingManager->executeBrowserItem(browserAction);
|
BrowserActionInfo *info = m_thingManager->executeBrowserItem(browserAction);
|
||||||
@ -512,7 +499,7 @@ void NymeaCore::executeRuleActions(const QList<RuleAction> ruleActions)
|
|||||||
|
|
||||||
foreach (const Action &action, actions) {
|
foreach (const Action &action, actions) {
|
||||||
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
|
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
|
||||||
ThingActionInfo *info = executeAction(action);
|
ThingActionInfo *info = m_thingManager->executeAction(action);
|
||||||
connect(info, &ThingActionInfo::finished, this, [info](){
|
connect(info, &ThingActionInfo::finished, this, [info](){
|
||||||
if (info->status() != Thing::ThingErrorNoError) {
|
if (info->status() != Thing::ThingErrorNoError) {
|
||||||
qCWarning(dcRuleEngine) << "Error executing action:" << info->status() << info->displayMessage();
|
qCWarning(dcRuleEngine) << "Error executing action:" << info->status() << info->displayMessage();
|
||||||
@ -660,7 +647,6 @@ ZigbeeManager *NymeaCore::zigbeeManager() const
|
|||||||
|
|
||||||
void NymeaCore::gotEvent(const Event &event)
|
void NymeaCore::gotEvent(const Event &event)
|
||||||
{
|
{
|
||||||
m_logger->logEvent(event);
|
|
||||||
emit eventTriggered(event);
|
emit eventTriggered(event);
|
||||||
|
|
||||||
QList<RuleAction> actions;
|
QList<RuleAction> actions;
|
||||||
|
|||||||
@ -84,7 +84,6 @@ public:
|
|||||||
QPair<Thing::ThingError, QList<RuleId> >removeConfiguredThing(const ThingId &thingId, const QHash<RuleId, RuleEngine::RemovePolicy> &removePolicyList);
|
QPair<Thing::ThingError, QList<RuleId> >removeConfiguredThing(const ThingId &thingId, const QHash<RuleId, RuleEngine::RemovePolicy> &removePolicyList);
|
||||||
Thing::ThingError removeConfiguredThing(const ThingId &thingId, const RuleEngine::RemovePolicy &removePolicy);
|
Thing::ThingError removeConfiguredThing(const ThingId &thingId, const RuleEngine::RemovePolicy &removePolicy);
|
||||||
|
|
||||||
ThingActionInfo *executeAction(const Action &action);
|
|
||||||
BrowserActionInfo* executeBrowserItem(const BrowserAction &browserAction);
|
BrowserActionInfo* executeBrowserItem(const BrowserAction &browserAction);
|
||||||
BrowserItemActionInfo* executeBrowserItemAction(const BrowserItemAction &browserItemAction);
|
BrowserItemActionInfo* executeBrowserItemAction(const BrowserItemAction &browserItemAction);
|
||||||
|
|
||||||
|
|||||||
@ -301,7 +301,6 @@ void WebServer::readClient()
|
|||||||
|
|
||||||
HttpRequest request;
|
HttpRequest request;
|
||||||
if (m_incompleteRequests.contains(socket)) {
|
if (m_incompleteRequests.contains(socket)) {
|
||||||
qCDebug(dcWebServer()) << "Append data to incomlete request";
|
|
||||||
request = m_incompleteRequests.take(socket);
|
request = m_incompleteRequests.take(socket);
|
||||||
request.appendData(data);
|
request.appendData(data);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -318,7 +318,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
QJsonObject st = stateTypesJson.toObject();
|
QJsonObject st = stateTypesJson.toObject();
|
||||||
bool writableState = false;
|
bool writableState = false;
|
||||||
|
|
||||||
QPair<QStringList, QStringList> verificationResult = verifyFields(StateType::typeProperties(), StateType::mandatoryTypeProperties(), st);
|
QStringList stateTypeProperties = {"id", "name", "displayName", "displayNameEvent", "type", "defaultValue", "cached",
|
||||||
|
"unit", "minValue", "maxValue", "possibleValues", "writable", "displayNameAction",
|
||||||
|
"ioType", "suggestLogging", "filter"};
|
||||||
|
QStringList mandatoryStateTypeProperties = {"id", "name", "displayName", "displayNameEvent", "type", "defaultValue"};
|
||||||
|
QPair<QStringList, QStringList> verificationResult = verifyFields(stateTypeProperties, mandatoryStateTypeProperties, st);
|
||||||
|
|
||||||
// Check mandatory fields
|
// Check mandatory fields
|
||||||
if (!verificationResult.first.isEmpty()) {
|
if (!verificationResult.first.isEmpty()) {
|
||||||
@ -470,6 +474,18 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
}
|
}
|
||||||
stateType.setIOType(ioType);
|
stateType.setIOType(ioType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateType.setSuggestLogging(st.value("suggestLogging").toBool());
|
||||||
|
|
||||||
|
if (st.contains("filter")) {
|
||||||
|
QString filter = st.value("filter").toString();
|
||||||
|
if (filter == "adaptive") {
|
||||||
|
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);
|
stateTypes.append(stateType);
|
||||||
|
|
||||||
// Events for state changed (Not checking for duplicate UUID, this is expected to be the same as the state!)
|
// Events for state changed (Not checking for duplicate UUID, this is expected to be the same as the state!)
|
||||||
@ -485,6 +501,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
paramType.setUnit(stateType.unit());
|
paramType.setUnit(stateType.unit());
|
||||||
eventType.setParamTypes(QList<ParamType>() << paramType);
|
eventType.setParamTypes(QList<ParamType>() << paramType);
|
||||||
eventType.setIndex(stateType.index());
|
eventType.setIndex(stateType.index());
|
||||||
|
eventType.setSuggestLogging(st.value("suggestLogging").toBool());
|
||||||
eventTypes.append(eventType);
|
eventTypes.append(eventType);
|
||||||
|
|
||||||
// ActionTypes for writeable StateTypes
|
// ActionTypes for writeable StateTypes
|
||||||
@ -497,7 +514,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
actionTypes.append(actionType);
|
actionTypes.append(actionType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thingClass.setStateTypes(stateTypes);
|
|
||||||
|
|
||||||
// ActionTypes
|
// ActionTypes
|
||||||
index = 0;
|
index = 0;
|
||||||
@ -544,7 +560,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
|
|
||||||
actionTypes.append(actionType);
|
actionTypes.append(actionType);
|
||||||
}
|
}
|
||||||
thingClass.setActionTypes(actionTypes);
|
|
||||||
|
|
||||||
// EventTypes
|
// EventTypes
|
||||||
index = 0;
|
index = 0;
|
||||||
@ -580,6 +595,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
EventType eventType(eventTypeId);
|
EventType eventType(eventTypeId);
|
||||||
eventType.setName(eventTypeName);
|
eventType.setName(eventTypeName);
|
||||||
eventType.setDisplayName(et.value("displayName").toString());
|
eventType.setDisplayName(et.value("displayName").toString());
|
||||||
|
eventType.setSuggestLogging(et.value("suggestLogging").toBool());
|
||||||
eventType.setIndex(index++);
|
eventType.setIndex(index++);
|
||||||
|
|
||||||
QPair<bool, QList<ParamType> > paramVerification = parseParamTypes(et.value("paramTypes").toArray());
|
QPair<bool, QList<ParamType> > paramVerification = parseParamTypes(et.value("paramTypes").toArray());
|
||||||
@ -590,7 +606,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
}
|
}
|
||||||
eventTypes.append(eventType);
|
eventTypes.append(eventType);
|
||||||
}
|
}
|
||||||
thingClass.setEventTypes(eventTypes);
|
|
||||||
|
|
||||||
// BrowserItemActionTypes
|
// BrowserItemActionTypes
|
||||||
index = 0;
|
index = 0;
|
||||||
@ -637,7 +652,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
|
|
||||||
browserItemActionTypes.append(actionType);
|
browserItemActionTypes.append(actionType);
|
||||||
}
|
}
|
||||||
thingClass.setBrowserItemActionTypes(browserItemActionTypes);
|
|
||||||
|
|
||||||
// Read interfaces
|
// Read interfaces
|
||||||
QStringList interfaces;
|
QStringList interfaces;
|
||||||
@ -646,22 +660,18 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
if (!iface.isValid()) {
|
if (!iface.isValid()) {
|
||||||
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" uses non-existing interface \"" + value.toString() + "\"");
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" uses non-existing interface \"" + value.toString() + "\"");
|
||||||
hasError = true;
|
hasError = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateTypes stateTypes(thingClass.stateTypes());
|
|
||||||
ActionTypes actionTypes(thingClass.actionTypes());
|
|
||||||
EventTypes eventTypes(thingClass.eventTypes());
|
|
||||||
|
|
||||||
foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) {
|
foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) {
|
||||||
StateType stateType = stateTypes.findByName(ifaceStateType.name());
|
if (!stateTypes.contains(ifaceStateType.name())) {
|
||||||
if (stateType.id().isNull()) {
|
|
||||||
if (!ifaceStateType.optional()) {
|
if (!ifaceStateType.optional()) {
|
||||||
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\"");
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\"");
|
||||||
hasError = true;
|
hasError = true;
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
StateType &stateType = stateTypes[ifaceStateType.name()];
|
||||||
if (ifaceStateType.type() != stateType.type()) {
|
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()) + "\"");
|
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;
|
hasError = true;
|
||||||
@ -697,18 +707,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()));
|
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;
|
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()) {
|
foreach (const InterfaceActionType &ifaceActionType, iface.actionTypes()) {
|
||||||
ActionType actionType = actionTypes.findByName(ifaceActionType.name());
|
if (!actionTypes.contains(ifaceActionType.name())) {
|
||||||
if (actionType.id().isNull()) {
|
|
||||||
if (!ifaceActionType.optional()) {
|
if (!ifaceActionType.optional()) {
|
||||||
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\"");
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\"");
|
||||||
hasError = true;
|
hasError = true;
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
ActionType &actionType = actionTypes[ifaceActionType.name()];
|
||||||
// Verify the params as required by the interface are available
|
// Verify the params as required by the interface are available
|
||||||
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
|
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
|
||||||
ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name());
|
ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name());
|
||||||
@ -779,15 +793,14 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (const InterfaceEventType &ifaceEventType, iface.eventTypes()) {
|
foreach (const InterfaceEventType &ifaceEventType, iface.eventTypes()) {
|
||||||
EventType eventType = eventTypes.findByName(ifaceEventType.name());
|
if (!eventTypes.contains(ifaceEventType.name())) {
|
||||||
if (!eventType.isValid()) {
|
|
||||||
if (!ifaceEventType.optional()) {
|
if (!ifaceEventType.optional()) {
|
||||||
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\"");
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\"");
|
||||||
hasError = true;
|
hasError = true;
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
EventType &eventType = eventTypes[ifaceEventType.name()];
|
||||||
|
|
||||||
// Verify all the params as required by the interface are available
|
// Verify all the params as required by the interface are available
|
||||||
foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) {
|
foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) {
|
||||||
@ -842,6 +855,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
|
// 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...
|
// an emitted event always needs to have params filled with values. The client might use them or not...
|
||||||
}
|
}
|
||||||
@ -851,6 +869,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
interfaces.removeDuplicates();
|
interfaces.removeDuplicates();
|
||||||
thingClass.setInterfaces(interfaces);
|
thingClass.setInterfaces(interfaces);
|
||||||
|
|
||||||
|
thingClass.setStateTypes(stateTypes);
|
||||||
|
thingClass.setActionTypes(actionTypes);
|
||||||
|
thingClass.setEventTypes(eventTypes);
|
||||||
|
thingClass.setBrowserItemActionTypes(browserItemActionTypes);
|
||||||
|
|
||||||
m_thingClasses.append(thingClass);
|
m_thingClasses.append(thingClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
libnymea/integrations/statevaluefilters/statevaluefilter.cpp
Normal file
45
libnymea/integrations/statevaluefilters/statevaluefilter.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
NYMEA_LOGGING_CATEGORY(dcStateValueFilter, "StateValueFilter")
|
||||||
|
|
||||||
|
StateValueFilter::StateValueFilter()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StateValueFilter::~StateValueFilter()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
51
libnymea/integrations/statevaluefilters/statevaluefilter.h
Normal file
51
libnymea/integrations/statevaluefilters/statevaluefilter.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(dcStateValueFilter)
|
||||||
|
|
||||||
|
class StateValueFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StateValueFilter();
|
||||||
|
virtual ~StateValueFilter();
|
||||||
|
|
||||||
|
virtual void addValue(const QVariant &value) = 0;
|
||||||
|
|
||||||
|
virtual QVariant filteredValue() const = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATEVALUEFILTER_H
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <qmath.h>
|
||||||
|
|
||||||
|
StateValueFilterAdaptive::StateValueFilterAdaptive()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateValueFilterAdaptive::addValue(const QVariant &value)
|
||||||
|
{
|
||||||
|
m_inputValues.prepend(value.toDouble());
|
||||||
|
m_inputValueCount++;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant StateValueFilterAdaptive::filteredValue() const
|
||||||
|
{
|
||||||
|
return m_outputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateValueFilterAdaptive::update()
|
||||||
|
{
|
||||||
|
while (m_inputValues.count() > m_windowSize) {
|
||||||
|
m_inputValues.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_inputValues.isEmpty()) {
|
||||||
|
m_outputValue = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_inputValues.count() == 1) {
|
||||||
|
// Not enough data
|
||||||
|
m_outputValue = m_inputValues.first();
|
||||||
|
m_outputValueCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double currentValue = m_inputValues.first();
|
||||||
|
if (qFuzzyCompare(currentValue, 0)) {
|
||||||
|
// If we went to 0, follow right away.
|
||||||
|
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_inputValues.count(); i++) {
|
||||||
|
sum += m_inputValues.at(i);
|
||||||
|
}
|
||||||
|
double normalizedValue = sum / m_inputValues.count();
|
||||||
|
double previousNormalizedValue = (sum - m_inputValues.first()) / (m_inputValues.count() - 1);
|
||||||
|
|
||||||
|
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 / previousNormalizedValue);
|
||||||
|
double changeRatioToCurrentOutput = 1 - qAbs(currentValue / m_outputValue);
|
||||||
|
double changeRatioFiltered = 1 - qAbs(normalizedValue / previousNormalizedValue);
|
||||||
|
|
||||||
|
|
||||||
|
// 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(changeRatioToAverage) > m_standardDeviation * 3) {
|
||||||
|
m_inputValues.clear();
|
||||||
|
m_inputValues.prepend(currentValue);
|
||||||
|
m_totalDeviation = 0;
|
||||||
|
if (!qFuzzyCompare(m_outputValue, normalizedValue)) {
|
||||||
|
m_outputValue = currentValue;
|
||||||
|
qCDebug(dcStateValueFilter()) << "Updating output value:" << m_outputValue << "(input exceeds max jitter)";
|
||||||
|
m_outputValueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, normalizedValue)) {
|
||||||
|
qCDebug(dcStateValueFilter()) << "Updating output value:" << normalizedValue << "(drift compensation)";
|
||||||
|
m_outputValue = normalizedValue;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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:" << 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 << ")";
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#include "statevaluefilter.h"
|
||||||
|
|
||||||
|
class StateValueFilterAdaptive : public StateValueFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StateValueFilterAdaptive();
|
||||||
|
|
||||||
|
void addValue(const QVariant &value) override;
|
||||||
|
QVariant filteredValue() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<double> m_inputValues;
|
||||||
|
|
||||||
|
int m_windowSize = 20;
|
||||||
|
double m_standardDeviation = 0.05;
|
||||||
|
double m_maxTotalDeviation = 0.4;
|
||||||
|
|
||||||
|
double m_totalDeviation = 0;
|
||||||
|
|
||||||
|
double m_outputValue = 0;
|
||||||
|
|
||||||
|
// Stats for debugging
|
||||||
|
quint64 m_inputValueCount = 0;
|
||||||
|
quint64 m_outputValueCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATEVALUEFILTERADAPTIVE_H
|
||||||
@ -133,6 +133,7 @@
|
|||||||
#include "thing.h"
|
#include "thing.h"
|
||||||
#include "types/event.h"
|
#include "types/event.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
#include "statevaluefilters/statevaluefilteradaptive.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@ -334,9 +335,6 @@ void Thing::setStateValue(const StateTypeId &stateTypeId, const QVariant &value)
|
|||||||
}
|
}
|
||||||
for (int i = 0; i < m_states.count(); ++i) {
|
for (int i = 0; i < m_states.count(); ++i) {
|
||||||
if (m_states.at(i).stateTypeId() == stateTypeId) {
|
if (m_states.at(i).stateTypeId() == stateTypeId) {
|
||||||
if (m_states.at(i).value() == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QVariant newValue = value;
|
QVariant newValue = value;
|
||||||
if (!newValue.convert(stateType.type())) {
|
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)";
|
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;
|
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) {
|
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;
|
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;
|
return;
|
||||||
@ -383,6 +386,11 @@ State Thing::state(const StateTypeId &stateTypeId) const
|
|||||||
return State(StateTypeId(), ThingId());
|
return State(StateTypeId(), ThingId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<EventTypeId> Thing::loggedEventTypeIds() const
|
||||||
|
{
|
||||||
|
return m_loggedEventTypeIds;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Returns the \l{ThingId} of the parent of this thing. If the parentId
|
/*! Returns the \l{ThingId} of the parent of this thing. If the parentId
|
||||||
is not set, this thing does not have a parent.
|
is not set, this thing does not have a parent.
|
||||||
*/
|
*/
|
||||||
@ -442,6 +450,27 @@ void Thing::setSetupStatus(Thing::ThingSetupStatus status, Thing::ThingError set
|
|||||||
emit setupStatusChanged();
|
emit setupStatusChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thing::setLoggedEventTypeIds(const QList<EventTypeId> 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<Thing*> &other)
|
Things::Things(const QList<Thing*> &other)
|
||||||
{
|
{
|
||||||
foreach (Thing* thing, other) {
|
foreach (Thing* thing, other) {
|
||||||
|
|||||||
@ -45,6 +45,7 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
class IntegrationPlugin;
|
class IntegrationPlugin;
|
||||||
|
class StateValueFilter;
|
||||||
|
|
||||||
class LIBNYMEA_EXPORT Thing: public QObject
|
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(QString setupDisplayMessage READ setupDisplayMessage NOTIFY setupStatusChanged USER true)
|
||||||
Q_PROPERTY(ThingError setupError READ setupError NOTIFY setupStatusChanged)
|
Q_PROPERTY(ThingError setupError READ setupError NOTIFY setupStatusChanged)
|
||||||
Q_PROPERTY(QUuid parentId READ parentId USER true)
|
Q_PROPERTY(QUuid parentId READ parentId USER true)
|
||||||
|
Q_PROPERTY(QList<EventTypeId> loggedEventTypeIds READ loggedEventTypeIds USER true)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ThingError {
|
enum ThingError {
|
||||||
@ -133,6 +135,8 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE State state(const StateTypeId &stateTypeId) const;
|
Q_INVOKABLE State state(const StateTypeId &stateTypeId) const;
|
||||||
|
|
||||||
|
QList<EventTypeId> loggedEventTypeIds() const;
|
||||||
|
|
||||||
ThingId parentId() const;
|
ThingId parentId() const;
|
||||||
void setParentId(const ThingId &parentId);
|
void setParentId(const ThingId &parentId);
|
||||||
|
|
||||||
@ -161,6 +165,8 @@ private:
|
|||||||
Thing(const PluginId &pluginId, const ThingClass &thingClass, QObject *parent = nullptr);
|
Thing(const PluginId &pluginId, const ThingClass &thingClass, QObject *parent = nullptr);
|
||||||
|
|
||||||
void setSetupStatus(ThingSetupStatus status, ThingError setupError, const QString &displayMessage = QString());
|
void setSetupStatus(ThingSetupStatus status, ThingError setupError, const QString &displayMessage = QString());
|
||||||
|
void setLoggedEventTypeIds(const QList<EventTypeId> loggedEventTypeIds);
|
||||||
|
void setStateValueFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThingClass m_thingClass;
|
ThingClass m_thingClass;
|
||||||
@ -176,6 +182,9 @@ private:
|
|||||||
ThingSetupStatus m_setupStatus = ThingSetupStatusNone;
|
ThingSetupStatus m_setupStatus = ThingSetupStatusNone;
|
||||||
ThingError m_setupError = ThingErrorNoError;
|
ThingError m_setupError = ThingErrorNoError;
|
||||||
QString m_setupDisplayMessage;
|
QString m_setupDisplayMessage;
|
||||||
|
|
||||||
|
QList<EventTypeId> m_loggedEventTypeIds;
|
||||||
|
QHash<StateTypeId, StateValueFilter*> m_stateValueFilters;
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug operator<<(QDebug dbg, Thing *device);
|
QDebug operator<<(QDebug dbg, Thing *device);
|
||||||
|
|||||||
@ -81,6 +81,9 @@ public:
|
|||||||
virtual Thing::ThingError editThing(const ThingId &thingId, const QString &name) = 0;
|
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 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 Thing::ThingError removeConfiguredThing(const ThingId &thingId) = 0;
|
||||||
|
|
||||||
virtual ThingActionInfo* executeAction(const Action &action) = 0;
|
virtual ThingActionInfo* executeAction(const Action &action) = 0;
|
||||||
@ -112,10 +115,11 @@ signals:
|
|||||||
void thingRemoved(const ThingId &thingId);
|
void thingRemoved(const ThingId &thingId);
|
||||||
void thingDisappeared(const ThingId &thingId);
|
void thingDisappeared(const ThingId &thingId);
|
||||||
void thingAdded(Thing *thing);
|
void thingAdded(Thing *thing);
|
||||||
void thingChanged(Thing *device);
|
void thingChanged(Thing *thing);
|
||||||
void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value);
|
void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value);
|
||||||
void ioConnectionAdded(const IOConnection &ioConnection);
|
void ioConnectionAdded(const IOConnection &ioConnection);
|
||||||
void ioConnectionRemoved(const IOConnectionId &ioConnectionId);
|
void ioConnectionRemoved(const IOConnectionId &ioConnectionId);
|
||||||
|
void actionExecuted(const Action &action, Thing::ThingError status);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // THINGMANAGER_H
|
#endif // THINGMANAGER_H
|
||||||
|
|||||||
@ -200,6 +200,10 @@ Interface ThingUtils::loadInterface(const QString &name)
|
|||||||
stateType.setMinValue(stateVariant.toMap().value("minValue"));
|
stateType.setMinValue(stateVariant.toMap().value("minValue"));
|
||||||
stateType.setMaxValue(stateVariant.toMap().value("maxValue"));
|
stateType.setMaxValue(stateVariant.toMap().value("maxValue"));
|
||||||
stateType.setOptional(stateVariant.toMap().value("optional", false).toBool());
|
stateType.setOptional(stateVariant.toMap().value("optional", false).toBool());
|
||||||
|
if (stateVariant.toMap().contains("logged")) {
|
||||||
|
stateType.setLoggingOverride(true);
|
||||||
|
stateType.setSuggestLogging(stateVariant.toMap().value("logged", false).toBool());
|
||||||
|
}
|
||||||
if (stateVariant.toMap().contains("unit")) {
|
if (stateVariant.toMap().contains("unit")) {
|
||||||
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
|
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
|
||||||
int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray());
|
int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray());
|
||||||
@ -214,6 +218,8 @@ Interface ThingUtils::loadInterface(const QString &name)
|
|||||||
InterfaceEventType stateChangeEventType;
|
InterfaceEventType stateChangeEventType;
|
||||||
stateChangeEventType.setName(stateType.name());
|
stateChangeEventType.setName(stateType.name());
|
||||||
stateChangeEventType.setOptional(stateType.optional());
|
stateChangeEventType.setOptional(stateType.optional());
|
||||||
|
stateChangeEventType.setSuggestLogging(stateType.suggestLogging());
|
||||||
|
stateChangeEventType.setLoggingOverride(stateType.loggingOverride());
|
||||||
ParamType stateChangeEventParamType;
|
ParamType stateChangeEventParamType;
|
||||||
stateChangeEventParamType.setName(stateType.name());
|
stateChangeEventParamType.setName(stateType.name());
|
||||||
stateChangeEventParamType.setType(stateType.type());
|
stateChangeEventParamType.setType(stateType.type());
|
||||||
@ -236,6 +242,10 @@ Interface ThingUtils::loadInterface(const QString &name)
|
|||||||
InterfaceActionType actionType;
|
InterfaceActionType actionType;
|
||||||
actionType.setName(actionVariant.toMap().value("name").toString());
|
actionType.setName(actionVariant.toMap().value("name").toString());
|
||||||
actionType.setOptional(actionVariant.toMap().value("optional").toBool());
|
actionType.setOptional(actionVariant.toMap().value("optional").toBool());
|
||||||
|
// if (actionVariant.toMap().contains("logged")) {
|
||||||
|
// actionType.setLoggingOverride(true);
|
||||||
|
// actionType.setSuggestLogging(actionVariant.toMap().value("logged").toBool());
|
||||||
|
// }
|
||||||
ParamTypes paramTypes;
|
ParamTypes paramTypes;
|
||||||
foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) {
|
foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) {
|
||||||
ParamType paramType;
|
ParamType paramType;
|
||||||
@ -254,6 +264,10 @@ Interface ThingUtils::loadInterface(const QString &name)
|
|||||||
InterfaceEventType eventType;
|
InterfaceEventType eventType;
|
||||||
eventType.setName(eventVariant.toMap().value("name").toString());
|
eventType.setName(eventVariant.toMap().value("name").toString());
|
||||||
eventType.setOptional(eventVariant.toMap().value("optional").toBool());
|
eventType.setOptional(eventVariant.toMap().value("optional").toBool());
|
||||||
|
if (eventVariant.toMap().contains("logged")) {
|
||||||
|
eventType.setLoggingOverride(true);
|
||||||
|
eventType.setSuggestLogging(eventVariant.toMap().value("logged").toBool());
|
||||||
|
}
|
||||||
ParamTypes paramTypes;
|
ParamTypes paramTypes;
|
||||||
foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) {
|
foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) {
|
||||||
ParamType paramType;
|
ParamType paramType;
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "batteryCritical",
|
"name": "batteryCritical",
|
||||||
"type": "bool"
|
"type": "bool",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"description": "The base for all buttons that emit a pressed event.",
|
"description": "The base for all buttons that emit a pressed event.",
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"name": "pressed"
|
"name": "pressed",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "closed",
|
"name": "closed",
|
||||||
"type": "bool"
|
"type": "bool",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
{
|
{
|
||||||
"name": "co2",
|
"name": "co2",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "PartsPerMillion"
|
"unit": "PartsPerMillion",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
{
|
{
|
||||||
"name": "conductivity",
|
"name": "conductivity",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "MicroSiemensPerCentimeter"
|
"unit": "MicroSiemensPerCentimeter",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
{
|
{
|
||||||
"name": "connected",
|
"name": "connected",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"defaultValue": false
|
"defaultValue": false,
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "daylight",
|
"name": "daylight",
|
||||||
"type": "bool"
|
"type": "bool",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sunriseTime",
|
"name": "sunriseTime",
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"description": "An interface for doorbells. Emits \"doorbellPressed\" when the doorbell is pressed.",
|
"description": "An interface for doorbells. Emits \"doorbellPressed\" when the doorbell is pressed.",
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"name": "doorbellPressed"
|
"name": "doorbellPressed",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "moving",
|
"name": "moving",
|
||||||
"type": "bool"
|
"type": "bool",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "percentage",
|
"name": "percentage",
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
{
|
{
|
||||||
"name": "currentPower",
|
"name": "currentPower",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "Watt"
|
"unit": "Watt",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "currentPower",
|
"name": "currentPower",
|
||||||
"type": "double"
|
"type": "double",
|
||||||
|
"unit": "Watt",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,8 @@
|
|||||||
"PinkyRight"
|
"PinkyRight"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
{
|
{
|
||||||
"name": "state",
|
"name": "state",
|
||||||
"type": "QString",
|
"type": "QString",
|
||||||
"allowedValues": ["open", "closed", "opening", "closing"]
|
"allowedValues": ["open", "closed", "opening", "closing"],
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "intermediatePosition",
|
"name": "intermediatePosition",
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
"name": "humidity",
|
"name": "humidity",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"minValue": 0,
|
"minValue": 0,
|
||||||
"maxValue": 100
|
"maxValue": 100,
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.",
|
"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": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"name": "triggerImpulse"
|
"name": "triggerImpulse",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"name": "triggered"
|
"name": "triggered",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
{
|
{
|
||||||
"name": "lightIntensity",
|
"name": "lightIntensity",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "Lux"
|
"unit": "Lux",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
"extends": "button",
|
"extends": "button",
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"name": "longPressed"
|
"name": "longPressed",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,8 @@
|
|||||||
"name": "buttonName",
|
"name": "buttonName",
|
||||||
"type": "QString"
|
"type": "QString"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "moisture",
|
"name": "moisture",
|
||||||
"type": "double"
|
"type": "double",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,8 @@
|
|||||||
"name": "buttonName",
|
"name": "buttonName",
|
||||||
"type": "QString"
|
"type": "QString"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
{
|
{
|
||||||
"name": "noise",
|
"name": "noise",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "Dezibel"
|
"unit": "Dezibel",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
"name": "body",
|
"name": "body",
|
||||||
"type": "QString"
|
"type": "QString"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
{
|
{
|
||||||
"name": "power",
|
"name": "power",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"writable": true
|
"writable": true,
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "isPresent",
|
"name": "isPresent",
|
||||||
"type": "bool"
|
"type": "bool",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lastSeenTime",
|
"name": "lastSeenTime",
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
{
|
{
|
||||||
"name": "pressure",
|
"name": "pressure",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "MilliBar"
|
"unit": "MilliBar",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
{
|
{
|
||||||
"name": "state",
|
"name": "state",
|
||||||
"type": "QString",
|
"type": "QString",
|
||||||
"allowedValues": ["locked", "locking", "unlocked", "unlocking", "unlatched", "unlatching"]
|
"allowedValues": ["locked", "locking", "unlocked", "unlocking", "unlatched", "unlatching"],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
{
|
{
|
||||||
"name": "state",
|
"name": "state",
|
||||||
"type": "QString",
|
"type": "QString",
|
||||||
"allowedValues": ["open", "closed", "opening", "closing", "intermediate"]
|
"allowedValues": ["open", "closed", "opening", "closing", "intermediate"],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
{
|
{
|
||||||
"name": "temperature",
|
"name": "temperature",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "DegreeCelsius"
|
"unit": "DegreeCelsius",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,8 @@
|
|||||||
"name": "userId",
|
"name": "userId",
|
||||||
"type": "QString"
|
"type": "QString"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
@ -25,8 +26,8 @@
|
|||||||
"name": "userId",
|
"name": "userId",
|
||||||
"type": "QString"
|
"type": "QString"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
]
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "removeUser",
|
"name": "removeUser",
|
||||||
@ -36,7 +37,8 @@
|
|||||||
"type": "QString"
|
"type": "QString"
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
],
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,32 +2,39 @@
|
|||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "weatherDescription",
|
"name": "weatherDescription",
|
||||||
"type": "QString"
|
"type": "QString",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "weatherCondition",
|
"name": "weatherCondition",
|
||||||
"type": "QString",
|
"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",
|
"name": "temperature",
|
||||||
"type": "double"
|
"type": "double",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "humidity",
|
"name": "humidity",
|
||||||
"type": "int"
|
"type": "int",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pressure",
|
"name": "pressure",
|
||||||
"type": "double"
|
"type": "double",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "windSpeed",
|
"name": "windSpeed",
|
||||||
"type": "double"
|
"type": "double",
|
||||||
|
"logged": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "windDirection",
|
"name": "windDirection",
|
||||||
"type": "int"
|
"type": "int",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,8 @@
|
|||||||
{
|
{
|
||||||
"name": "windSpeed",
|
"name": "windSpeed",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"unit": "MeterPerSecond"
|
"unit": "MeterPerSecond",
|
||||||
|
"logged": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
#include "jsonhandler.h"
|
#include "jsonhandler.h"
|
||||||
|
#include "typeutils.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -207,6 +207,9 @@ void JsonHandler::registerObject(const QMetaObject &metaObject)
|
|||||||
typeName = QString("$ref:BasicType");
|
typeName = QString("$ref:BasicType");
|
||||||
} else if (QString(metaProperty.typeName()).startsWith("QList")) {
|
} else if (QString(metaProperty.typeName()).startsWith("QList")) {
|
||||||
QString elementType = QString(metaProperty.typeName()).remove("QList<").remove(">");
|
QString elementType = QString(metaProperty.typeName()).remove("QList<").remove(">");
|
||||||
|
if (elementType == "EventTypeId" || elementType == "StateTypeId" || elementType == "ActionTypeId") {
|
||||||
|
elementType = "QUuid";
|
||||||
|
}
|
||||||
QVariant::Type variantType = QVariant::nameToType(elementType.toUtf8());
|
QVariant::Type variantType = QVariant::nameToType(elementType.toUtf8());
|
||||||
typeName = QVariantList() << enumValueName(variantTypeToBasicType(variantType));
|
typeName = QVariantList() << enumValueName(variantTypeToBasicType(variantType));
|
||||||
} else {
|
} else {
|
||||||
@ -344,6 +347,18 @@ QVariant JsonHandler::pack(const QMetaObject &metaObject, const void *value) con
|
|||||||
foreach (const QUuid &entry, propertyValue.value<QList<QUuid>>()) {
|
foreach (const QUuid &entry, propertyValue.value<QList<QUuid>>()) {
|
||||||
list << entry;
|
list << entry;
|
||||||
}
|
}
|
||||||
|
} else if (propertyTypeName == "QList<EventTypeId>") {
|
||||||
|
foreach (const EventTypeId &entry, propertyValue.value<QList<EventTypeId>>()) {
|
||||||
|
list << entry;
|
||||||
|
}
|
||||||
|
} else if (propertyTypeName == "QList<StateTypeId>") {
|
||||||
|
foreach (const EventTypeId &entry, propertyValue.value<QList<EventTypeId>>()) {
|
||||||
|
list << entry;
|
||||||
|
}
|
||||||
|
} else if (propertyTypeName == "QList<ActionTypeId>") {
|
||||||
|
foreach (const EventTypeId &entry, propertyValue.value<QList<EventTypeId>>()) {
|
||||||
|
list << entry;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT_X(false, this->metaObject()->className(), QString("Unhandled list type: %1").arg(propertyTypeName).toUtf8());
|
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;
|
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());
|
intList.append(val.toInt());
|
||||||
}
|
}
|
||||||
metaProperty.writeOnGadget(ptr, QVariant::fromValue(intList));
|
metaProperty.writeOnGadget(ptr, QVariant::fromValue(intList));
|
||||||
} else if (metaProperty.typeName() == QStringLiteral("QList<QUuid>")) {
|
} else if (metaProperty.typeName() == QStringLiteral("QList<QUuid>")
|
||||||
|
|| metaProperty.typeName() == QStringLiteral("QList<EventTypeId>")
|
||||||
|
|| metaProperty.typeName() == QStringLiteral("QList<StateTypeId>")
|
||||||
|
|| metaProperty.typeName() == QStringLiteral("QList<ActionTypeId>")) {
|
||||||
QList<QUuid> uuidList;
|
QList<QUuid> uuidList;
|
||||||
foreach (const QVariant &val, variant.toList()) {
|
foreach (const QVariant &val, variant.toList()) {
|
||||||
uuidList.append(val.toUuid());
|
uuidList.append(val.toUuid());
|
||||||
|
|||||||
@ -126,6 +126,8 @@ SOURCES += \
|
|||||||
integrations/thingsetupinfo.cpp \
|
integrations/thingsetupinfo.cpp \
|
||||||
integrations/thingutils.cpp \
|
integrations/thingutils.cpp \
|
||||||
integrations/servicedata.cpp \
|
integrations/servicedata.cpp \
|
||||||
|
integrations/statevaluefilters/statevaluefilter.cpp \
|
||||||
|
integrations/statevaluefilters/statevaluefilteradaptive.cpp \
|
||||||
jsonrpc/jsoncontext.cpp \
|
jsonrpc/jsoncontext.cpp \
|
||||||
jsonrpc/jsonhandler.cpp \
|
jsonrpc/jsonhandler.cpp \
|
||||||
jsonrpc/jsonreply.cpp \
|
jsonrpc/jsonreply.cpp \
|
||||||
|
|||||||
@ -130,6 +130,26 @@ ActionTypes::ActionTypes(const QList<ActionType> &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
|
QVariant ActionTypes::get(int index) const
|
||||||
{
|
{
|
||||||
return QVariant::fromValue(at(index));
|
return QVariant::fromValue(at(index));
|
||||||
@ -160,8 +180,21 @@ ActionType ActionTypes::findById(const ActionTypeId &id)
|
|||||||
return ActionType(ActionTypeId());
|
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)
|
QDebug operator<<(QDebug dbg, const ActionType &actionType)
|
||||||
{
|
{
|
||||||
dbg.nospace().noquote() << "ActionType: " << actionType.name() << actionType.displayName() << actionType.id();
|
dbg.nospace().noquote() << "ActionType: " << actionType.name() << actionType.displayName() << actionType.id();
|
||||||
return dbg;
|
return dbg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -84,10 +84,13 @@ class ActionTypes: public QList<ActionType>
|
|||||||
public:
|
public:
|
||||||
ActionTypes() = default;
|
ActionTypes() = default;
|
||||||
ActionTypes(const QList<ActionType> &other);
|
ActionTypes(const QList<ActionType> &other);
|
||||||
|
bool contains(const ActionTypeId &id) const;
|
||||||
|
bool contains(const QString &name) const;
|
||||||
Q_INVOKABLE QVariant get(int index) const;
|
Q_INVOKABLE QVariant get(int index) const;
|
||||||
Q_INVOKABLE void put(const QVariant &variant);
|
Q_INVOKABLE void put(const QVariant &variant);
|
||||||
ActionType findByName(const QString &name);
|
ActionType findByName(const QString &name);
|
||||||
ActionType findById(const ActionTypeId &id);
|
ActionType findById(const ActionTypeId &id);
|
||||||
|
ActionType &operator[](const QString &name);
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(ActionTypes)
|
Q_DECLARE_METATYPE(ActionTypes)
|
||||||
|
|
||||||
|
|||||||
@ -126,6 +126,16 @@ bool Event::isStateChangeEvent() const
|
|||||||
return m_isStateChangeEvent;
|
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.
|
/*! Compare this Event to the Event given by \a other.
|
||||||
* Events are equal (returns true) if eventTypeId, deviceId and params match. */
|
* Events are equal (returns true) if eventTypeId, deviceId and params match. */
|
||||||
bool Event::operator ==(const Event &other) const
|
bool Event::operator ==(const Event &other) const
|
||||||
|
|||||||
@ -65,12 +65,16 @@ public:
|
|||||||
|
|
||||||
bool isStateChangeEvent() const;
|
bool isStateChangeEvent() const;
|
||||||
|
|
||||||
|
bool logged() const;
|
||||||
|
void setLogged(bool logged);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventTypeId m_eventTypeId;
|
EventTypeId m_eventTypeId;
|
||||||
ThingId m_thingId;
|
ThingId m_thingId;
|
||||||
ParamList m_params;
|
ParamList m_params;
|
||||||
|
|
||||||
bool m_isStateChangeEvent;
|
bool m_isStateChangeEvent;
|
||||||
|
bool m_logged = false;
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(Event)
|
Q_DECLARE_METATYPE(Event)
|
||||||
QDebug operator<<(QDebug dbg, const Event &event);
|
QDebug operator<<(QDebug dbg, const Event &event);
|
||||||
|
|||||||
@ -110,6 +110,16 @@ void EventType::setParamTypes(const ParamTypes ¶mTypes)
|
|||||||
m_paramTypes = paramTypes;
|
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 */
|
/*! Returns true if this EventType has a valid id and name */
|
||||||
bool EventType::isValid() const
|
bool EventType::isValid() const
|
||||||
{
|
{
|
||||||
@ -130,11 +140,31 @@ QStringList EventType::mandatoryTypeProperties()
|
|||||||
|
|
||||||
EventTypes::EventTypes(const QList<EventType> &other)
|
EventTypes::EventTypes(const QList<EventType> &other)
|
||||||
{
|
{
|
||||||
foreach (const EventType &at, other) {
|
foreach (const EventType &et, other) {
|
||||||
append(at);
|
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
|
QVariant EventTypes::get(int index) const
|
||||||
{
|
{
|
||||||
return QVariant::fromValue(at(index));
|
return QVariant::fromValue(at(index));
|
||||||
@ -164,3 +194,15 @@ EventType EventTypes::findById(const EventTypeId &id)
|
|||||||
}
|
}
|
||||||
return EventType(EventTypeId());
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@ -64,6 +64,9 @@ public:
|
|||||||
ParamTypes paramTypes() const;
|
ParamTypes paramTypes() const;
|
||||||
void setParamTypes(const ParamTypes ¶mTypes);
|
void setParamTypes(const ParamTypes ¶mTypes);
|
||||||
|
|
||||||
|
bool suggestLogging() const;
|
||||||
|
void setSuggestLogging(bool logged);
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
static QStringList typeProperties();
|
static QStringList typeProperties();
|
||||||
@ -75,6 +78,7 @@ private:
|
|||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
int m_index;
|
int m_index;
|
||||||
QList<ParamType> m_paramTypes;
|
QList<ParamType> m_paramTypes;
|
||||||
|
bool m_logged = false;
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(EventType)
|
Q_DECLARE_METATYPE(EventType)
|
||||||
|
|
||||||
@ -85,10 +89,13 @@ class EventTypes: public QList<EventType>
|
|||||||
public:
|
public:
|
||||||
EventTypes() = default;
|
EventTypes() = default;
|
||||||
EventTypes(const QList<EventType> &other);
|
EventTypes(const QList<EventType> &other);
|
||||||
|
bool contains(const EventTypeId &id) const;
|
||||||
|
bool contains(const QString &name) const;
|
||||||
Q_INVOKABLE QVariant get(int index) const;
|
Q_INVOKABLE QVariant get(int index) const;
|
||||||
Q_INVOKABLE void put(const QVariant &variant);
|
Q_INVOKABLE void put(const QVariant &variant);
|
||||||
EventType findByName(const QString &name);
|
EventType findByName(const QString &name);
|
||||||
EventType findById(const EventTypeId &id);
|
EventType findById(const EventTypeId &id);
|
||||||
|
EventType &operator[](const QString &name);
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(EventTypes)
|
Q_DECLARE_METATYPE(EventTypes)
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,16 @@ void InterfaceEventType::setOptional(bool optional)
|
|||||||
m_optional = optional;
|
m_optional = optional;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InterfaceEventType::loggingOverride() const
|
||||||
|
{
|
||||||
|
return m_loggingOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceEventType::setLoggingOverride(bool loggingOverride)
|
||||||
|
{
|
||||||
|
m_loggingOverride = loggingOverride;
|
||||||
|
}
|
||||||
|
|
||||||
InterfaceEventTypes::InterfaceEventTypes(const QList<InterfaceEventType> &other):
|
InterfaceEventTypes::InterfaceEventTypes(const QList<InterfaceEventType> &other):
|
||||||
QList<InterfaceEventType>(other)
|
QList<InterfaceEventType>(other)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,8 +11,12 @@ public:
|
|||||||
bool optional() const;
|
bool optional() const;
|
||||||
void setOptional(bool optional);
|
void setOptional(bool optional);
|
||||||
|
|
||||||
|
bool loggingOverride() const;
|
||||||
|
void setLoggingOverride(bool loggingOverride);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_optional = false;
|
bool m_optional = false;
|
||||||
|
bool m_loggingOverride = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InterfaceEventTypes: public QList<InterfaceEventType>
|
class InterfaceEventTypes: public QList<InterfaceEventType>
|
||||||
|
|||||||
@ -15,6 +15,16 @@ void InterfaceStateType::setOptional(bool optional)
|
|||||||
m_optional = optional;
|
m_optional = optional;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InterfaceStateType::loggingOverride() const
|
||||||
|
{
|
||||||
|
return m_loggingOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceStateType::setLoggingOverride(bool loggingOverride)
|
||||||
|
{
|
||||||
|
m_loggingOverride = loggingOverride;
|
||||||
|
}
|
||||||
|
|
||||||
InterfaceStateTypes::InterfaceStateTypes(const QList<InterfaceStateType> &other):
|
InterfaceStateTypes::InterfaceStateTypes(const QList<InterfaceStateType> &other):
|
||||||
QList<InterfaceStateType>(other)
|
QList<InterfaceStateType>(other)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,8 +11,12 @@ public:
|
|||||||
bool optional() const;
|
bool optional() const;
|
||||||
void setOptional(bool optional);
|
void setOptional(bool optional);
|
||||||
|
|
||||||
|
bool loggingOverride() const;
|
||||||
|
void setLoggingOverride(bool loggingOverride);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_optional = false;
|
bool m_optional = false;
|
||||||
|
bool m_loggingOverride = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InterfaceStateTypes: public QList<InterfaceStateType>
|
class InterfaceStateTypes: public QList<InterfaceStateType>
|
||||||
|
|||||||
@ -80,6 +80,16 @@ void State::setValue(const QVariant &value)
|
|||||||
m_value = value;
|
m_value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Types::StateValueFilter State::filter() const
|
||||||
|
{
|
||||||
|
return m_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::setFilter(Types::StateValueFilter filter)
|
||||||
|
{
|
||||||
|
m_filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Writes the stateTypeId, the deviceId and the value of the given \a state to \a dbg. */
|
/*! Writes the stateTypeId, the deviceId and the value of the given \a state to \a dbg. */
|
||||||
QDebug operator<<(QDebug dbg, const State &state)
|
QDebug operator<<(QDebug dbg, const State &state)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -42,6 +42,7 @@ class LIBNYMEA_EXPORT State
|
|||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QUuid stateTypeId READ stateTypeId)
|
Q_PROPERTY(QUuid stateTypeId READ stateTypeId)
|
||||||
Q_PROPERTY(QVariant value READ value)
|
Q_PROPERTY(QVariant value READ value)
|
||||||
|
Q_PROPERTY(Types::StateValueFilter filter READ filter)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
State();
|
State();
|
||||||
@ -53,10 +54,14 @@ public:
|
|||||||
QVariant value() const;
|
QVariant value() const;
|
||||||
void setValue(const QVariant &value);
|
void setValue(const QVariant &value);
|
||||||
|
|
||||||
|
Types::StateValueFilter filter() const;
|
||||||
|
void setFilter(Types::StateValueFilter filter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StateTypeId m_stateTypeId;
|
StateTypeId m_stateTypeId;
|
||||||
ThingId m_thingId;
|
ThingId m_thingId;
|
||||||
QVariant m_value;
|
QVariant m_value;
|
||||||
|
Types::StateValueFilter m_filter = Types::StateValueFilterNone;
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(State)
|
Q_DECLARE_METATYPE(State)
|
||||||
|
|
||||||
|
|||||||
@ -208,18 +208,24 @@ void StateType::setCached(bool cached)
|
|||||||
m_cached = cached;
|
m_cached = cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Returns a list of all valid properties a DeviceClass definition can have. */
|
bool StateType::suggestLogging() const
|
||||||
QStringList StateType::typeProperties()
|
|
||||||
{
|
{
|
||||||
return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue"
|
return m_logged;
|
||||||
<< "cached" << "unit" << "minValue" << "maxValue" << "possibleValues" << "writable"
|
|
||||||
<< "displayNameAction" << "ioType";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Returns a list of mandatory properties a DeviceClass definition must have. */
|
void StateType::setSuggestLogging(bool logged)
|
||||||
QStringList StateType::mandatoryTypeProperties()
|
|
||||||
{
|
{
|
||||||
return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue";
|
m_logged = logged;
|
||||||
|
}
|
||||||
|
|
||||||
|
Types::StateValueFilter StateType::filter() const
|
||||||
|
{
|
||||||
|
return m_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateType::setFilter(Types::StateValueFilter filter)
|
||||||
|
{
|
||||||
|
m_filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Returns true if this state type has an ID, a type and a name set. */
|
/*! Returns true if this state type has an ID, a type and a name set. */
|
||||||
@ -245,6 +251,16 @@ bool StateTypes::contains(const StateTypeId &stateTypeId)
|
|||||||
return false;
|
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
|
QVariant StateTypes::get(int index) const
|
||||||
{
|
{
|
||||||
return QVariant::fromValue(at(index));
|
return QVariant::fromValue(at(index));
|
||||||
@ -274,3 +290,15 @@ StateType StateTypes::findById(const StateTypeId &id)
|
|||||||
}
|
}
|
||||||
return StateType(StateTypeId());
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@ -93,8 +93,11 @@ public:
|
|||||||
bool cached() const;
|
bool cached() const;
|
||||||
void setCached(bool cached);
|
void setCached(bool cached);
|
||||||
|
|
||||||
static QStringList typeProperties();
|
bool suggestLogging() const;
|
||||||
static QStringList mandatoryTypeProperties();
|
void setSuggestLogging(bool logged);
|
||||||
|
|
||||||
|
Types::StateValueFilter filter() const;
|
||||||
|
void setFilter(Types::StateValueFilter filter);
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
@ -112,6 +115,8 @@ private:
|
|||||||
Types::IOType m_ioType = Types::IOTypeNone;
|
Types::IOType m_ioType = Types::IOTypeNone;
|
||||||
bool m_writable = false;
|
bool m_writable = false;
|
||||||
bool m_cached = true;
|
bool m_cached = true;
|
||||||
|
bool m_logged = false;
|
||||||
|
Types::StateValueFilter m_filter = Types::StateValueFilterNone;
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(StateType)
|
Q_DECLARE_METATYPE(StateType)
|
||||||
|
|
||||||
@ -123,10 +128,12 @@ public:
|
|||||||
StateTypes() = default;
|
StateTypes() = default;
|
||||||
StateTypes(const QList<StateType> &other);
|
StateTypes(const QList<StateType> &other);
|
||||||
bool contains(const StateTypeId &stateTypeId);
|
bool contains(const StateTypeId &stateTypeId);
|
||||||
|
bool contains(const QString &name);
|
||||||
Q_INVOKABLE QVariant get(int index) const;
|
Q_INVOKABLE QVariant get(int index) const;
|
||||||
Q_INVOKABLE void put(const QVariant &variant);
|
Q_INVOKABLE void put(const QVariant &variant);
|
||||||
StateType findByName(const QString &name);
|
StateType findByName(const QString &name);
|
||||||
StateType findById(const StateTypeId &id);
|
StateType findById(const StateTypeId &id);
|
||||||
|
StateType &operator[](const QString &name);
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(StateTypes)
|
Q_DECLARE_METATYPE(StateTypes)
|
||||||
|
|
||||||
|
|||||||
@ -173,6 +173,12 @@ public:
|
|||||||
IOTypeAnalogOutput
|
IOTypeAnalogOutput
|
||||||
};
|
};
|
||||||
Q_ENUM(IOType)
|
Q_ENUM(IOType)
|
||||||
|
|
||||||
|
enum StateValueFilter {
|
||||||
|
StateValueFilterNone,
|
||||||
|
StateValueFilterAdaptive
|
||||||
|
};
|
||||||
|
Q_ENUM(StateValueFilter)
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Types::InputType)
|
Q_DECLARE_METATYPE(Types::InputType)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
|
|||||||
|
|
||||||
# define protocol versions
|
# define protocol versions
|
||||||
JSON_PROTOCOL_VERSION_MAJOR=5
|
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}"
|
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
|
||||||
LIBNYMEA_API_VERSION_MAJOR=7
|
LIBNYMEA_API_VERSION_MAJOR=7
|
||||||
LIBNYMEA_API_VERSION_MINOR=0
|
LIBNYMEA_API_VERSION_MINOR=0
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
/* This file is generated by the nymea build system. Any changes to this file will *
|
/* 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
|
#ifndef EXTERNPLUGININFO_H
|
||||||
#define EXTERNPLUGININFO_H
|
#define EXTERNPLUGININFO_H
|
||||||
|
|||||||
@ -104,7 +104,7 @@ void HttpDaemon::readClient()
|
|||||||
} else if (stateTypeId == mockDoubleStateTypeId) {
|
} else if (stateTypeId == mockDoubleStateTypeId) {
|
||||||
stateValue.convert(QVariant::Double);
|
stateValue.convert(QVariant::Double);
|
||||||
}
|
}
|
||||||
qCDebug(dcMock()) << "Set state value" << stateValue;
|
qCDebug(dcMock()) << "Setting state value" << stateValue;
|
||||||
emit setState(stateTypeId, stateValue);
|
emit setState(stateTypeId, stateValue);
|
||||||
} else if (url.path() == "/generateevent") {
|
} else if (url.path() == "/generateevent") {
|
||||||
emit triggerEvent(EventTypeId(query.queryItemValue("eventtypeid")));
|
emit triggerEvent(EventTypeId(query.queryItemValue("eventtypeid")));
|
||||||
|
|||||||
@ -82,7 +82,8 @@
|
|||||||
"displayName": "Dummy int state",
|
"displayName": "Dummy int state",
|
||||||
"displayNameEvent": "Dummy int state changed",
|
"displayNameEvent": "Dummy int state changed",
|
||||||
"defaultValue": 10,
|
"defaultValue": 10,
|
||||||
"type": "int"
|
"type": "int",
|
||||||
|
"suggestLogging": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9dd6a97c-dfd1-43dc-acbd-367932742310",
|
"id": "9dd6a97c-dfd1-43dc-acbd-367932742310",
|
||||||
@ -152,7 +153,8 @@
|
|||||||
"minValue": 0,
|
"minValue": 0,
|
||||||
"maxValue": 100,
|
"maxValue": 100,
|
||||||
"defaultValue": 50,
|
"defaultValue": 50,
|
||||||
"writable": true
|
"writable": true,
|
||||||
|
"filter": "adaptive"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ebc41327-53d5-40c2-8e7b-1164a8ff359e",
|
"id": "ebc41327-53d5-40c2-8e7b-1164a8ff359e",
|
||||||
@ -307,7 +309,8 @@
|
|||||||
"displayName": "Dummy int state",
|
"displayName": "Dummy int state",
|
||||||
"displayNameEvent": "Dummy int state changed",
|
"displayNameEvent": "Dummy int state changed",
|
||||||
"defaultValue": 10,
|
"defaultValue": 10,
|
||||||
"type": "int"
|
"type": "int",
|
||||||
|
"suggestLogging": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "978b0ba5-d008-41bd-b63d-a3bd23cb6469",
|
"id": "978b0ba5-d008-41bd-b63d-a3bd23cb6469",
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
/* This file is generated by the nymea build system. Any changes to this file will *
|
/* 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
|
#ifndef PLUGININFO_H
|
||||||
#define PLUGININFO_H
|
#define PLUGININFO_H
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
5.3
|
5.4
|
||||||
{
|
{
|
||||||
"enums": {
|
"enums": {
|
||||||
"BasicType": [
|
"BasicType": [
|
||||||
@ -244,6 +244,10 @@
|
|||||||
"StateOperatorAnd",
|
"StateOperatorAnd",
|
||||||
"StateOperatorOr"
|
"StateOperatorOr"
|
||||||
],
|
],
|
||||||
|
"StateValueFilter": [
|
||||||
|
"StateValueFilterNone",
|
||||||
|
"StateValueFilterAdaptive"
|
||||||
|
],
|
||||||
"TagError": [
|
"TagError": [
|
||||||
"TagErrorNoError",
|
"TagErrorNoError",
|
||||||
"TagErrorThingNotFound",
|
"TagErrorThingNotFound",
|
||||||
@ -1215,6 +1219,17 @@
|
|||||||
"thingError": "$ref:ThingError"
|
"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": {
|
"Integrations.SetPluginConfiguration": {
|
||||||
"description": "Set a plugin's params.",
|
"description": "Set a plugin's params.",
|
||||||
"params": {
|
"params": {
|
||||||
@ -1225,6 +1240,17 @@
|
|||||||
"thingError": "$ref:ThingError"
|
"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": {
|
"Integrations.SetThingSettings": {
|
||||||
"description": "Change the settings of a thing.",
|
"description": "Change the settings of a thing.",
|
||||||
"params": {
|
"params": {
|
||||||
@ -2521,6 +2547,9 @@
|
|||||||
"o:settings": "$ref:ParamList",
|
"o:settings": "$ref:ParamList",
|
||||||
"r:deviceClassId": "Uuid",
|
"r:deviceClassId": "Uuid",
|
||||||
"r:id": "Uuid",
|
"r:id": "Uuid",
|
||||||
|
"r:o:loggedEventTypeIds": [
|
||||||
|
"Uuid"
|
||||||
|
],
|
||||||
"r:o:parentId": "Uuid",
|
"r:o:parentId": "Uuid",
|
||||||
"r:o:setupDisplayMessage": "String",
|
"r:o:setupDisplayMessage": "String",
|
||||||
"r:params": "$ref:ParamList",
|
"r:params": "$ref:ParamList",
|
||||||
@ -2774,6 +2803,7 @@
|
|||||||
"sslEnabled": "Bool"
|
"sslEnabled": "Bool"
|
||||||
},
|
},
|
||||||
"State": {
|
"State": {
|
||||||
|
"r:filter": "$ref:StateValueFilter",
|
||||||
"r:stateTypeId": "Uuid",
|
"r:stateTypeId": "Uuid",
|
||||||
"r:value": "Variant"
|
"r:value": "Variant"
|
||||||
},
|
},
|
||||||
@ -2833,6 +2863,9 @@
|
|||||||
"o:name": "String",
|
"o:name": "String",
|
||||||
"o:settings": "$ref:ParamList",
|
"o:settings": "$ref:ParamList",
|
||||||
"r:id": "Uuid",
|
"r:id": "Uuid",
|
||||||
|
"r:o:loggedEventTypeIds": [
|
||||||
|
"Uuid"
|
||||||
|
],
|
||||||
"r:o:parentId": "Uuid",
|
"r:o:parentId": "Uuid",
|
||||||
"r:o:setupDisplayMessage": "String",
|
"r:o:setupDisplayMessage": "String",
|
||||||
"r:params": "$ref:ParamList",
|
"r:params": "$ref:ParamList",
|
||||||
|
|||||||
@ -852,22 +852,6 @@ void TestJSONRPC::ruleActiveChangedNotifications()
|
|||||||
|
|
||||||
clientSpy.wait();
|
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();
|
spy.clear(); clientSpy.clear();
|
||||||
|
|
||||||
// set the rule inactive
|
// set the rule inactive
|
||||||
@ -889,22 +873,6 @@ void TestJSONRPC::ruleActiveChangedNotifications()
|
|||||||
clientSpy.wait();
|
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");
|
notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged");
|
||||||
verifyRuleError(response);
|
verifyRuleError(response);
|
||||||
|
|
||||||
@ -960,26 +928,10 @@ void TestJSONRPC::stateChangeEmitsNotifications()
|
|||||||
// Devices.StateChanged
|
// Devices.StateChanged
|
||||||
// Devices.EventTriggered
|
// Devices.EventTriggered
|
||||||
// Events.EventTriggered <-- deprecated
|
// Events.EventTriggered <-- deprecated
|
||||||
// Logging.LogEntryAdded
|
while (clientSpy.count() < 3) {
|
||||||
while (clientSpy.count() < 4) {
|
|
||||||
clientSpy.wait();
|
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
|
// Make sure the notification contains all the stuff we expect
|
||||||
QVariantList eventTriggeredVariants = checkNotifications(clientSpy, "Events.EventTriggered");
|
QVariantList eventTriggeredVariants = checkNotifications(clientSpy, "Events.EventTriggered");
|
||||||
QVERIFY2(!eventTriggeredVariants.isEmpty(), "Did not get Events.EventTriggered notification.");
|
QVERIFY2(!eventTriggeredVariants.isEmpty(), "Did not get Events.EventTriggered notification.");
|
||||||
|
|||||||
@ -71,6 +71,7 @@ private slots:
|
|||||||
void invalidFilter_data();
|
void invalidFilter_data();
|
||||||
void invalidFilter();
|
void invalidFilter();
|
||||||
|
|
||||||
|
void eventLogs_data();
|
||||||
void eventLogs();
|
void eventLogs();
|
||||||
|
|
||||||
void actionLog();
|
void actionLog();
|
||||||
@ -198,6 +199,7 @@ void TestLogging::systemLogs()
|
|||||||
qWarning() << "Restarting server";
|
qWarning() << "Restarting server";
|
||||||
restartServer();
|
restartServer();
|
||||||
qWarning() << "Restart done";
|
qWarning() << "Restart done";
|
||||||
|
waitForDBSync();
|
||||||
|
|
||||||
// there should be 2 log entries, one for shutdown, one for startup (from server restart)
|
// there should be 2 log entries, one for shutdown, one for startup (from server restart)
|
||||||
response = injectAndWait("Logging.GetLogEntries", params);
|
response = injectAndWait("Logging.GetLogEntries", params);
|
||||||
@ -261,24 +263,53 @@ void TestLogging::invalidFilter()
|
|||||||
qDebug() << response.toMap().value("error").toString();
|
qDebug() << response.toMap().value("error").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestLogging::eventLogs_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<StateTypeId>("stateTypeId");
|
||||||
|
QTest::addColumn<QVariant>("initValue");
|
||||||
|
QTest::addColumn<QVariant>("newValue");
|
||||||
|
QTest::addColumn<bool>("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()
|
void TestLogging::eventLogs()
|
||||||
{
|
{
|
||||||
|
QFETCH(StateTypeId, stateTypeId);
|
||||||
|
QFETCH(QVariant, initValue);
|
||||||
|
QFETCH(QVariant, newValue);
|
||||||
|
QFETCH(bool, expectLogEntry);
|
||||||
|
|
||||||
QList<Thing*> devices = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
QList<Thing*> devices = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
||||||
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
||||||
Thing *device = devices.first();
|
Thing *device = devices.first();
|
||||||
|
|
||||||
enableNotifications({"Events", "Logging"});
|
|
||||||
|
|
||||||
// Setup connection to mock client
|
// Setup connection to mock client
|
||||||
QNetworkAccessManager nam;
|
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)));
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||||
|
|
||||||
// trigger event in mock device
|
// trigger state change in mock device
|
||||||
int port = device->paramValue(mockThingHttpportParamTypeId).toInt();
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(stateTypeId.toString()).arg(newValue.toString())));
|
||||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(port).arg(mockEvent1EventTypeId.toString())));
|
reply = nam.get(request);
|
||||||
QNetworkReply *reply = nam.get(request);
|
{
|
||||||
|
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
|
||||||
|
finishedSpy.wait();
|
||||||
|
}
|
||||||
|
|
||||||
// Lets wait for the notification
|
// Lets wait for the notification
|
||||||
QTest::qWait(200);
|
QTest::qWait(200);
|
||||||
@ -286,41 +317,42 @@ void TestLogging::eventLogs()
|
|||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
// Make sure the logg notification contains all the stuff we expect
|
// Make sure the logg notification contains all the stuff we expect
|
||||||
QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded");
|
QVariantList logEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded");
|
||||||
QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification.");
|
qDebug() << "got" << logEntryAddedVariants.count() << "Logging.LogEntryAdded notifications";
|
||||||
qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded notifications";
|
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded";
|
qDebug() << "got" << logEntryAddedVariants.count() << "Logging.LogEntryAdded";
|
||||||
foreach (const QVariant &loggEntryAddedVariant, loggEntryAddedVariants) {
|
foreach (const QVariant &logEntryAddedVariant, logEntryAddedVariants) {
|
||||||
QVariantMap logEntry = loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap();
|
QVariantMap logEntry = logEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap();
|
||||||
if (logEntry.value("thingId").toUuid() == device->id()) {
|
if (logEntry.value("thingId").toUuid() == device->id()) {
|
||||||
found = true;
|
found = true;
|
||||||
// Make sure the notification contains all the stuff we expect
|
// 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("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));
|
QCOMPARE(logEntry.value("loggingLevel").toString(), enumValueName(Logging::LoggingLevelInfo));
|
||||||
break;
|
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
|
if (expectLogEntry) {
|
||||||
QVariantMap params;
|
// get this logentry with filter
|
||||||
params.insert("thingIds", QVariantList() << device->id());
|
QVariantMap params;
|
||||||
params.insert("loggingSources", QVariantList() << enumValueName(Logging::LoggingSourceEvents));
|
params.insert("thingIds", QVariantList() << device->id());
|
||||||
params.insert("eventTypes", QVariantList() << enumValueName(Logging::LoggingEventTypeTrigger));
|
params.insert("loggingSources", QVariantList() << enumValueName(Logging::LoggingSourceStates));
|
||||||
params.insert("typeIds", QVariantList() << mockEvent1EventTypeId);
|
params.insert("eventTypes", QVariantList() << enumValueName(Logging::LoggingEventTypeTrigger));
|
||||||
|
params.insert("typeIds", QVariantList() << stateTypeId);
|
||||||
|
|
||||||
QVariant response = injectAndWait("Logging.GetLogEntries", params);
|
QVariant response = injectAndWait("Logging.GetLogEntries", params);
|
||||||
verifyLoggingError(response);
|
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
|
// disable notifications
|
||||||
QCOMPARE(disableNotifications(), true);
|
QCOMPARE(disableNotifications(), true);
|
||||||
@ -629,7 +661,7 @@ void TestLogging::testHouseKeeping()
|
|||||||
// Trigger something that creates a logging entry
|
// Trigger something that creates a logging entry
|
||||||
QNetworkAccessManager nam;
|
QNetworkAccessManager nam;
|
||||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
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);
|
QNetworkReply *reply = nam.get(request);
|
||||||
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
|
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
|
||||||
spy.wait();
|
spy.wait();
|
||||||
|
|||||||
Reference in New Issue
Block a user