more work... not really working yet

pull/387/head
Michael Zanetti 2020-12-23 13:00:08 +01:00
parent 56448543df
commit ae38e185b6
34 changed files with 390 additions and 123 deletions

View File

@ -650,6 +650,14 @@ ThingPairingInfo *ThingManagerImplementation::confirmPairing(const PairingTransa
ParamList settings = buildParams(thingClass.settingsTypes(), ParamList());
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);
connect(info, &ThingSetupInfo::finished, thing, [this, info, externalInfo, addNewThing](){
@ -746,6 +754,14 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi
ParamList settings = buildParams(thingClass.settingsTypes(), ParamList());
thing->setSettings(settings);
QList<EventTypeId> loggedEventTypeIds;
foreach (const EventType &eventType, thingClass.eventTypes()) {
if (eventType.suggestLogging()) {
loggedEventTypeIds.append(eventType.id());
}
}
thing->setLoggedEventTypeIds(loggedEventTypeIds);
ThingSetupInfo *info = setupThing(thing);
connect(info, &ThingSetupInfo::finished, this, [this, info](){
if (info->status() != Thing::ThingErrorNoError) {
@ -1258,6 +1274,10 @@ ThingActionInfo *ThingManagerImplementation::executeAction(const Action &action)
return info;
}
connect(info, &ThingActionInfo::finished, this, [=](){
emit actionExecuted(action, info->status());
});
plugin->executeAction(info);
return info;
@ -1541,6 +1561,14 @@ void ThingManagerImplementation::loadConfiguredThings()
thing->setSettings(thingSettings);
QList<EventTypeId> loggedEventTypeIds;
foreach (const EventType &eventType, thingClass.eventTypes()) {
if (eventType.suggestLogging()) {
loggedEventTypeIds.append(eventType.id());
}
}
thing->setLoggedEventTypeIds(loggedEventTypeIds);
settings.endGroup(); // ThingId
// We always add the thing to the list in this case. If it's in the stored things
@ -1659,6 +1687,14 @@ void ThingManagerImplementation::onAutoThingsAppeared(const ThingDescriptors &th
thing->setSettings(settings);
thing->setParentId(thingDescriptor.parentId());
QList<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();
ThingSetupInfo *info = setupThing(thing);
@ -1725,7 +1761,7 @@ void ThingManagerImplementation::cleanupThingStateCache()
}
}
void ThingManagerImplementation::onEventTriggered(const Event &event)
void ThingManagerImplementation::onEventTriggered(Event event)
{
// Doing some sanity checks here...
Thing *thing = m_configuredThings.value(event.thingId());
@ -1738,7 +1774,12 @@ void ThingManagerImplementation::onEventTriggered(const Event &event)
qCWarning(dcThingManager()) << "The given thing does not have an event type of id " + event.eventTypeId().toString() + ". Not forwarding event.";
return;
}
// All good, forward the event
// configure logging
if (thing->loggedEventTypeIds().contains(event.eventTypeId())) {
event.setLogged(true);
}
// Forward the event
emit eventTriggered(event);
}

View File

@ -140,7 +140,7 @@ private slots:
void onAutoThingDisappeared(const ThingId &thingId);
void onLoaded();
void cleanupThingStateCache();
void onEventTriggered(const Event &event);
void onEventTriggered(Event event);
// Only connect this to Things. It will query the sender()
void slotThingStateValueChanged(const StateTypeId &stateTypeId, const QVariant &value);

View File

@ -107,7 +107,7 @@ JsonReply* ActionHandler::ExecuteAction(const QVariantMap &params, const JsonCon
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
ThingActionInfo *info = NymeaCore::instance()->executeAction(action);
ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action);
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
QVariantMap data;
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));

View File

@ -62,6 +62,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
registerEnum<Types::Unit>();
registerEnum<Types::InputType>();
registerEnum<Types::IOType>();
registerEnum<Types::StateValueFilter>();
registerEnum<RuleEngine::RemovePolicy>();
registerEnum<BrowserItem::BrowserIcon>();
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
@ -900,7 +901,7 @@ JsonReply *DeviceHandler::ExecuteAction(const QVariantMap &params, const JsonCon
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
ThingActionInfo *info = NymeaCore::instance()->executeAction(action);
ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action);
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
QVariantMap data;
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));

View File

@ -61,6 +61,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
registerEnum<Types::Unit>();
registerEnum<Types::InputType>();
registerEnum<Types::IOType>();
registerEnum<Types::StateValueFilter>();
registerEnum<RuleEngine::RemovePolicy>();
registerEnum<BrowserItem::BrowserIcon>();
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
@ -958,7 +959,7 @@ JsonReply *IntegrationsHandler::ExecuteAction(const QVariantMap &params, const J
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
ThingActionInfo *info = NymeaCore::instance()->executeAction(action);
ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action);
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
QVariantMap data;
data.insert("thingError", enumValueName(info->status()));

View File

@ -34,6 +34,8 @@
#include "logging.h"
#include "logvaluetool.h"
#include "integrations/thingmanager.h"
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlDriver>
@ -104,6 +106,13 @@ LogEngine::~LogEngine()
m_db.close();
}
void LogEngine::setThingManager(ThingManager *thingManager)
{
m_thingManager = thingManager;
connect(thingManager, &ThingManager::eventTriggered, this, &LogEngine::logEvent);
connect(thingManager, &ThingManager::actionExecuted, this, &LogEngine::logAction);
}
LogEntriesFetchJob *LogEngine::fetchLogEntries(const LogFilter &filter)
{
QList<LogEntry> results;
@ -222,6 +231,10 @@ void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::
void LogEngine::logEvent(const Event &event)
{
if (!event.logged()) {
return;
}
QVariantList valueList;
Logging::LoggingSource sourceType;
if (event.isStateChangeEvent()) {
@ -249,9 +262,10 @@ void LogEngine::logEvent(const Event &event)
appendLogEntry(entry);
}
void LogEngine::logAction(const Action &action, Logging::LoggingLevel level, int errorCode)
void LogEngine::logAction(const Action &action, Thing::ThingError status)
{
LogEntry entry(level, Logging::LoggingSourceActions, errorCode);
Logging::LoggingLevel level = status == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert;
LogEntry entry(QDateTime::currentDateTime(), level, Logging::LoggingSourceActions, status);
entry.setTypeId(action.actionTypeId());
entry.setThingId(action.thingId());

View File

@ -38,6 +38,7 @@
#include "types/browseritemaction.h"
#include "types/browseraction.h"
#include "ruleengine/rule.h"
#include "integrations/thingmanager.h"
#include <QObject>
#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();
void setThingManager(ThingManager *thingManager);
LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter());
ThingsFetchJob *fetchThings();
@ -68,9 +71,11 @@ public:
void setMaxLogEntries(int maxLogEntries, int trimSize);
void clearDatabase();
void removeThingLogs(const ThingId &thingId);
void removeRuleLogs(const RuleId &ruleId);
public slots:
void logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level = Logging::LoggingLevelInfo);
void logEvent(const Event &event);
void logAction(const Action &action, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
void logBrowserAction(const BrowserAction &browserAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
void logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
void logRuleTriggered(const Rule &rule);
@ -78,8 +83,10 @@ public:
void logRuleEnabledChanged(const Rule &rule, const bool &enabled);
void logRuleActionsExecuted(const Rule &rule);
void logRuleExitActionsExecuted(const Rule &rule);
void removeThingLogs(const ThingId &thingId);
void removeRuleLogs(const RuleId &ruleId);
private slots:
void logEvent(const Event &event);
void logAction(const Action &action, Thing::ThingError status);
signals:
void logEntryAdded(const LogEntry &logEntry);
@ -114,6 +121,8 @@ private:
bool m_initialized = false;
bool m_dbMalformed = false;
ThingManager *m_thingManager = nullptr;
// When maxQueueLength is exceeded, jobs will be flagged and discarded if this source logs more events
int m_maxQueueLength;
QHash<QString, QList<DatabaseJob*>> m_flaggedJobs;

View File

@ -99,9 +99,6 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
}
m_timeManager = new TimeManager(this);
qCDebug(dcCore) << "Creating Log Engine";
m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this);
qCDebug(dcCore()) << "Creating User Manager";
m_userManager = new UserManager(NymeaSettings::settingsPath() + "/user-db.sqlite", this);
@ -120,6 +117,10 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
qCDebug(dcCore) << "Creating Rule Engine";
m_ruleEngine = new RuleEngine(this);
qCDebug(dcCore) << "Creating Log Engine";
m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this);
m_logger->setThingManager(m_thingManager);
qCDebug(dcCore()) << "Creating Script Engine";
m_scriptEngine = new ScriptEngine(m_thingManager, this);
m_serverManager->jsonServer()->registerHandler(new ScriptsHandler(m_scriptEngine, m_scriptEngine));
@ -380,20 +381,6 @@ Thing::ThingError NymeaCore::removeConfiguredThing(const ThingId &thingId, const
return removeError;
}
ThingActionInfo* NymeaCore::executeAction(const Action &action)
{
ThingActionInfo *info = m_thingManager->executeAction(action);
connect(info, &ThingActionInfo::finished, this, [this, info](){
if (info->status() == Thing::ThingErrorNoError) {
m_logger->logAction(info->action());
} else {
m_logger->logAction(info->action(), Logging::LoggingLevelAlert, info->status());
}
});
return info;
}
BrowserActionInfo* NymeaCore::executeBrowserItem(const BrowserAction &browserAction)
{
BrowserActionInfo *info = m_thingManager->executeBrowserItem(browserAction);
@ -512,7 +499,7 @@ void NymeaCore::executeRuleActions(const QList<RuleAction> ruleActions)
foreach (const Action &action, actions) {
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
ThingActionInfo *info = executeAction(action);
ThingActionInfo *info = m_thingManager->executeAction(action);
connect(info, &ThingActionInfo::finished, this, [info](){
if (info->status() != Thing::ThingErrorNoError) {
qCWarning(dcRuleEngine) << "Error executing action:" << info->status() << info->displayMessage();
@ -660,17 +647,6 @@ ZigbeeManager *NymeaCore::zigbeeManager() const
void NymeaCore::gotEvent(const Event &event)
{
Thing *thing = m_thingManager->findConfiguredThing(event.thingId());
ThingClass thingClass = thing ? thing->thingClass() : ThingClass();
EventType eventType = thingClass.eventTypes().findById(event.eventTypeId());
foreach (const QString &interfaceName, thingClass.interfaces()) {
Interface iface = m_thingManager->supportedInterfaces().findByName(interfaceName);
if (iface.eventTypes().findByName(eventType.name()).logged()) {
m_logger->logEvent(event);
break;
}
}
emit eventTriggered(event);
QList<RuleAction> actions;

View File

@ -84,7 +84,6 @@ public:
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);
ThingActionInfo *executeAction(const Action &action);
BrowserActionInfo* executeBrowserItem(const BrowserAction &browserAction);
BrowserItemActionInfo* executeBrowserItemAction(const BrowserItemAction &browserItemAction);

View File

@ -469,6 +469,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
break;
}
stateType.setIOType(ioType);
stateType.setSuggestLogging(st.value("suggestLogging").toBool());
}
stateTypes.append(stateType);
@ -485,6 +486,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
paramType.setUnit(stateType.unit());
eventType.setParamTypes(QList<ParamType>() << paramType);
eventType.setIndex(stateType.index());
eventType.setSuggestLogging(st.value("suggestLogging").toBool());
eventTypes.append(eventType);
// ActionTypes for writeable StateTypes
@ -497,7 +499,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
actionTypes.append(actionType);
}
}
thingClass.setStateTypes(stateTypes);
// ActionTypes
index = 0;
@ -544,7 +545,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
actionTypes.append(actionType);
}
thingClass.setActionTypes(actionTypes);
// EventTypes
index = 0;
@ -580,6 +580,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
EventType eventType(eventTypeId);
eventType.setName(eventTypeName);
eventType.setDisplayName(et.value("displayName").toString());
eventType.setSuggestLogging(et.value("suggestLogging").toBool());
eventType.setIndex(index++);
QPair<bool, QList<ParamType> > paramVerification = parseParamTypes(et.value("paramTypes").toArray());
@ -590,7 +591,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
}
eventTypes.append(eventType);
}
thingClass.setEventTypes(eventTypes);
// BrowserItemActionTypes
index = 0;
@ -637,7 +637,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
browserItemActionTypes.append(actionType);
}
thingClass.setBrowserItemActionTypes(browserItemActionTypes);
// Read interfaces
QStringList interfaces;
@ -646,22 +645,18 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
if (!iface.isValid()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" uses non-existing interface \"" + value.toString() + "\"");
hasError = true;
continue;
}
StateTypes stateTypes(thingClass.stateTypes());
ActionTypes actionTypes(thingClass.actionTypes());
EventTypes eventTypes(thingClass.eventTypes());
foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) {
StateType stateType = stateTypes.findByName(ifaceStateType.name());
if (stateType.id().isNull()) {
if (!stateTypes.contains(ifaceStateType.name())) {
if (!ifaceStateType.optional()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\"");
hasError = true;
} else {
continue;
}
continue;
}
StateType &stateType = stateTypes[ifaceStateType.name()];
if (ifaceStateType.type() != stateType.type()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching type: \"" + QVariant::typeToName(stateType.type()) + "\" != \"" + QVariant::typeToName(ifaceStateType.type()) + "\"");
hasError = true;
@ -697,18 +692,22 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching unit: \"" + unitEnum.valueToKey(ifaceStateType.unit()) + "\" != \"" + unitEnum.valueToKey(stateType.unit()));
hasError = true;
}
// Override logged property as the interface has higher priority than the plugin dev
if (ifaceStateType.loggingOverride()) {
stateType.setSuggestLogging(ifaceStateType.suggestLogging());
}
}
foreach (const InterfaceActionType &ifaceActionType, iface.actionTypes()) {
ActionType actionType = actionTypes.findByName(ifaceActionType.name());
if (actionType.id().isNull()) {
if (!actionTypes.contains(ifaceActionType.name())) {
if (!ifaceActionType.optional()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\"");
hasError = true;
} else {
continue;
}
continue;
}
ActionType &actionType = actionTypes[ifaceActionType.name()];
// Verify the params as required by the interface are available
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name());
@ -779,15 +778,14 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
}
foreach (const InterfaceEventType &ifaceEventType, iface.eventTypes()) {
EventType eventType = eventTypes.findByName(ifaceEventType.name());
if (!eventType.isValid()) {
if (!eventTypes.contains(ifaceEventType.name())) {
if (!ifaceEventType.optional()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\"");
hasError = true;
} else {
continue;
}
continue;
}
EventType &eventType = eventTypes[ifaceEventType.name()];
// Verify all the params as required by the interface are available
foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) {
@ -842,6 +840,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
}
}
// Override logging
if (ifaceEventType.loggingOverride()) {
eventType.setSuggestLogging(ifaceEventType.suggestLogging());
}
// Note: No need to check for default values (as with actions) for additional params as
// an emitted event always needs to have params filled with values. The client might use them or not...
}
@ -851,6 +854,11 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
interfaces.removeDuplicates();
thingClass.setInterfaces(interfaces);
thingClass.setStateTypes(stateTypes);
thingClass.setActionTypes(actionTypes);
thingClass.setEventTypes(eventTypes);
thingClass.setBrowserItemActionTypes(browserItemActionTypes);
m_thingClasses.append(thingClass);
}
}

View File

@ -0,0 +1,6 @@
#include "statevaluefilter.h"
StateValueFilter::StateValueFilter()
{
}

View File

@ -0,0 +1,18 @@
#ifndef STATEVALUEFILTER_H
#define STATEVALUEFILTER_H
#include <QVariant>
class StateValueFilter
{
public:
StateValueFilter();
virtual ~StateValueFilter();
virtual void addValue(const QVariant &value) = 0;
virtual QVariant filteredValue() const = 0;
};
#endif // STATEVALUEFILTER_H

View File

@ -0,0 +1,11 @@
#include "statevaluefilteradaptive.h"
StateValueFilterAdaptive::StateValueFilterAdaptive()
{
}
void StateValueFilterAdaptive::addValue(const QVariant &value)
{
}

View File

@ -0,0 +1,15 @@
#ifndef STATEVALUEFILTERADAPTIVE_H
#define STATEVALUEFILTERADAPTIVE_H
#include "statevaluefilter.h"
class StateValueFilterAdaptive : public StateValueFilter
{
public:
StateValueFilterAdaptive();
void addValue(const QVariant &value) override;
QVariant filteredValue() const override;
};
#endif // STATEVALUEFILTERADAPTIVE_H

View File

@ -133,6 +133,7 @@
#include "thing.h"
#include "types/event.h"
#include "loggingcategories.h"
#include "statevaluefilters/statevaluefilteradaptive.h"
#include <QDebug>
@ -334,9 +335,6 @@ void Thing::setStateValue(const StateTypeId &stateTypeId, const QVariant &value)
}
for (int i = 0; i < m_states.count(); ++i) {
if (m_states.at(i).stateTypeId() == stateTypeId) {
if (m_states.at(i).value() == value)
return;
QVariant newValue = value;
if (!newValue.convert(stateType.type())) {
qCWarning(dcThing()).nospace() << m_name << ": Invalid value " << value << " for state " << stateType.name() << ". Type mismatch. Expected type: " << QVariant::typeToName(stateType.type()) << " (Discarding change)";
@ -355,8 +353,13 @@ void Thing::setStateValue(const StateTypeId &stateTypeId, const QVariant &value)
return;
}
QVariant oldValue = m_states.at(i).value();
StateValueFilter *filter = m_stateValueFilters.value(stateTypeId);
if (filter) {
filter->addValue(newValue);
newValue = filter->filteredValue();
}
QVariant oldValue = m_states.at(i).value();
if (oldValue == newValue) {
qCDebug(dcThing()).nospace() << m_name << ": Discarding state change for " << stateType.name() << " as the value did not actually change. Old value:" << oldValue << "New value:" << newValue;
return;
@ -383,22 +386,9 @@ State Thing::state(const StateTypeId &stateTypeId) const
return State(StateTypeId(), ThingId());
}
void Thing::setStateLogged(const StateTypeId &stateTypeId, bool logged)
QList<EventTypeId> Thing::loggedEventTypeIds() const
{
for (int i = 0; i < m_states.count(); i++) {
if (m_states.at(i).stateTypeId() == stateTypeId) {
m_states[i].setLogged(logged);
}
}
}
void Thing::setStateFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter)
{
for (int i = 0; i < m_states.count(); i++) {
if (m_states.at(i).stateTypeId() == stateTypeId) {
m_states[i].setFilter(filter);
}
}
return m_loggedEventTypeIds;
}
/*! Returns the \l{ThingId} of the parent of this thing. If the parentId
@ -460,6 +450,27 @@ void Thing::setSetupStatus(Thing::ThingSetupStatus status, Thing::ThingError set
emit setupStatusChanged();
}
void Thing::setLoggedEventTypeIds(const QList<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)
{
foreach (Thing* thing, other) {

View File

@ -45,6 +45,7 @@
#include <QVariant>
class IntegrationPlugin;
class StateValueFilter;
class LIBNYMEA_EXPORT Thing: public QObject
{
@ -60,6 +61,7 @@ class LIBNYMEA_EXPORT Thing: public QObject
Q_PROPERTY(QString setupDisplayMessage READ setupDisplayMessage NOTIFY setupStatusChanged USER true)
Q_PROPERTY(ThingError setupError READ setupError NOTIFY setupStatusChanged)
Q_PROPERTY(QUuid parentId READ parentId USER true)
Q_PROPERTY(QList<EventTypeId> loggedEventTypeIds READ loggedEventTypeIds USER true)
public:
enum ThingError {
@ -133,8 +135,7 @@ public:
Q_INVOKABLE State state(const StateTypeId &stateTypeId) const;
void setStateLogged(const StateTypeId &stateTypeId, bool logged);
void setStateFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter);
QList<EventTypeId> loggedEventTypeIds() const;
ThingId parentId() const;
void setParentId(const ThingId &parentId);
@ -164,6 +165,8 @@ private:
Thing(const PluginId &pluginId, const ThingClass &thingClass, QObject *parent = nullptr);
void setSetupStatus(ThingSetupStatus status, ThingError setupError, const QString &displayMessage = QString());
void setLoggedEventTypeIds(const QList<EventTypeId> loggedEventTypeIds);
void setStateValueFilter(const StateTypeId &stateTypeId, Types::StateValueFilter filter);
private:
ThingClass m_thingClass;
@ -179,6 +182,9 @@ private:
ThingSetupStatus m_setupStatus = ThingSetupStatusNone;
ThingError m_setupError = ThingErrorNoError;
QString m_setupDisplayMessage;
QList<EventTypeId> m_loggedEventTypeIds;
QHash<StateTypeId, StateValueFilter*> m_stateValueFilters;
};
QDebug operator<<(QDebug dbg, Thing *device);

View File

@ -116,6 +116,7 @@ signals:
void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value);
void ioConnectionAdded(const IOConnection &ioConnection);
void ioConnectionRemoved(const IOConnectionId &ioConnectionId);
void actionExecuted(const Action &action, Thing::ThingError status);
};
#endif // THINGMANAGER_H

View File

@ -200,7 +200,10 @@ Interface ThingUtils::loadInterface(const QString &name)
stateType.setMinValue(stateVariant.toMap().value("minValue"));
stateType.setMaxValue(stateVariant.toMap().value("maxValue"));
stateType.setOptional(stateVariant.toMap().value("optional", false).toBool());
stateType.setLogged(stateVariant.toMap().value("logged", false).toBool());
if (stateVariant.toMap().contains("logged")) {
stateType.setLoggingOverride(true);
stateType.setSuggestLogging(stateVariant.toMap().value("logged", false).toBool());
}
if (stateVariant.toMap().contains("unit")) {
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray());
@ -215,7 +218,8 @@ Interface ThingUtils::loadInterface(const QString &name)
InterfaceEventType stateChangeEventType;
stateChangeEventType.setName(stateType.name());
stateChangeEventType.setOptional(stateType.optional());
stateChangeEventType.setLogged(stateType.logged());
stateChangeEventType.setSuggestLogging(stateType.suggestLogging());
stateChangeEventType.setLoggingOverride(stateType.loggingOverride());
ParamType stateChangeEventParamType;
stateChangeEventParamType.setName(stateType.name());
stateChangeEventParamType.setType(stateType.type());
@ -238,6 +242,10 @@ Interface ThingUtils::loadInterface(const QString &name)
InterfaceActionType actionType;
actionType.setName(actionVariant.toMap().value("name").toString());
actionType.setOptional(actionVariant.toMap().value("optional").toBool());
// if (actionVariant.toMap().contains("logged")) {
// actionType.setLoggingOverride(true);
// actionType.setSuggestLogging(actionVariant.toMap().value("logged").toBool());
// }
ParamTypes paramTypes;
foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) {
ParamType paramType;
@ -256,7 +264,10 @@ Interface ThingUtils::loadInterface(const QString &name)
InterfaceEventType eventType;
eventType.setName(eventVariant.toMap().value("name").toString());
eventType.setOptional(eventVariant.toMap().value("optional").toBool());
eventType.setLogged(eventVariant.toMap().value("logged").toBool());
if (eventVariant.toMap().contains("logged")) {
eventType.setLoggingOverride(true);
eventType.setSuggestLogging(eventVariant.toMap().value("logged").toBool());
}
ParamTypes paramTypes;
foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) {
ParamType paramType;

View File

@ -29,7 +29,7 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "jsonhandler.h"
#include "typeutils.h"
#include "loggingcategories.h"
#include <QDebug>
@ -207,6 +207,9 @@ void JsonHandler::registerObject(const QMetaObject &metaObject)
typeName = QString("$ref:BasicType");
} else if (QString(metaProperty.typeName()).startsWith("QList")) {
QString elementType = QString(metaProperty.typeName()).remove("QList<").remove(">");
if (elementType == "EventTypeId" || elementType == "StateTypeId" || elementType == "ActionTypeId") {
elementType = "QUuid";
}
QVariant::Type variantType = QVariant::nameToType(elementType.toUtf8());
typeName = QVariantList() << enumValueName(variantTypeToBasicType(variantType));
} else {
@ -344,6 +347,18 @@ QVariant JsonHandler::pack(const QMetaObject &metaObject, const void *value) con
foreach (const QUuid &entry, propertyValue.value<QList<QUuid>>()) {
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 {
Q_ASSERT_X(false, this->metaObject()->className(), QString("Unhandled list type: %1").arg(propertyTypeName).toUtf8());
qCWarning(dcJsonRpc()) << "Cannot pack property of unhandled list type" << propertyTypeName;
@ -455,7 +470,10 @@ QVariant JsonHandler::unpack(const QMetaObject &metaObject, const QVariant &valu
intList.append(val.toInt());
}
metaProperty.writeOnGadget(ptr, QVariant::fromValue(intList));
} else if (metaProperty.typeName() == QStringLiteral("QList<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;
foreach (const QVariant &val, variant.toList()) {
uuidList.append(val.toUuid());

View File

@ -126,6 +126,8 @@ SOURCES += \
integrations/thingsetupinfo.cpp \
integrations/thingutils.cpp \
integrations/servicedata.cpp \
integrations/statevaluefilters/statevaluefilter.cpp \
integrations/statevaluefilters/statevaluefilteradaptive.cpp \
jsonrpc/jsoncontext.cpp \
jsonrpc/jsonhandler.cpp \
jsonrpc/jsonreply.cpp \

View File

@ -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
{
return QVariant::fromValue(at(index));
@ -160,8 +180,21 @@ ActionType ActionTypes::findById(const ActionTypeId &id)
return ActionType(ActionTypeId());
}
ActionType &ActionTypes::operator[](const QString &name)
{
int index = -1;
for (int i = 0; i < count(); i++) {
if (at(i).name() == name) {
index = i;
break;
}
}
return QList::operator[](index);
}
QDebug operator<<(QDebug dbg, const ActionType &actionType)
{
dbg.nospace().noquote() << "ActionType: " << actionType.name() << actionType.displayName() << actionType.id();
return dbg;
}

View File

@ -84,10 +84,13 @@ class ActionTypes: public QList<ActionType>
public:
ActionTypes() = default;
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 void put(const QVariant &variant);
ActionType findByName(const QString &name);
ActionType findById(const ActionTypeId &id);
ActionType &operator[](const QString &name);
};
Q_DECLARE_METATYPE(ActionTypes)

View File

@ -126,6 +126,16 @@ bool Event::isStateChangeEvent() const
return m_isStateChangeEvent;
}
bool Event::logged() const
{
return m_logged;
}
void Event::setLogged(bool logged)
{
m_logged = logged;
}
/*! Compare this Event to the Event given by \a other.
* Events are equal (returns true) if eventTypeId, deviceId and params match. */
bool Event::operator ==(const Event &other) const

View File

@ -46,6 +46,7 @@ class LIBNYMEA_EXPORT Event
Q_PROPERTY(QUuid thingId READ thingId)
Q_PROPERTY(QUuid deviceId READ thingId REVISION 1)
Q_PROPERTY(ParamList params READ params)
Q_PROPERTY(bool logged READ logged)
public:
Event();
Event(const EventTypeId &eventTypeId, const ThingId &thingId, const ParamList &params = ParamList(), bool isStateChangeEvent = false);
@ -65,12 +66,16 @@ public:
bool isStateChangeEvent() const;
bool logged() const;
void setLogged(bool logged);
private:
EventTypeId m_eventTypeId;
ThingId m_thingId;
ParamList m_params;
bool m_isStateChangeEvent;
bool m_logged = false;
};
Q_DECLARE_METATYPE(Event)
QDebug operator<<(QDebug dbg, const Event &event);

View File

@ -110,6 +110,16 @@ void EventType::setParamTypes(const 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 */
bool EventType::isValid() const
{
@ -130,11 +140,31 @@ QStringList EventType::mandatoryTypeProperties()
EventTypes::EventTypes(const QList<EventType> &other)
{
foreach (const EventType &at, other) {
append(at);
foreach (const EventType &et, other) {
append(et);
}
}
bool EventTypes::contains(const EventTypeId &id) const
{
foreach (const EventType &eventType, *this) {
if (eventType.id() == id) {
return true;
}
}
return false;
}
bool EventTypes::contains(const QString &name) const
{
foreach (const EventType &eventType, *this) {
if (eventType.name() == name) {
return true;
}
}
return false;
}
QVariant EventTypes::get(int index) const
{
return QVariant::fromValue(at(index));
@ -164,3 +194,15 @@ EventType EventTypes::findById(const EventTypeId &id)
}
return EventType(EventTypeId());
}
EventType &EventTypes::operator[](const QString &name)
{
int index = -1;
for (int i = 0; i < count(); i++) {
if (at(i).name() == name) {
index = i;
break;
}
}
return QList::operator[](index);
}

View File

@ -64,6 +64,9 @@ public:
ParamTypes paramTypes() const;
void setParamTypes(const ParamTypes &paramTypes);
bool suggestLogging() const;
void setSuggestLogging(bool logged);
bool isValid() const;
static QStringList typeProperties();
@ -75,6 +78,7 @@ private:
QString m_displayName;
int m_index;
QList<ParamType> m_paramTypes;
bool m_logged = false;
};
Q_DECLARE_METATYPE(EventType)
@ -85,10 +89,13 @@ class EventTypes: public QList<EventType>
public:
EventTypes() = default;
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 void put(const QVariant &variant);
EventType findByName(const QString &name);
EventType findById(const EventTypeId &id);
EventType &operator[](const QString &name);
};
Q_DECLARE_METATYPE(EventTypes)

View File

@ -15,14 +15,14 @@ void InterfaceEventType::setOptional(bool optional)
m_optional = optional;
}
bool InterfaceEventType::logged() const
bool InterfaceEventType::loggingOverride() const
{
return m_logged;
return m_loggingOverride;
}
void InterfaceEventType::setLogged(bool logged)
void InterfaceEventType::setLoggingOverride(bool loggingOverride)
{
m_logged = logged;
m_loggingOverride = loggingOverride;
}
InterfaceEventTypes::InterfaceEventTypes(const QList<InterfaceEventType> &other):

View File

@ -11,12 +11,12 @@ public:
bool optional() const;
void setOptional(bool optional);
bool logged() const;
void setLogged(bool logged);
bool loggingOverride() const;
void setLoggingOverride(bool loggingOverride);
private:
bool m_optional = false;
bool m_logged = false;
bool m_loggingOverride = false;
};
class InterfaceEventTypes: public QList<InterfaceEventType>

View File

@ -15,14 +15,14 @@ void InterfaceStateType::setOptional(bool optional)
m_optional = optional;
}
bool InterfaceStateType::logged() const
bool InterfaceStateType::loggingOverride() const
{
return m_logged;
return m_loggingOverride;
}
void InterfaceStateType::setLogged(bool logged)
void InterfaceStateType::setLoggingOverride(bool loggingOverride)
{
m_logged = logged;
m_loggingOverride = loggingOverride;
}
InterfaceStateTypes::InterfaceStateTypes(const QList<InterfaceStateType> &other):

View File

@ -11,12 +11,12 @@ public:
bool optional() const;
void setOptional(bool optional);
bool logged() const;
void setLogged(bool logged);
bool loggingOverride() const;
void setLoggingOverride(bool loggingOverride);
private:
bool m_optional = false;
bool m_logged = false;
bool m_loggingOverride = false;
};
class InterfaceStateTypes: public QList<InterfaceStateType>

View File

@ -90,16 +90,6 @@ void State::setFilter(Types::StateValueFilter filter)
m_filter = filter;
}
bool State::logged() const
{
return m_logged;
}
void State::setLogged(bool logged)
{
m_logged = logged;
}
/*! Writes the stateTypeId, the deviceId and the value of the given \a state to \a dbg. */
QDebug operator<<(QDebug dbg, const State &state)
{

View File

@ -43,7 +43,6 @@ class LIBNYMEA_EXPORT State
Q_PROPERTY(QUuid stateTypeId READ stateTypeId)
Q_PROPERTY(QVariant value READ value)
Q_PROPERTY(Types::StateValueFilter filter READ filter)
Q_PROPERTY(bool logged READ logged)
public:
State();
@ -58,15 +57,11 @@ public:
Types::StateValueFilter filter() const;
void setFilter(Types::StateValueFilter filter);
bool logged() const;
void setLogged(bool logged);
private:
StateTypeId m_stateTypeId;
ThingId m_thingId;
QVariant m_value;
Types::StateValueFilter m_filter;
bool m_logged = false;
Types::StateValueFilter m_filter = Types::StateValueFilterNone;
};
Q_DECLARE_METATYPE(State)

View File

@ -208,6 +208,16 @@ void StateType::setCached(bool cached)
m_cached = cached;
}
bool StateType::suggestLogging() const
{
return m_logged;
}
void StateType::setSuggestLogging(bool logged)
{
m_logged = logged;
}
Types::StateValueFilter StateType::filter() const
{
return m_filter;
@ -223,7 +233,7 @@ QStringList StateType::typeProperties()
{
return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue"
<< "cached" << "unit" << "minValue" << "maxValue" << "possibleValues" << "writable"
<< "displayNameAction" << "ioType";
<< "displayNameAction" << "ioType" << "logged";
}
/*! Returns a list of mandatory properties a DeviceClass definition must have. */
@ -255,6 +265,16 @@ bool StateTypes::contains(const StateTypeId &stateTypeId)
return false;
}
bool StateTypes::contains(const QString &name)
{
foreach (const StateType &stateType, *this) {
if (stateType.name() == name) {
return true;
}
}
return false;
}
QVariant StateTypes::get(int index) const
{
return QVariant::fromValue(at(index));
@ -284,3 +304,15 @@ StateType StateTypes::findById(const StateTypeId &id)
}
return StateType(StateTypeId());
}
StateType &StateTypes::operator[](const QString &name)
{
int index = -1;
for (int i = 0; i < count(); i++) {
if (at(i).name() == name) {
index = i;
break;
}
}
return QList::operator[](index);
}

View File

@ -93,8 +93,8 @@ public:
bool cached() const;
void setCached(bool cached);
bool logged() const;
void setLogged(bool logged);
bool suggestLogging() const;
void setSuggestLogging(bool logged);
Types::StateValueFilter filter() const;
void setFilter(Types::StateValueFilter filter);
@ -131,10 +131,12 @@ public:
StateTypes() = default;
StateTypes(const QList<StateType> &other);
bool contains(const StateTypeId &stateTypeId);
bool contains(const QString &name);
Q_INVOKABLE QVariant get(int index) const;
Q_INVOKABLE void put(const QVariant &variant);
StateType findByName(const QString &name);
StateType findById(const StateTypeId &id);
StateType &operator[](const QString &name);
};
Q_DECLARE_METATYPE(StateTypes)